diff --git a/lld/ELF/Arch/AArch64.cpp b/lld/ELF/Arch/AArch64.cpp --- a/lld/ELF/Arch/AArch64.cpp +++ b/lld/ELF/Arch/AArch64.cpp @@ -64,8 +64,9 @@ symbolicRel = R_AARCH64_ABS64; tlsDescRel = R_AARCH64_TLSDESC; tlsGotRel = R_AARCH64_TLS_TPREL64; - pltEntrySize = 16; pltHeaderSize = 32; + pltEntrySize = 16; + ipltEntrySize = 16; defaultMaxPageSize = 65536; // Align to the 2 MiB page size (known as a superpage or huge page). @@ -590,8 +591,10 @@ btiEntry = btiHeader && !config->shared; pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC); - if (btiEntry || pacEntry) + if (btiEntry || pacEntry) { pltEntrySize = 24; + ipltEntrySize = 24; + } } void AArch64BtiPac::writePltHeader(uint8_t *buf) const { diff --git a/lld/ELF/Arch/ARM.cpp b/lld/ELF/Arch/ARM.cpp --- a/lld/ELF/Arch/ARM.cpp +++ b/lld/ELF/Arch/ARM.cpp @@ -59,8 +59,9 @@ tlsModuleIndexRel = R_ARM_TLS_DTPMOD32; tlsOffsetRel = R_ARM_TLS_DTPOFF32; gotBaseSymInGotPlt = false; - pltEntrySize = 16; pltHeaderSize = 32; + pltEntrySize = 16; + ipltEntrySize = 16; trapInstr = {0xd4, 0xd4, 0xd4, 0xd4}; needsThunks = true; } diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp --- a/lld/ELF/Arch/PPC.cpp +++ b/lld/ELF/Arch/PPC.cpp @@ -144,6 +144,7 @@ gotPltHeaderEntriesNum = 0; pltHeaderSize = 64; // size of PLTresolve in .glink pltEntrySize = 4; + ipltEntrySize = 4; needsThunks = true; diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp --- a/lld/ELF/Arch/PPC64.cpp +++ b/lld/ELF/Arch/PPC64.cpp @@ -296,11 +296,12 @@ relativeRel = R_PPC64_RELATIVE; iRelativeRel = R_PPC64_IRELATIVE; symbolicRel = R_PPC64_ADDR64; + pltHeaderSize = 60; pltEntrySize = 4; + ipltEntrySize = 4; gotBaseSymInGotPlt = false; gotHeaderEntriesNum = 1; gotPltHeaderEntriesNum = 2; - pltHeaderSize = 60; needsThunks = true; tlsModuleIndexRel = R_PPC64_DTPMOD64; diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp --- a/lld/ELF/Arch/RISCV.cpp +++ b/lld/ELF/Arch/RISCV.cpp @@ -95,8 +95,9 @@ // .got.plt[0] = _dl_runtime_resolve, .got.plt[1] = link_map gotPltHeaderEntriesNum = 2; - pltEntrySize = 16; pltHeaderSize = 32; + pltEntrySize = 16; + ipltEntrySize = 16; } static uint32_t getEFlags(InputFile *f) { diff --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp --- a/lld/ELF/Arch/X86.cpp +++ b/lld/ELF/Arch/X86.cpp @@ -57,8 +57,9 @@ tlsGotRel = R_386_TLS_TPOFF; tlsModuleIndexRel = R_386_TLS_DTPMOD32; tlsOffsetRel = R_386_TLS_DTPOFF32; - pltEntrySize = 16; pltHeaderSize = 16; + pltEntrySize = 16; + ipltEntrySize = 16; trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the non-PAE large page size (known as a superpage or huge page). @@ -235,7 +236,7 @@ } write32le(buf + 7, relOff); - write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); + write32le(buf + 12, in.plt->getVA() - pltEntryAddr - 16); } int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const { @@ -432,6 +433,7 @@ RetpolinePic::RetpolinePic() { pltHeaderSize = 48; pltEntrySize = 32; + ipltEntrySize = 32; } void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const { @@ -474,7 +476,7 @@ memcpy(buf, insn, sizeof(insn)); uint32_t ebx = in.gotPlt->getVA(); - unsigned off = pltHeaderSize + pltEntrySize * index; + unsigned off = pltEntryAddr - in.plt->getVA(); write32le(buf + 3, gotPltEntryAddr - ebx); write32le(buf + 8, -off - 12 + 32); write32le(buf + 13, -off - 17 + 18); @@ -485,6 +487,7 @@ RetpolineNoPic::RetpolineNoPic() { pltHeaderSize = 48; pltEntrySize = 32; + ipltEntrySize = 32; } void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const { @@ -532,7 +535,7 @@ }; memcpy(buf, insn, sizeof(insn)); - unsigned off = pltHeaderSize + pltEntrySize * index; + unsigned off = pltEntryAddr - in.plt->getVA(); write32le(buf + 2, gotPltEntryAddr); write32le(buf + 7, -off - 11 + 32); write32le(buf + 12, -off - 16 + 17); diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp --- a/lld/ELF/Arch/X86_64.cpp +++ b/lld/ELF/Arch/X86_64.cpp @@ -61,8 +61,9 @@ tlsGotRel = R_X86_64_TPOFF64; tlsModuleIndexRel = R_X86_64_DTPMOD64; tlsOffsetRel = R_X86_64_DTPOFF64; - pltEntrySize = 16; pltHeaderSize = 16; + pltEntrySize = 16; + ipltEntrySize = 16; trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3 // Align to the large page size (known as a superpage or huge page). @@ -166,7 +167,7 @@ write32le(buf + 2, gotPltEntryAddr - pltEntryAddr - 6); write32le(buf + 7, index); - write32le(buf + 12, -pltHeaderSize - pltEntrySize * index - 16); + write32le(buf + 12, in.plt->getVA() - pltEntryAddr - 16); } RelType X86_64::getDynRel(RelType type) const { @@ -599,6 +600,7 @@ Retpoline::Retpoline() { pltHeaderSize = 48; pltEntrySize = 32; + ipltEntrySize = 32; } void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const { @@ -639,7 +641,7 @@ }; memcpy(buf, insn, sizeof(insn)); - uint64_t off = pltHeaderSize + pltEntrySize * index; + uint64_t off = pltEntryAddr - in.plt->getVA(); write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); write32le(buf + 8, -off - 12 + 32); @@ -651,6 +653,7 @@ RetpolineZNow::RetpolineZNow() { pltHeaderSize = 32; pltEntrySize = 16; + ipltEntrySize = 16; } void RetpolineZNow::writePltHeader(uint8_t *buf) const { @@ -679,7 +682,7 @@ memcpy(buf, insn, sizeof(insn)); write32le(buf + 3, gotPltEntryAddr - pltEntryAddr - 7); - write32le(buf + 8, -pltHeaderSize - pltEntrySize * index - 12); + write32le(buf + 8, in.plt->getVA() - pltEntryAddr - 12); } static TargetInfo *getTargetInfo() { diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1011,7 +1011,7 @@ expr, type); } -template +template static void addPltEntry(PltSection *plt, GotPltSection *gotPlt, RelocationBaseSection *rel, RelType type, Symbol &sym) { plt->addEntry(sym); @@ -1415,13 +1415,9 @@ } else if (!needsPlt(expr)) { // Make the ifunc's PLT entry canonical by changing the value of its // symbol to redirect all references to point to it. - unsigned entryOffset = sym.pltIndex * target->pltEntrySize; - if (config->zRetpolineplt) - entryOffset += target->pltHeaderSize; - auto &d = cast(sym); d.section = in.iplt; - d.value = entryOffset; + d.value = sym.pltIndex * target->ipltEntrySize; d.size = 0; // It's important to set the symbol type here so that dynamic loaders // don't try to call the PLT as if it were an ifunc resolver. diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -163,9 +163,11 @@ } uint64_t Symbol::getPltVA() const { - PltSection *plt = isInIplt ? in.iplt : in.plt; - uint64_t outVA = - plt->getVA() + plt->headerSize + pltIndex * target->pltEntrySize; + uint64_t outVA = isInIplt + ? in.iplt->getVA() + pltIndex * target->ipltEntrySize + : in.plt->getVA() + in.plt->headerSize + + pltIndex * target->pltEntrySize; + // While linking microMIPS code PLT code are always microMIPS // code. Set the less-significant bit to track that fact. // See detailed comment in the `getSymVA` function. diff --git a/lld/ELF/SyntheticSections.h b/lld/ELF/SyntheticSections.h --- a/lld/ELF/SyntheticSections.h +++ b/lld/ELF/SyntheticSections.h @@ -662,16 +662,15 @@ size_t size = 0; }; -// The PltSection is used for both the Plt and Iplt. The former usually has a -// header as its first entry that is used at run-time to resolve lazy binding. -// The latter is used for GNU Ifunc symbols, that will be subject to a -// Target->IRelativeRel. +// Used for PLT entries. It usually has a PLT header for lazy binding. Each PLT +// entry is associated with a JUMP_SLOT relocation, which may be resolved lazily +// at runtime. class PltSection : public SyntheticSection { public: - PltSection(bool isIplt); + PltSection(); void writeTo(uint8_t *buf) override; size_t getSize() const override; - bool isNeeded() const override { return !entries.empty(); } + bool isNeeded() const override; void addSymbols(); void addEntry(Symbol &sym); @@ -679,7 +678,22 @@ private: std::vector entries; - bool isIplt; +}; + +// Used for non-preemptible ifuncs. It does not have a header. Each entry is +// associated with an IRELATIVE relocation, which will be resolved eagerly at +// runtime. PltSection cannot can only contain entries associated with JUMP_SLOT +// relocations, so IPLT entries are in a separate section. +class IpltSection final : public SyntheticSection { + std::vector entries; + +public: + IpltSection(); + void writeTo(uint8_t *buf) override; + size_t getSize() const override; + bool isNeeded() const override { return !entries.empty(); } + void addSymbols(); + void addEntry(Symbol &sym); }; class GdbIndexSection final : public SyntheticSection { @@ -1162,7 +1176,7 @@ SyntheticSection *partEnd; SyntheticSection *partIndex; PltSection *plt; - PltSection *iplt; + IpltSection *iplt; PPC32Got2Section *ppc32Got2; RelocationBaseSection *relaPlt; RelocationBaseSection *relaIplt; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -2445,14 +2445,13 @@ // On PowerPC64 the lazy symbol resolvers go into the `global linkage table` // in the .glink section, rather then the typical .plt section. -PltSection::PltSection(bool isIplt) - : SyntheticSection( - SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, - (config->emachine == EM_PPC || config->emachine == EM_PPC64) - ? ".glink" - : ".plt"), - headerSize(!isIplt || config->zRetpolineplt ? target->pltHeaderSize : 0), - isIplt(isIplt) { +PltSection::PltSection() + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".plt"), + headerSize(target->pltHeaderSize) { + if (config->emachine == EM_PPC || config->emachine == EM_PPC64) { + name = ".glink"; + } + // The PLT needs to be writable on SPARC as the dynamic linker will // modify the instructions in the PLT entries. if (config->emachine == EM_SPARCV9) @@ -2465,10 +2464,9 @@ return; } - // At beginning of PLT or retpoline IPLT, we have code to call the dynamic + // At beginning of PLT, we have code to call the dynamic // linker to resolve dynsyms at runtime. Write such code. - if (headerSize) - target->writePltHeader(buf); + target->writePltHeader(buf); size_t off = headerSize; for (size_t i = 0, e = entries.size(); i != e; ++i) { @@ -2489,12 +2487,15 @@ return headerSize + entries.size() * target->pltEntrySize; } -// Some architectures such as additional symbols in the PLT section. For -// example ARM uses mapping symbols to aid disassembly +bool PltSection::isNeeded() const { + // For -z retpolineplt, .iplt needs the .plt header. + return !entries.empty() || (config->zRetpolineplt && in.iplt->isNeeded()); +} + +// Used by ARM to add mapping symbols in the PLT section, which aid +// disassembly. void PltSection::addSymbols() { - // The PLT may have symbols defined for the Header, the IPLT has no header - if (!isIplt) - target->addPltHeaderSymbols(*this); + target->addPltHeaderSymbols(*this); size_t off = headerSize; for (size_t i = 0; i < entries.size(); ++i) { @@ -2503,6 +2504,40 @@ } } +IpltSection::IpltSection() + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".plt") { + if (config->emachine == EM_PPC || config->emachine == EM_PPC64) { + name = ".glink"; + } +} + +void IpltSection::writeTo(uint8_t *buf) { + uint32_t off = 0; + for (const Symbol *sym : entries) { + target->writeIplt(buf + off, sym->getGotPltVA(), getVA() + off, + sym->pltIndex); + off += target->ipltEntrySize; + } +} + +size_t IpltSection::getSize() const { + return entries.size() * target->ipltEntrySize; +} + +void IpltSection::addEntry(Symbol &sym) { + sym.pltIndex = entries.size(); + entries.push_back(&sym); +} + +// ARM uses mapping symbols to aid disassembly. +void IpltSection::addSymbols() { + size_t off = 0; + for (size_t i = 0, e = entries.size(); i != e; ++i) { + target->addPltSymbols(*this, off); + off += target->pltEntrySize; + } +} + // The string hash function for .gdb_index. static uint32_t computeGdbHash(StringRef s) { uint32_t h = 0; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -43,6 +43,11 @@ virtual void writePlt(uint8_t *buf, uint64_t gotEntryAddr, uint64_t pltEntryAddr, int32_t index) const {} + virtual void writeIplt(uint8_t *buf, uint64_t gotEntryAddr, + uint64_t pltEntryAddr, int32_t index) const { + // All but PPC64 use the same format for .plt and .iplt entries. + writePlt(buf, gotEntryAddr, pltEntryAddr, index); + } virtual void addPltHeaderSymbols(InputSection &isec) const {} virtual void addPltSymbols(InputSection &isec, uint64_t off) const {} @@ -101,6 +106,7 @@ RelType tlsOffsetRel; unsigned pltEntrySize; unsigned pltHeaderSize; + unsigned ipltEntrySize; // At least on x86_64 positions 1 and 2 are used by the first plt entry // to support lazy loading. diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -515,9 +515,9 @@ /*sort=*/false); add(in.relaIplt); - in.plt = make(false); + in.plt = make(); add(in.plt); - in.iplt = make(true); + in.iplt = make(); add(in.iplt); if (config->andFeatures) diff --git a/lld/test/ELF/gnu-ifunc-i386.s b/lld/test/ELF/gnu-ifunc-i386.s --- a/lld/test/ELF/gnu-ifunc-i386.s +++ b/lld/test/ELF/gnu-ifunc-i386.s @@ -123,11 +123,11 @@ // DISASM-NEXT: foo: // DISASM-NEXT: 401100: jmpl *4202784 // DISASM-NEXT: pushl $0 -// DISASM-NEXT: jmp -32 <_start+0xa> +// DISASM-NEXT: jmp -16 // DISASM: bar: // DISASM-NEXT: 401110: jmpl *4202788 // DISASM-NEXT: pushl $8 -// DISASM-NEXT: jmp -48 <_start+0xa> +// DISASM-NEXT: jmp -32 .text .type foo STT_GNU_IFUNC diff --git a/lld/test/ELF/gnu-ifunc-plt-i386.s b/lld/test/ELF/gnu-ifunc-plt-i386.s --- a/lld/test/ELF/gnu-ifunc-plt-i386.s +++ b/lld/test/ELF/gnu-ifunc-plt-i386.s @@ -63,10 +63,10 @@ // DISASM-NEXT: jmp -48 <.plt> // DISASM-NEXT: jmpl *4207276 // DISASM-NEXT: pushl $0 -// DISASM-NEXT: jmp -32 +// DISASM-NEXT: jmp -64 <.plt> // DISASM-NEXT: jmpl *4207280 // DISASM-NEXT: pushl $8 -// DISASM-NEXT: jmp -48 +// DISASM-NEXT: jmp -80 <.plt> .text .type foo STT_GNU_IFUNC diff --git a/lld/test/ELF/gnu-ifunc-plt.s b/lld/test/ELF/gnu-ifunc-plt.s --- a/lld/test/ELF/gnu-ifunc-plt.s +++ b/lld/test/ELF/gnu-ifunc-plt.s @@ -64,10 +64,10 @@ // DISASM-NEXT: 20131b: jmp -48 <.plt> // DISASM-NEXT: 201320: jmpq *8498(%rip) // DISASM-NEXT: 201326: pushq $0 -// DISASM-NEXT: 20132b: jmp -32 +// DISASM-NEXT: 20132b: jmp -64 <.plt> // DISASM-NEXT: 201330: jmpq *8490(%rip) // DISASM-NEXT: 201336: pushq $1 -// DISASM-NEXT: 20133b: jmp -48 +// DISASM-NEXT: 20133b: jmp -80 <.plt> .text .type foo STT_GNU_IFUNC diff --git a/lld/test/ELF/gnu-ifunc-shared.s b/lld/test/ELF/gnu-ifunc-shared.s --- a/lld/test/ELF/gnu-ifunc-shared.s +++ b/lld/test/ELF/gnu-ifunc-shared.s @@ -41,7 +41,7 @@ // DISASM-NEXT: 134b: jmp -48 <.plt> // DISASM-NEXT: 1350: jmpq *8466(%rip) // DISASM-NEXT: 1356: pushq $0 -// DISASM-NEXT: 135b: jmp -32 +// DISASM-NEXT: 135b: jmp -64 <.plt> // CHECK: Relocations [ // CHECK-NEXT: Section (5) .rela.dyn { diff --git a/lld/test/ELF/gnu-ifunc.s b/lld/test/ELF/gnu-ifunc.s --- a/lld/test/ELF/gnu-ifunc.s +++ b/lld/test/ELF/gnu-ifunc.s @@ -107,10 +107,10 @@ // DISASM-NEXT: .plt: // DISASM-NEXT: 2011b0: {{.*}} jmpq *4122(%rip) // DISASM-NEXT: 2011b6: {{.*}} pushq $0 -// DISASM-NEXT: 2011bb: {{.*}} jmp -32 <_start+0x16> +// DISASM-NEXT: 2011bb: {{.*}} jmp -16 <.plt> // DISASM-NEXT: 2011c0: {{.*}} jmpq *4114(%rip) // DISASM-NEXT: 2011c6: {{.*}} pushq $1 -// DISASM-NEXT: 2011cb: {{.*}} jmp -48 <_start+0x16> +// DISASM-NEXT: 2011cb: {{.*}} jmp -32 <.plt> .text .type foo STT_GNU_IFUNC