Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -34,6 +34,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; + void writePltGot(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const override; void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override; RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data, @@ -158,6 +160,17 @@ write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16); } +template +void X86_64::writePltGot(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const { + const uint8_t Inst[] = { + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmpq *got(%rip) + 0xcc, 0xcc // trap instructions + }; + memcpy(Buf, Inst, sizeof(Inst)); + write32le(Buf + 2, GotEntryAddr - PltEntryAddr - 6); +} + template bool X86_64::isPicRel(uint32_t Type) const { return Type != R_X86_64_PC32 && Type != R_X86_64_32 && Type != R_X86_64_TPOFF32; Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -881,6 +881,8 @@ if (Body.isGnuIFunc() && !Preemptible) addPltEntry(InX::Iplt, InX::IgotPlt, In::RelaIplt, Target->IRelativeRel, Body, true); + else if (Body.isInGot() && InX::PltGot) + InX::PltGot->addEntry(Body); else addPltEntry(InX::Plt, InX::GotPlt, In::RelaPlt, Target->PltRel, Body, !Preemptible); Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -121,6 +121,9 @@ // True if this symbol is in the Igot sub-section of the .got.plt or .got. unsigned IsInIgot : 1; + // True if this symbol is in the .plt.got section. + unsigned IsInPltGot : 1; + unsigned IsPreemptible : 1; // The following fields have the same meaning as the ELF symbol attributes. Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -127,8 +127,8 @@ uint8_t Type) : SymbolKind(K), NeedsPltAddr(false), IsLocal(IsLocal), IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false), - IsInIgot(false), IsPreemptible(false), Type(Type), StOther(StOther), - Name(Name) {} + IsInIgot(false), IsInPltGot(false), IsPreemptible(false), Type(Type), + StOther(StOther), Name(Name) {} InputFile *SymbolBody::getFile() const { if (isLocal()) { @@ -175,6 +175,8 @@ uint64_t SymbolBody::getPltVA() const { if (this->IsInIplt) return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize; + if (this->IsInPltGot) + return InX::PltGot->getVA() + PltIndex * InX::PltGot->Entsize; return InX::Plt->getVA() + Target->PltHeaderSize + PltIndex * Target->PltEntrySize; } Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -493,6 +493,23 @@ size_t HeaderSize; }; +// When there are both PLT and GOT references to symbol, linker normally +// creates GOTPLT slot for PLT entry and GOT slot for GOT reference. Two +// dynamic relocations are used. It is possible to combine GOTPLT and GOT slot. +// PltGotSection is used for this optimization, it contains special 8-bytes +// entries that are used instead of regular PLT entries. +class PltGotSection : public SyntheticSection { +public: + PltGotSection(); + void writeTo(uint8_t *Buf) override; + size_t getSize() const override { return Entries.size() * 8; } + bool empty() const override { return Entries.empty(); } + void addEntry(SymbolBody &Sym); + +private: + std::vector Entries; +}; + class GdbIndexSection final : public SyntheticSection { const unsigned OffsetTypeSize = 4; const unsigned CuListOffset = 6 * OffsetTypeSize; @@ -767,6 +784,7 @@ static MipsGotSection *MipsGot; static MipsRldMapSection *MipsRldMap; static PltSection *Plt; + static PltGotSection *PltGot; static PltSection *Iplt; static StringTableSection *ShStrTab; static StringTableSection *StrTab; Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1708,6 +1708,27 @@ return (HeaderSize == 0) ? InX::Plt->getSize() : 0; } +PltGotSection::PltGotSection() + : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 8, ".plt.got") { + Entsize = 8; +} + +void PltGotSection::writeTo(uint8_t *Buf) { + uint64_t Off = 0; + for (SymbolBody *B : Entries) { + uint64_t Got = B->getGotVA(); + uint64_t Plt = this->getVA() + Off; + Target->writePltGot(Buf + Off, Got, Plt); + Off += Target->PltEntrySize; + } +} + +void PltGotSection::addEntry(SymbolBody &Sym) { + Sym.PltIndex = Entries.size(); + Sym.IsInPltGot = true; + Entries.push_back(&Sym); +} + // The hash function used for .gdb_index version 5 or above. static uint32_t gdbHash(StringRef Str) { uint32_t R = 0; @@ -2330,6 +2351,7 @@ MipsGotSection *InX::MipsGot; MipsRldMapSection *InX::MipsRldMap; PltSection *InX::Plt; +PltGotSection *InX::PltGot; PltSection *InX::Iplt; StringTableSection *InX::ShStrTab; StringTableSection *InX::StrTab; Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -38,6 +38,8 @@ virtual void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const {} + virtual void writePltGot(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const {} virtual void addPltHeaderSymbols(InputSectionBase *IS) const {} virtual void addPltSymbols(InputSectionBase *IS, uint64_t Off) const {} // Returns true if a relocation only uses the low bits of a value such that Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -388,6 +388,11 @@ InX::Iplt = make(0); Add(InX::Iplt); + if (Config->EMachine == EM_X86_64) { + InX::PltGot = make(); + Add(InX::PltGot); + } + if (!Config->Relocatable) { if (Config->EhFrameHdr) { In::EhFrameHdr = make>(); Index: test/ELF/gotplt-x86-64.s =================================================================== --- test/ELF/gotplt-x86-64.s +++ test/ELF/gotplt-x86-64.s @@ -0,0 +1,60 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t +# RUN: ld.lld %t -shared -o %t.so +# RUN: llvm-readobj -s -r %t.so | FileCheck %s +# RUN: llvm-objdump -d %t.so | FileCheck %s --check-prefix=DISASM + +# CHECK: Section { +# CHECK: Index: +# CHECK: Name: .plt.got +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_EXECINSTR +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x1010 +# CHECK-NEXT: Offset: 0x1010 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 8 +# CHECK-NEXT: EntrySize: 8 +# CHECK-NEXT: } +# CHECK: Section { +# CHECK: Index: +# CHECK: Name: .got +# CHECK-NEXT: Type: SHT_PROGBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_WRITE +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x2090 +# CHECK-NEXT: Offset: 0x2090 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 8 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK: Relocations [ +# CHECK-NEXT: Section ({{.*}}) .rela.dyn { +# CHECK-NEXT: 0x2090 R_X86_64_GLOB_DAT foo 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +# DISASM: Disassembly of section .text: +# DISASM-NEXT: _start: +## 4233 == 0x1089; 0x1000 + 7 + 0x1089 == 0x2090 (.got). +# DISASM-NEXT: 1000: {{.*}} movq 4233(%rip), %rax +## 0x1007 + 4 + 5 == 0x1010 (.plt.got[0]) +# DISASM-NEXT: 1007: {{.*}} callq 4 +# DISASM-NEXT: Disassembly of section .plt.got: +# DISASM-NEXT: .plt.got: +# DISASM-NEXT: 1010: {{.*}} jmpq *4218(%rip) +# DISASM-NEXT: 1016: {{.*}} int3 +# DISASM-NEXT: 1017: {{.*}} int3 + +.globl _start +_start: + movq foo@GOTPCREL(%rip), %rax + callq foo@PLT