summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/InputSection.cpp26
-rw-r--r--lld/ELF/OutputSections.cpp44
-rw-r--r--lld/ELF/OutputSections.h9
-rw-r--r--lld/ELF/Target.cpp14
-rw-r--r--lld/ELF/Target.h3
-rw-r--r--lld/ELF/Writer.cpp21
-rw-r--r--lld/test/ELF/mips-got16.s95
7 files changed, 196 insertions, 16 deletions
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index f6aa51b47b9..7e8f6c4e094 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -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) &&
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index 62ad8e24752..bd9a94eeb6d 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -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);
diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h
index 7132413fc33..2af5a480446 100644
--- a/lld/ELF/OutputSections.h
+++ b/lld/ELF/OutputSections.h
@@ -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>
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 430e077b031..a60965472e4 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -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>();
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index a3040831685..828222879e4 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -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;
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index bc2c7ad43f9..ce334aa5e66 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -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)
diff --git a/lld/test/ELF/mips-got16.s b/lld/test/ELF/mips-got16.s
new file mode 100644
index 00000000000..c1eee3c9c04
--- /dev/null
+++ b/lld/test/ELF/mips-got16.s
@@ -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
OpenPOWER on IntegriCloud