[ELF] Add IpltSection
authorFangrui Song <maskray@google.com>
Sat, 14 Dec 2019 22:17:35 +0000 (14:17 -0800)
committerFangrui Song <maskray@google.com>
Tue, 17 Dec 2019 08:06:04 +0000 (00:06 -0800)
PltSection is used by both PLT and IPLT. The PLT section may have a
header while the IPLT section does not. Split off IpltSection from
PltSection to be clearer.

Unlike other targets, PPC64 cannot use the same code sequence for PLT
and IPLT. This helps make a future PPC64 patch (D71509) more isolated.

On EM_386 and EM_X86_64, when PLT is empty while IPLT is not, currently
we are inconsistent whether the PLT header is conceptually attached to
in.plt or in.iplt .  Consistently attach the header to in.plt can make
the -z retpolineplt logic simpler. It also makes `jmp` point to an
aesthetically better place for non-retpolineplt cases.

Reviewed By: grimar, ruiu

Differential Revision: https://reviews.llvm.org/D71519

18 files changed:
lld/ELF/Arch/AArch64.cpp
lld/ELF/Arch/ARM.cpp
lld/ELF/Arch/PPC.cpp
lld/ELF/Arch/PPC64.cpp
lld/ELF/Arch/RISCV.cpp
lld/ELF/Arch/X86.cpp
lld/ELF/Arch/X86_64.cpp
lld/ELF/Relocations.cpp
lld/ELF/Symbols.cpp
lld/ELF/SyntheticSections.cpp
lld/ELF/SyntheticSections.h
lld/ELF/Target.h
lld/ELF/Writer.cpp
lld/test/ELF/gnu-ifunc-i386.s
lld/test/ELF/gnu-ifunc-plt-i386.s
lld/test/ELF/gnu-ifunc-plt.s
lld/test/ELF/gnu-ifunc-shared.s
lld/test/ELF/gnu-ifunc.s

index 6697ee3..8d04c00 100644 (file)
@@ -64,8 +64,9 @@ AArch64::AArch64() {
   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 @@ AArch64BtiPac::AArch64BtiPac() {
   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 {
index d4df027..7172ee0 100644 (file)
@@ -59,8 +59,9 @@ ARM::ARM() {
   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;
 }
index be8ca30..9b2722d 100644 (file)
@@ -144,6 +144,7 @@ PPC::PPC() {
   gotPltHeaderEntriesNum = 0;
   pltHeaderSize = 64; // size of PLTresolve in .glink
   pltEntrySize = 4;
+  ipltEntrySize = 4;
 
   needsThunks = true;
 
index e9a1c53..9043226 100644 (file)
@@ -296,11 +296,12 @@ PPC64::PPC64() {
   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;
index 4b5006e..5877faa 100644 (file)
@@ -95,8 +95,9 @@ RISCV::RISCV() {
   // .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) {
index 31b3b0a..8d54e7e 100644 (file)
@@ -57,8 +57,9 @@ X86::X86() {
   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 @@ void X86::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
   }
 
   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 @@ public:
 RetpolinePic::RetpolinePic() {
   pltHeaderSize = 48;
   pltEntrySize = 32;
+  ipltEntrySize = 32;
 }
 
 void RetpolinePic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
@@ -474,7 +476,7 @@ void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
   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 @@ void RetpolinePic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
 RetpolineNoPic::RetpolineNoPic() {
   pltHeaderSize = 48;
   pltEntrySize = 32;
+  ipltEntrySize = 32;
 }
 
 void RetpolineNoPic::writeGotPlt(uint8_t *buf, const Symbol &s) const {
@@ -532,7 +535,7 @@ void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
   };
   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);
index 459fd11..d2c4828 100644 (file)
@@ -61,8 +61,9 @@ X86_64::X86_64() {
   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 @@ void X86_64::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
 
   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 @@ public:
 Retpoline::Retpoline() {
   pltHeaderSize = 48;
   pltEntrySize = 32;
+  ipltEntrySize = 32;
 }
 
 void Retpoline::writeGotPlt(uint8_t *buf, const Symbol &s) const {
@@ -639,7 +641,7 @@ void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
   };
   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 @@ void Retpoline::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
 RetpolineZNow::RetpolineZNow() {
   pltHeaderSize = 32;
   pltEntrySize = 16;
+  ipltEntrySize = 16;
 }
 
 void RetpolineZNow::writePltHeader(uint8_t *buf) const {
@@ -679,7 +682,7 @@ void RetpolineZNow::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
   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() {
index 31b2a14..78bafe5 100644 (file)
@@ -1011,7 +1011,7 @@ static void addRelativeReloc(InputSectionBase *isec, uint64_t offsetInSec,
                          expr, type);
 }
 
-template <class GotPltSection>
+template <class PltSection, class GotPltSection>
 static void addPltEntry(PltSection *plt, GotPltSection *gotPlt,
                         RelocationBaseSection *rel, RelType type, Symbol &sym) {
   plt->addEntry(sym);
@@ -1415,13 +1415,9 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
     } 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<Defined>(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.
index f81e73e..365be6f 100644 (file)
@@ -163,9 +163,11 @@ uint64_t Symbol::getGotPltOffset() const {
 }
 
 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.
index 46529eb..4cb398b 100644 (file)
@@ -2445,14 +2445,13 @@ void HashTableSection::writeTo(uint8_t *buf) {
 
 // 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 @@ void PltSection::writeTo(uint8_t *buf) {
     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 @@ size_t PltSection::getSize() const {
   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 @@ void PltSection::addSymbols() {
   }
 }
 
+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;
index 601f660..83446ce 100644 (file)
@@ -662,16 +662,15 @@ private:
   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 @@ public:
 
 private:
   std::vector<const Symbol *> 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<const Symbol *> 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 @@ struct InStruct {
   SyntheticSection *partEnd;
   SyntheticSection *partIndex;
   PltSection *plt;
-  PltSection *iplt;
+  IpltSection *iplt;
   PPC32Got2Section *ppc32Got2;
   RelocationBaseSection *relaPlt;
   RelocationBaseSection *relaIplt;
index 8abf580..2b0b09c 100644 (file)
@@ -43,6 +43,11 @@ public:
 
   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 @@ public:
   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.
index 38009bd..c0ad334 100644 (file)
@@ -515,9 +515,9 @@ template <class ELFT> void createSyntheticSections() {
       /*sort=*/false);
   add(in.relaIplt);
 
-  in.plt = make<PltSection>(false);
+  in.plt = make<PltSection>();
   add(in.plt);
-  in.iplt = make<PltSection>(true);
+  in.iplt = make<IpltSection>();
   add(in.iplt);
 
   if (config->andFeatures)
index 6d35059..8b82347 100644 (file)
 // DISASM-NEXT: foo:
 // DISASM-NEXT:   401100:       jmpl *4202784
 // DISASM-NEXT:                 pushl $0
-// DISASM-NEXT:                 jmp -32 <_start+0xa>
+// DISASM-NEXT:                 jmp -16 <foo>
 // DISASM:      bar:
 // DISASM-NEXT:   401110:       jmpl *4202788
 // DISASM-NEXT:                 pushl $8
-// DISASM-NEXT:                 jmp -48 <_start+0xa>
+// DISASM-NEXT:                 jmp -32 <foo>
 
 .text
 .type foo STT_GNU_IFUNC
index 180ecc3..4c19824 100644 (file)
 // DISASM-NEXT:                  jmp     -48 <.plt>
 // DISASM-NEXT:                  jmpl    *4207276
 // DISASM-NEXT:                  pushl   $0
-// DISASM-NEXT:                  jmp     -32 <zed2@plt>
+// DISASM-NEXT:                  jmp     -64 <.plt>
 // DISASM-NEXT:                  jmpl    *4207280
 // DISASM-NEXT:                  pushl   $8
-// DISASM-NEXT:                  jmp     -48 <zed2@plt>
+// DISASM-NEXT:                  jmp     -80 <.plt>
 
 .text
 .type foo STT_GNU_IFUNC
index 0610d85..c359a1d 100644 (file)
 // DISASM-NEXT:   20131b:       jmp     -48 <.plt>
 // DISASM-NEXT:   201320:       jmpq    *8498(%rip)
 // DISASM-NEXT:   201326:       pushq   $0
-// DISASM-NEXT:   20132b:       jmp     -32 <zed2@plt>
+// DISASM-NEXT:   20132b:       jmp     -64 <.plt>
 // DISASM-NEXT:   201330:       jmpq    *8490(%rip)
 // DISASM-NEXT:   201336:       pushq   $1
-// DISASM-NEXT:   20133b:       jmp     -48 <zed2@plt>
+// DISASM-NEXT:   20133b:       jmp     -80 <.plt>
 
 .text
 .type foo STT_GNU_IFUNC
index d0e7010..b630776 100644 (file)
@@ -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 <f2@plt>
+// DISASM-NEXT:     135b:       jmp     -64 <.plt>
 
 // CHECK: Relocations [
 // CHECK-NEXT:   Section (5) .rela.dyn {
index 97e8e9c..e5b2af4 100644 (file)
 // 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