Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -189,12 +189,18 @@ uintX_t A = getAddend(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::Got->getMipsLocalPageAddr(SymVA); + } Target->relocateOne(BufLoc, BufEnd, Type, AddrLoc, SymVA, 0, findMipsPairedReloc(Buf, SymIndex, Type, NextRelocs)); continue; @@ -212,7 +218,13 @@ if (Target->relocNeedsPlt(Type, *Body)) { SymVA = Out::Plt->getEntryAddr(*Body); } else if (Target->relocNeedsGot(Type, *Body)) { - SymVA = Out::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 oif the symbol. + SymVA = Out::Got->getMipsLocalFullAddr(*Body); + else + SymVA = Out::Got->getEntryAddr(*Body); if (Body->isTls()) Type = Target->getTlsGotReloc(Type); } else if (!Target->needsCopyRel(Type, *Body) && Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -123,10 +123,13 @@ 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 @@ private: std::vector Entries; uint32_t LocalTlsIndexOff = -1; + uint32_t MipsLocalEntries = 0; + llvm::DenseMap MipsLocalGotPos; + + uintX_t getMipsLocalEntryAddr(uintX_t EntryValue); }; template Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -78,10 +78,14 @@ } template void GotSection::addEntry(SymbolBody *Sym) { - Sym->GotIndex = Target->getGotHeaderEntriesNum() + Entries.size(); + Sym->GotIndex = Entries.size(); Entries.push_back(Sym); } +template void GotSection::addMipsLocalEntry() { + ++MipsLocalEntries; +} + template bool GotSection::addDynTlsEntry(SymbolBody *Sym) { if (Sym->hasGlobalDynIndex()) return false; @@ -104,7 +108,32 @@ template typename GotSection::uintX_t GotSection::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 +typename GotSection::uintX_t +GotSection::getMipsLocalFullAddr(const SymbolBody &B) { + return getMipsLocalEntryAddr(getSymVA(B)); +} + +template +typename GotSection::uintX_t +GotSection::getMipsLocalPageAddr(uintX_t EntryValue) { + // Initialize the entry by the %hi(EntryValue) expression + // but without right-shifting. + return getMipsLocalEntryAddr((EntryValue + 0x8000) & ~0xffff); +} + +template +typename GotSection::uintX_t +GotSection::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 @@ -120,18 +149,23 @@ template unsigned GotSection::getMipsLocalEntriesNum() const { - // TODO: Update when the support of GOT entries for local symbols is added. - return Target->getGotHeaderEntriesNum(); + return Target->getGotHeaderEntriesNum() + MipsLocalEntries; } template void GotSection::finalize() { this->Header.sh_size = - (Target->getGotHeaderEntriesNum() + Entries.size()) * sizeof(uintX_t); + (Target->getGotHeaderEntriesNum() + MipsLocalEntries + Entries.size()) * + sizeof(uintX_t); } template void GotSection::writeTo(uint8_t *Buf) { Target->writeGotHeaderEntries(Buf); + for (const auto &L : MipsLocalGotPos) { + uint8_t *Entry = Buf + L.second * sizeof(uintX_t); + write(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); Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -118,6 +118,9 @@ template typename llvm::object::ELFFile::uintX_t getMipsGpAddr(); +// Returns true if the relocation requires entry in the local part of GOT. +bool needsMipsLocalGot(uint32_t Type, SymbolBody *Body); + template bool isGnuIFunc(const SymbolBody &S); extern std::unique_ptr Target; Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -1634,6 +1634,20 @@ 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(); template uint32_t getMipsGpAddr(); template uint64_t getMipsGpAddr(); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -258,8 +258,13 @@ } 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::Got->addMipsLocalEntry(); + } else if (Body) { if (auto *E = dyn_cast>(Body)) { if (E->NeedsCopy) continue; @@ -294,13 +299,23 @@ } 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) Index: test/ELF/mips-got16.s =================================================================== --- /dev/null +++ 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