[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:
parent
1a75b0d14f
commit
56ab5f0289
|
@ -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) &&
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue