diff options
-rw-r--r-- | lld/ELF/InputFiles.h | 2 | ||||
-rw-r--r-- | lld/ELF/Symbols.h | 5 | ||||
-rw-r--r-- | lld/ELF/Writer.cpp | 99 | ||||
-rw-r--r-- | lld/test/elf2/got.s | 45 |
4 files changed, 133 insertions, 18 deletions
diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h index bd840ff23a0..a96892cf97b 100644 --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -134,7 +134,7 @@ public: ArrayRef<SectionChunk<ELFT> *> getChunks() { return Chunks; } - const SymbolBody *getSymbolBody(uint32_t SymbolIndex) const { + SymbolBody *getSymbolBody(uint32_t SymbolIndex) const { uint32_t FirstNonLocal = this->Symtab->sh_info; if (SymbolIndex < FirstNonLocal) return nullptr; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 7ace5929262..56c75be65f5 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -70,6 +70,10 @@ public: } void setDynamicSymbolTableIndex(unsigned V) { DynamicSymbolTableIndex = V; } + unsigned getGotIndex() const { return GotIndex; } + bool isInGot() const { return GotIndex != -1U; } + void setGotIndex(unsigned I) { GotIndex = I; } + // A SymbolBody has a backreference to a Symbol. Originally they are // doubly-linked. A backreference will never change. But the pointer // in the Symbol may be mutated by the resolver. If you have a @@ -96,6 +100,7 @@ protected: unsigned MostConstrainingVisibility : 2; unsigned IsUsedInRegularObj : 1; unsigned DynamicSymbolTableIndex = 0; + unsigned GotIndex = -1; StringRef Name; Symbol *Backref = nullptr; }; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 07a830bd6a6..b784ec6c347 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -81,6 +81,8 @@ public: } uint32_t getType() { return Header.sh_type; } + static unsigned getAddrSize() { return Is64Bits ? 8 : 4; } + virtual void finalize() {} virtual void writeTo(uint8_t *Buf) = 0; @@ -98,17 +100,55 @@ template <class ELFT> struct DynamicReloc { const Elf_Rel &RI; }; +static bool relocNeedsGOT(uint32_t Type) { + switch (Type) { + default: + return false; + case R_X86_64_GOTPCREL: + return true; + } +} + +template <class ELFT> +class GotSection final : public OutputSectionBase<ELFT::Is64Bits> { + typedef OutputSectionBase<ELFT::Is64Bits> Base; + typedef typename Base::uintX_t uintX_t; + +public: + GotSection() + : OutputSectionBase<ELFT::Is64Bits>(".got", SHT_PROGBITS, + SHF_ALLOC | SHF_WRITE) { + this->Header.sh_addralign = this->getAddrSize(); + } + void finalize() override { + this->Header.sh_size = Entries.size() * this->getAddrSize(); + } + void writeTo(uint8_t *Buf) override {} + void addEntry(SymbolBody *Sym) { + Sym->setGotIndex(Entries.size()); + Entries.push_back(Sym); + } + bool empty() const { return Entries.empty(); } + uintX_t getEntryAddr(const SymbolBody &B) const { + return this->getVA() + B.getGotIndex() * this->getAddrSize(); + } + +private: + std::vector<const SymbolBody *> Entries; +}; + template <class ELFT> class RelocationSection final : public OutputSectionBase<ELFT::Is64Bits> { typedef typename ELFFile<ELFT>::Elf_Rel Elf_Rel; typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela; public: - RelocationSection(SymbolTableSection<ELFT> &DynSymSec, bool IsRela) + RelocationSection(SymbolTableSection<ELFT> &DynSymSec, + const GotSection<ELFT> &GotSec, bool IsRela) : OutputSectionBase<ELFT::Is64Bits>(IsRela ? ".rela.dyn" : ".rel.dyn", IsRela ? SHT_RELA : SHT_REL, SHF_ALLOC), - DynSymSec(DynSymSec), IsRela(IsRela) { + DynSymSec(DynSymSec), GotSec(GotSec), IsRela(IsRela) { this->Header.sh_entsize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); this->Header.sh_addralign = ELFT::Is64Bits ? 8 : 4; } @@ -127,12 +167,18 @@ public: OutputSection<ELFT> *Out = C.getOutputSection(); uint32_t SymIndex = RI.getSymbol(IsMips64EL); const SymbolBody *Body = C.getFile()->getSymbolBody(SymIndex); - - P->r_offset = RI.r_offset + C.getOutputSectionOff() + Out->getVA(); - P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), - RI.getType(IsMips64EL), IsMips64EL); - if (IsRela) - P->r_addend = static_cast<const Elf_Rela &>(RI).r_addend; + uint32_t Type = RI.getType(IsMips64EL); + if (relocNeedsGOT(Type)) { + P->r_offset = GotSec.getEntryAddr(*Body); + P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), + R_X86_64_GLOB_DAT, IsMips64EL); + } else { + P->r_offset = RI.r_offset + C.getOutputSectionOff() + Out->getVA(); + P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), Type, + IsMips64EL); + if (IsRela) + P->r_addend = static_cast<const Elf_Rela &>(RI).r_addend; + } ++P; } @@ -143,6 +189,7 @@ public: private: std::vector<DynamicReloc<ELFT>> Relocs; SymbolTableSection<ELFT> &DynSymSec; + const GotSection<ELFT> &GotSec; const bool IsRela; }; } @@ -155,8 +202,10 @@ public: typedef typename ELFFile<ELFT>::Elf_Shdr Elf_Shdr; typedef typename ELFFile<ELFT>::Elf_Rel Elf_Rel; typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela; - OutputSection(StringRef Name, uint32_t sh_type, uintX_t sh_flags) - : OutputSectionBase<ELFT::Is64Bits>(Name, sh_type, sh_flags) {} + OutputSection(const GotSection<ELFT> &GotSec, StringRef Name, + uint32_t sh_type, uintX_t sh_flags) + : OutputSectionBase<ELFT::Is64Bits>(Name, sh_type, sh_flags), + GotSec(GotSec) {} void addChunk(SectionChunk<ELFT> *C); void writeTo(uint8_t *Buf) override; @@ -173,6 +222,7 @@ public: private: std::vector<SectionChunk<ELFT> *> Chunks; + const GotSection<ELFT> &GotSec; }; namespace { @@ -507,7 +557,7 @@ public: typedef typename ELFFile<ELFT>::Elf_Rela Elf_Rela; Writer(SymbolTable *T) : SymTabSec(*this, *T, StrTabSec), DynSymSec(*this, *T, DynStrSec), - RelaDynSec(DynSymSec, T->shouldUseRela()), HashSec(DynSymSec), + RelaDynSec(DynSymSec, GotSec, T->shouldUseRela()), HashSec(DynSymSec), DynamicSec(*T, HashSec, RelaDynSec) {} void run(); @@ -559,6 +609,8 @@ private: RelocationSection<ELFT> RelaDynSec; + GotSection<ELFT> GotSec; + HashTableSection<ELFT> HashSec; DynamicSection<ELFT> DynamicSec; @@ -676,16 +728,22 @@ void OutputSection<ELFT>::relocate( if (!Body) continue; + uint32_t Type = RI.getType(IsMips64EL); uintX_t SymVA; - if (auto *DR = dyn_cast<DefinedRegular<ELFT>>(Body)) + if (auto *DR = dyn_cast<DefinedRegular<ELFT>>(Body)) { SymVA = getSymVA<ELFT>(DR); - else if (auto *DA = dyn_cast<DefinedAbsolute<ELFT>>(Body)) + } else if (auto *DA = dyn_cast<DefinedAbsolute<ELFT>>(Body)) { SymVA = DA->Sym.st_value; - else + } else if (auto *S = dyn_cast<SharedSymbol<ELFT>>(Body)) { + if (!relocNeedsGOT(Type)) + continue; + SymVA = GotSec.getEntryAddr(*S); + Type = R_X86_64_PC32; + } else { // Skip unsupported for now. continue; + } - uint32_t Type = RI.getType(IsMips64EL); relocateOne(Buf, RI, Type, BaseAddr, SymVA); } } @@ -899,12 +957,17 @@ void Writer<ELFT>::scanRelocs( bool IsMips64EL = File.getObj()->isMips64EL(); for (const RelType &RI : Rels) { uint32_t SymIndex = RI.getSymbol(IsMips64EL); - const SymbolBody *Body = File.getSymbolBody(SymIndex); + SymbolBody *Body = File.getSymbolBody(SymIndex); if (!Body) continue; auto *S = dyn_cast<SharedSymbol<ELFT>>(Body); if (!S) continue; + if (relocNeedsGOT(RI.getType(IsMips64EL))) { + if (Body->isInGot()) + continue; + GotSec.addEntry(Body); + } RelaDynSec.addReloc({C, RI}); } } @@ -934,7 +997,7 @@ template <class ELFT> void Writer<ELFT>::createSections() { OutputSection<ELFT> *&Sec = Map[Key]; if (!Sec) { Sec = new (CAlloc.Allocate()) - OutputSection<ELFT>(Key.Name, Key.sh_type, Key.sh_flags); + OutputSection<ELFT>(GotSec, Key.Name, Key.sh_type, Key.sh_flags); OutputSections.push_back(Sec); } return Sec; @@ -1009,6 +1072,8 @@ template <class ELFT> void Writer<ELFT>::createSections() { OutputSections.push_back(&DynStrSec); if (RelaDynSec.hasRelocs()) OutputSections.push_back(&RelaDynSec); + if (!GotSec.empty()) + OutputSections.push_back(&GotSec); } std::stable_sort(OutputSections.begin(), OutputSections.end(), diff --git a/lld/test/elf2/got.s b/lld/test/elf2/got.s new file mode 100644 index 00000000000..aaff3a1df54 --- /dev/null +++ b/lld/test/elf2/got.s @@ -0,0 +1,45 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o +// RUN: lld -flavor gnu2 -shared %t2.o -o %t2.so +// RUN: lld -flavor gnu2 %t.o %t2.so -o %t +// RUN: llvm-readobj -s -r %t | FileCheck %s +// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s +// REQUIRES: x86 + +// CHECK: Name: .got +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_WRITE +// CHECK-NEXT: ] +// CHECK-NEXT: Address: 0x15000 +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: 16 +// CHECK-NEXT: Link: 0 +// CHECK-NEXT: Info: 0 +// CHECK-NEXT: AddressAlignment: 8 + +// CHECK: Relocations [ +// CHECK-NEXT: Section ({{.*}}) .rela.dyn { +// CHECK-NEXT: 0x15000 R_X86_64_GLOB_DAT bar 0x0 +// CHECK-NEXT: 0x15008 R_X86_64_GLOB_DAT zed 0x0 +// CHECK-NEXT: } +// CHECK-NEXT: ] + + +// Unfortunately FileCheck can't do math, so we have to check for explicit +// values: +// 0x15000 - (0x11000 + 2) - 4 = 16378 +// 0x15000 - (0x11006 + 2) - 4 = 16372 +// 0x15008 - (0x1100c + 2) - 4 = 16374 + +// DISASM: _start: +// DISASM-NEXT: 11000: ff 25 fa 3f 00 00 jmpq *16378(%rip) +// DISASM-NEXT: 11006: ff 25 f4 3f 00 00 jmpq *16372(%rip) +// DISASM-NEXT: 1100c: ff 25 f6 3f 00 00 jmpq *16374(%rip) + +.global _start +_start: + jmp *bar@GOTPCREL(%rip) + jmp *bar@GOTPCREL(%rip) + jmp *zed@GOTPCREL(%rip) |