[ELF][MIPS] Initial support of MIPS local GOT entries

Some MIPS relocation (for now R_MIPS_GOT16) requires creation of GOT
entries for symbol not included in the dynamic symbol table. They are
local symbols and non-local symbols with 'local' visibility. Local GOT
entries occupy continuous block between GOT header and regular GOT
entries.

The patch adds initial support for handling local GOT entries. The main
problem is allocating local GOT entries for local symbols. Such entries
should be initialized by high 16-bit of the symbol value. In ideal world
there should be no duplicated entries with the same values. But at the
moment of the `Writer::scanRelocs` call we do not know a value of the
symbol. In this patch we create new local GOT entry for each relocation
against local symbol, though we can exhaust GOT quickly. That needs to
be optimized later. When we calculate relocation we know a final symbol
value and request local GOT entry index. To do that we maintain map
between addresses and local GOT entry indexes. If we start to calculate
relocations in parallel we will have to serialize access to this map.

Differential Revision: http://reviews.llvm.org/D16324

llvm-svn: 258388
This commit is contained in:
Simon Atanasyan 2016-01-21 05:33:23 +00:00
parent 1a75b0d14f
commit 56ab5f0289
7 changed files with 196 additions and 16 deletions

View File

@ -189,12 +189,18 @@ void InputSectionBase<ELFT>::relocate(uint8_t *Buf, uint8_t *BufEnd,
uintX_t A = getAddend<ELFT>(RI);
if (!Body) {
uintX_t SymVA = getLocalRelTarget(*File, RI, A);
// We need to adjust SymVA value in case of R_MIPS_GPREL16/32 relocations
// because they use the following expression to calculate the relocation's
// result for local symbol: S + A + GP0 - G.
if (Config->EMachine == EM_MIPS &&
(Type == R_MIPS_GPREL16 || Type == R_MIPS_GPREL32))
SymVA += File->getMipsGp0();
if (Config->EMachine == EM_MIPS) {
if (Type == R_MIPS_GPREL16 || Type == R_MIPS_GPREL32)
// We need to adjust SymVA value in case of R_MIPS_GPREL16/32
// relocations because they use the following expression to calculate
// the relocation's result for local symbol: S + A + GP0 - G.
SymVA += File->getMipsGp0();
else if (Type == R_MIPS_GOT16)
// R_MIPS_GOT16 relocation against local symbol requires index of
// a local GOT entry which contains page address corresponds
// to the symbol address.
SymVA = Out<ELFT>::Got->getMipsLocalPageAddr(SymVA);
}
Target->relocateOne(BufLoc, BufEnd, Type, AddrLoc, SymVA, 0,
findMipsPairedReloc(Buf, SymIndex, Type, NextRelocs));
continue;
@ -212,7 +218,13 @@ void InputSectionBase<ELFT>::relocate(uint8_t *Buf, uint8_t *BufEnd,
if (Target->relocNeedsPlt(Type, *Body)) {
SymVA = Out<ELFT>::Plt->getEntryAddr(*Body);
} else if (Target->relocNeedsGot(Type, *Body)) {
SymVA = Out<ELFT>::Got->getEntryAddr(*Body);
if (Config->EMachine == EM_MIPS && needsMipsLocalGot(Type, Body))
// Under some conditions relocations against non-local symbols require
// entries in the local part of MIPS GOT. In that case we need an entry
// initialized by full address of the symbol.
SymVA = Out<ELFT>::Got->getMipsLocalFullAddr(*Body);
else
SymVA = Out<ELFT>::Got->getEntryAddr(*Body);
if (Body->isTls())
Type = Target->getTlsGotReloc(Type);
} else if (!Target->needsCopyRel(Type, *Body) &&

View File

@ -78,10 +78,14 @@ GotSection<ELFT>::GotSection()
}
template <class ELFT> void GotSection<ELFT>::addEntry(SymbolBody *Sym) {
Sym->GotIndex = Target->getGotHeaderEntriesNum() + Entries.size();
Sym->GotIndex = Entries.size();
Entries.push_back(Sym);
}
template <class ELFT> void GotSection<ELFT>::addMipsLocalEntry() {
++MipsLocalEntries;
}
template <class ELFT> bool GotSection<ELFT>::addDynTlsEntry(SymbolBody *Sym) {
if (Sym->hasGlobalDynIndex())
return false;
@ -104,7 +108,32 @@ template <class ELFT> bool GotSection<ELFT>::addCurrentModuleTlsIndex() {
template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getEntryAddr(const SymbolBody &B) const {
return this->getVA() + B.GotIndex * sizeof(uintX_t);
return this->getVA() +
(Target->getGotHeaderEntriesNum() + MipsLocalEntries + B.GotIndex) *
sizeof(uintX_t);
}
template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getMipsLocalFullAddr(const SymbolBody &B) {
return getMipsLocalEntryAddr(getSymVA<ELFT>(B));
}
template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getMipsLocalPageAddr(uintX_t EntryValue) {
// Initialize the entry by the %hi(EntryValue) expression
// but without right-shifting.
return getMipsLocalEntryAddr((EntryValue + 0x8000) & ~0xffff);
}
template <class ELFT>
typename GotSection<ELFT>::uintX_t
GotSection<ELFT>::getMipsLocalEntryAddr(uintX_t EntryValue) {
size_t NewIndex = Target->getGotHeaderEntriesNum() + MipsLocalGotPos.size();
auto P = MipsLocalGotPos.insert(std::make_pair(EntryValue, NewIndex));
assert(!P.second || MipsLocalGotPos.size() <= MipsLocalEntries);
return this->getVA() + P.first->second * sizeof(uintX_t);
}
template <class ELFT>
@ -120,18 +149,23 @@ const SymbolBody *GotSection<ELFT>::getMipsFirstGlobalEntry() const {
template <class ELFT>
unsigned GotSection<ELFT>::getMipsLocalEntriesNum() const {
// TODO: Update when the support of GOT entries for local symbols is added.
return Target->getGotHeaderEntriesNum();
return Target->getGotHeaderEntriesNum() + MipsLocalEntries;
}
template <class ELFT> void GotSection<ELFT>::finalize() {
this->Header.sh_size =
(Target->getGotHeaderEntriesNum() + Entries.size()) * sizeof(uintX_t);
(Target->getGotHeaderEntriesNum() + MipsLocalEntries + Entries.size()) *
sizeof(uintX_t);
}
template <class ELFT> void GotSection<ELFT>::writeTo(uint8_t *Buf) {
Target->writeGotHeaderEntries(Buf);
for (const auto &L : MipsLocalGotPos) {
uint8_t *Entry = Buf + L.second * sizeof(uintX_t);
write<uintX_t, ELFT::TargetEndianness, sizeof(uintX_t)>(Entry, L.first);
}
Buf += Target->getGotHeaderEntriesNum() * sizeof(uintX_t);
Buf += MipsLocalEntries * sizeof(uintX_t);
for (const SymbolBody *B : Entries) {
uint8_t *Entry = Buf;
Buf += sizeof(uintX_t);

View File

@ -123,10 +123,13 @@ public:
void finalize() override;
void writeTo(uint8_t *Buf) override;
void addEntry(SymbolBody *Sym);
void addMipsLocalEntry();
bool addDynTlsEntry(SymbolBody *Sym);
bool addCurrentModuleTlsIndex();
bool empty() const { return Entries.empty(); }
bool empty() const { return MipsLocalEntries == 0 && Entries.empty(); }
uintX_t getEntryAddr(const SymbolBody &B) const;
uintX_t getMipsLocalFullAddr(const SymbolBody &B);
uintX_t getMipsLocalPageAddr(uintX_t Addr);
uintX_t getGlobalDynAddr(const SymbolBody &B) const;
uintX_t getNumEntries() const { return Entries.size(); }
@ -145,6 +148,10 @@ public:
private:
std::vector<const SymbolBody *> Entries;
uint32_t LocalTlsIndexOff = -1;
uint32_t MipsLocalEntries = 0;
llvm::DenseMap<uintX_t, size_t> MipsLocalGotPos;
uintX_t getMipsLocalEntryAddr(uintX_t EntryValue);
};
template <class ELFT>

View File

@ -1634,6 +1634,20 @@ template <class ELFT> typename ELFFile<ELFT>::uintX_t getMipsGpAddr() {
return 0;
}
bool needsMipsLocalGot(uint32_t Type, SymbolBody *Body) {
// The R_MIPS_GOT16 relocation requires creation of entry in the local part
// of GOT if its target is a local symbol or non-local symbol with 'local'
// visibility.
if (Type != R_MIPS_GOT16)
return false;
if (!Body)
return true;
uint8_t V = Body->getVisibility();
if (V != STV_DEFAULT && V != STV_PROTECTED)
return true;
return !Config->Shared;
}
template uint32_t getMipsGpAddr<ELF32LE>();
template uint32_t getMipsGpAddr<ELF32BE>();
template uint64_t getMipsGpAddr<ELF64LE>();

View File

@ -118,6 +118,9 @@ uint64_t getPPC64TocBase();
template <class ELFT>
typename llvm::object::ELFFile<ELFT>::uintX_t getMipsGpAddr();
// Returns true if the relocation requires entry in the local part of GOT.
bool needsMipsLocalGot(uint32_t Type, SymbolBody *Body);
template <class ELFT> bool isGnuIFunc(const SymbolBody &S);
extern std::unique_ptr<TargetInfo> Target;

View File

@ -258,8 +258,13 @@ void Writer<ELFT>::scanRelocs(
}
bool NeedsGot = false;
bool NeedsMipsLocalGot = false;
bool NeedsPlt = false;
if (Body) {
if (Config->EMachine == EM_MIPS && needsMipsLocalGot(Type, Body)) {
NeedsMipsLocalGot = true;
// FIXME (simon): Do not add so many redundant entries.
Out<ELFT>::Got->addMipsLocalEntry();
} else if (Body) {
if (auto *E = dyn_cast<SharedSymbol<ELFT>>(Body)) {
if (E->NeedsCopy)
continue;
@ -294,13 +299,23 @@ void Writer<ELFT>::scanRelocs(
}
if (Config->EMachine == EM_MIPS) {
if (NeedsGot) {
if (Type == R_MIPS_LO16)
// Ignore R_MIPS_LO16 relocation. If it is a pair for R_MIPS_GOT16 we
// already completed all required action (GOT entry allocation) when
// handle R_MIPS_GOT16a. If it is a pair for R_MIPS_HI16 against
// _gp_disp it does not require dynamic relocation. If its a pair for
// R_MIPS_HI16 against a regular symbol it does not require dynamic
// relocation too because that case is possible for executable file
// linking only.
continue;
if (NeedsGot || NeedsMipsLocalGot) {
// MIPS ABI has special rules to process GOT entries
// and doesn't require relocation entries for them.
// See "Global Offset Table" in Chapter 5 in the following document
// for detailed description:
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
Body->setUsedInDynamicReloc();
if (NeedsGot)
Body->setUsedInDynamicReloc();
continue;
}
if (Body == Config->MipsGpDisp)

95
lld/test/ELF/mips-got16.s Normal file
View File

@ -0,0 +1,95 @@
# Check R_MIPS_GOT16 relocation calculation.
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
# RUN: ld.lld %t.o -shared -o %t.so
# RUN: llvm-objdump -d -t %t.so | FileCheck %s
# RUN: llvm-readobj -r -mips-plt-got %t.so | FileCheck -check-prefix=GOT %s
# REQUIRES: mips
# CHECK: Disassembly of section .text:
# CHECK-NEXT: __start:
# CHECK-NEXT: 10000: 8f 88 80 18 lw $8, -32744($gp)
# CHECK-NEXT: 10004: 21 08 00 1c addi $8, $8, 28
# CHECK-NEXT: 10008: 8f 88 80 1c lw $8, -32740($gp)
# CHECK-NEXT: 1000c: 21 08 00 00 addi $8, $8, 0
# CHECK-NEXT: 10010: 8f 88 80 20 lw $8, -32736($gp)
# CHECK-NEXT: 10014: 21 08 00 04 addi $8, $8, 4
# CHECK-NEXT: 10018: 8f 88 80 24 lw $8, -32732($gp)
#
# CHECK: SYMBOL TABLE:
# CHECK: 0001001c .text 00000000 $LC0
# CHECK: 00030000 .data 00000000 $LC1
# CHECK: 00030004 .data 00000000 .hidden bar
# CHECK: 00000000 *UND* 00000000 foo
# GOT: Relocations [
# GOT-NEXT: ]
# GOT: Primary GOT {
# GOT-NEXT: Canonical gp value: 0x27FF0
# GOT-NEXT: Reserved entries [
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x20000
# GOT-NEXT: Access: -32752
# GOT-NEXT: Initial: 0x0
# GOT-NEXT: Purpose: Lazy resolver
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x20004
# GOT-NEXT: Access: -32748
# GOT-NEXT: Initial: 0x80000000
# GOT-NEXT: Purpose: Module pointer (GNU extension)
# GOT-NEXT: }
# GOT-NEXT: ]
# GOT-NEXT: Local entries [
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x20008
# GOT-NEXT: Access: -32744
# GOT-NEXT: Initial: 0x10000
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x2000C
# GOT-NEXT: Access: -32740
# GOT-NEXT: Initial: 0x30000
# GOT-NEXT: }
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x20010
# GOT-NEXT: Access: -32736
# GOT-NEXT: Initial: 0x30004
# GOT-NEXT: }
# GOT-NEXT: ]
# GOT-NEXT: Global entries [
# GOT-NEXT: Entry {
# GOT-NEXT: Address: 0x20014
# GOT-NEXT: Access: -32732
# GOT-NEXT: Initial: 0x0
# GOT-NEXT: Value: 0x0
# GOT-NEXT: Type: None
# GOT-NEXT: Section: Undefined
# GOT-NEXT: Name: foo@
# GOT-NEXT: }
# GOT-NEXT: ]
# GOT-NEXT: Number of TLS and multi-GOT entries: 0
# GOT-NEXT: }
.text
.globl __start
__start:
lw $t0,%got($LC0)($gp)
addi $t0,$t0,%lo($LC0)
lw $t0,%got($LC1)($gp)
addi $t0,$t0,%lo($LC1)
lw $t0,%got(bar)($gp)
addi $t0,$t0,%lo(bar)
lw $t0,%got(foo)($gp)
$LC0:
nop
.data
$LC1:
.word 0
.global bar
.hidden bar
bar:
.word 0