Index: lld/trunk/ELF/OutputSections.h =================================================================== --- lld/trunk/ELF/OutputSections.h +++ lld/trunk/ELF/OutputSections.h @@ -232,6 +232,8 @@ bool hasRelocs() const { return !Relocs.empty(); } bool isRela() const { return IsRela; } + bool Static = false; + private: bool applyTlsDynamicReloc(SymbolBody *Body, uint32_t Type, Elf_Rel *P, Elf_Rel *N); Index: lld/trunk/ELF/OutputSections.cpp =================================================================== --- lld/trunk/ELF/OutputSections.cpp +++ lld/trunk/ELF/OutputSections.cpp @@ -269,7 +269,9 @@ unsigned Sym = CanBePreempted ? Body->DynamicSymbolTableIndex : 0; unsigned Reloc; - if (!CanBePreempted || IsDynRelative) + if (!CanBePreempted && Body && isGnuIFunc(*Body)) + Reloc = Target->getIRelativeReloc(); + else if (!CanBePreempted || IsDynRelative) Reloc = Target->getRelativeReloc(); else if (LazyReloc) Reloc = Target->getPltReloc(); @@ -320,7 +322,8 @@ } template void RelocationSection::finalize() { - this->Header.sh_link = Out::DynSymTab->SectionIndex; + this->Header.sh_link = Static ? Out::SymTab->SectionIndex + : Out::DynSymTab->SectionIndex; this->Header.sh_size = Relocs.size() * this->Header.sh_entsize; } Index: lld/trunk/ELF/Symbols.h =================================================================== --- lld/trunk/ELF/Symbols.h +++ lld/trunk/ELF/Symbols.h @@ -183,6 +183,11 @@ // The content for _gp symbol for MIPS target. static Elf_Sym MipsGp; + // __rel_iplt_start/__rel_iplt_end for signaling + // where R_[*]_IRELATIVE relocations do live. + static Elf_Sym RelaIpltStart; + static Elf_Sym RelaIpltEnd; + DefinedAbsolute(StringRef N, const Elf_Sym &Sym) : Defined(Base::DefinedAbsoluteKind, N, Sym) {} @@ -200,6 +205,12 @@ template typename DefinedAbsolute::Elf_Sym DefinedAbsolute::MipsGp; +template +typename DefinedAbsolute::Elf_Sym DefinedAbsolute::RelaIpltStart; + +template +typename DefinedAbsolute::Elf_Sym DefinedAbsolute::RelaIpltEnd; + template class DefinedCommon : public Defined { typedef ELFSymbolBody Base; typedef typename Base::Elf_Sym Elf_Sym; Index: lld/trunk/ELF/Target.h =================================================================== --- lld/trunk/ELF/Target.h +++ lld/trunk/ELF/Target.h @@ -27,6 +27,7 @@ unsigned getGotReloc() const { return GotReloc; } unsigned getPltReloc() const { return PltReloc; } unsigned getRelativeReloc() const { return RelativeReloc; } + unsigned getIRelativeReloc() const { return IRelativeReloc; } bool isTlsLocalDynamicReloc(unsigned Type) const { return Type == TlsLocalDynamicReloc; } @@ -88,6 +89,7 @@ unsigned GotReloc; unsigned PltReloc; unsigned RelativeReloc; + unsigned IRelativeReloc; unsigned TlsGotReloc = 0; unsigned TlsLocalDynamicReloc = 0; unsigned TlsGlobalDynamicReloc = 0; @@ -105,6 +107,8 @@ template typename llvm::object::ELFFile::uintX_t getMipsGpAddr(); +template bool isGnuIFunc(const SymbolBody &S); + extern std::unique_ptr Target; TargetInfo *createTarget(); } Index: lld/trunk/ELF/Target.cpp =================================================================== --- lld/trunk/ELF/Target.cpp +++ lld/trunk/ELF/Target.cpp @@ -70,6 +70,17 @@ error("Improper alignment for relocation " + S); } +template bool isGnuIFunc(const SymbolBody &S) { + if (auto *SS = dyn_cast>(&S)) + return SS->Sym.getType() == STT_GNU_IFUNC; + return false; +} + +template bool isGnuIFunc(const SymbolBody &S); +template bool isGnuIFunc(const SymbolBody &S); +template bool isGnuIFunc(const SymbolBody &S); +template bool isGnuIFunc(const SymbolBody &S); + namespace { class X86TargetInfo final : public TargetInfo { public: @@ -258,6 +269,7 @@ PCRelReloc = R_386_PC32; GotReloc = R_386_GLOB_DAT; PltReloc = R_386_JUMP_SLOT; + IRelativeReloc = R_386_IRELATIVE; RelativeReloc = R_386_RELATIVE; TlsGotReloc = R_386_TLS_TPOFF; TlsGlobalDynamicReloc = R_386_TLS_GD; @@ -357,7 +369,8 @@ } bool X86TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { - return (Type == R_386_PLT32 && canBePreempted(&S, true)) || + return isGnuIFunc(S) || + (Type == R_386_PLT32 && canBePreempted(&S, true)) || (Type == R_386_PC32 && S.isShared()); } @@ -557,6 +570,7 @@ GotReloc = R_X86_64_GLOB_DAT; PltReloc = R_X86_64_JUMP_SLOT; RelativeReloc = R_X86_64_RELATIVE; + IRelativeReloc = R_X86_64_IRELATIVE; TlsGotReloc = R_X86_64_TPOFF64; TlsLocalDynamicReloc = R_X86_64_TLSLD; TlsGlobalDynamicReloc = R_X86_64_TLSGD; @@ -633,6 +647,8 @@ bool X86_64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { if (needsCopyRel(Type, S)) return false; + if (isGnuIFunc(S)) + return true; switch (Type) { default: Index: lld/trunk/ELF/Writer.cpp =================================================================== --- lld/trunk/ELF/Writer.cpp +++ lld/trunk/ELF/Writer.cpp @@ -277,6 +277,15 @@ } } + // An STT_GNU_IFUNC symbol always uses a PLT entry, and all references + // to the symbol go through the PLT. This is true even for a local + // symbol, although local symbols normally do not require PLT entries. + if (Body && isGnuIFunc(*Body)) { + Body->setUsedInDynamicReloc(); + Out::RelaPlt->addReloc({&C, &RI}); + continue; + } + if (Config->EMachine == EM_MIPS && NeedsGot) { // MIPS ABI has special rules to process GOT entries // and doesn't require relocation entries for them. @@ -578,6 +587,27 @@ return std::distance(ItA, ItB) > 0; } +// A statically linked executable will have rel[a].plt section +// to hold R_[*]_IRELATIVE relocations. +// The multi-arch libc will use these symbols to locate +// these relocations at program startup time. +// If RelaPlt is empty then there is no reason to create this symbols. +template +static void addIRelocMarkers(SymbolTable &Symtab, bool IsDynamic) { + if (IsDynamic || !Out::RelaPlt || !Out::RelaPlt->hasRelocs()) + return; + bool IsRela = shouldUseRela(); + auto AddMarker = [&](StringRef Name, typename Writer::Elf_Sym &Sym) { + if (SymbolBody *B = Symtab.find(Name)) + if (B->isUndefined()) + Symtab.addAbsolute(Name, Sym); + }; + AddMarker(IsRela ? "__rela_iplt_start" : "__rel_iplt_start", + DefinedAbsolute::RelaIpltStart); + AddMarker(IsRela ? "__rela_iplt_end" : "__rel_iplt_end", + DefinedAbsolute::RelaIpltEnd); +} + // Create output section objects and add them to OutputSections. template void Writer::createSections() { // .interp needs to be on the first page in the output file. @@ -720,6 +750,8 @@ } } + addIRelocMarkers(Symtab, isOutputDynamic()); + std::vector *> CommonSymbols; std::vector *> SharedCopySymbols; for (auto &P : Symtab.getSymbols()) { @@ -762,8 +794,6 @@ OutputSections.push_back(Out::DynStrTab); if (Out::RelaDyn->hasRelocs()) OutputSections.push_back(Out::RelaDyn); - if (Out::RelaPlt && Out::RelaPlt->hasRelocs()) - OutputSections.push_back(Out::RelaPlt); // This is a MIPS specific section to hold a space within the data segment // of executable file which is pointed to by the DT_MIPS_RLD_MAP entry. // See "Dynamic section" in Chapter 5 in the following document: @@ -777,6 +807,13 @@ } } + // We always need to add rel[a].plt to output if it has entries. + // Even during static linking it can contain R_[*]_IRELATIVE relocations. + if (Out::RelaPlt && Out::RelaPlt->hasRelocs()) { + OutputSections.push_back(Out::RelaPlt); + Out::RelaPlt->Static = !isOutputDynamic(); + } + bool needsGot = !Out::Got->empty(); // We add the .got section to the result for dynamic MIPS target because // its address and properties are mentioned in the .dynamic section. @@ -997,6 +1034,15 @@ // point to the end of the data segment. DefinedAbsolute::End.st_value = VA; + // Update __rel_iplt_start/__rel_iplt_end to wrap the + // rela.plt section. + if (Out::RelaPlt) { + uintX_t Start = Out::RelaPlt->getVA(); + DefinedAbsolute::RelaIpltStart.st_value = Start; + DefinedAbsolute::RelaIpltEnd.st_value = + Start + Out::RelaPlt->getSize(); + } + // Update MIPS _gp absolute symbol so that it points to the static data. if (Config->EMachine == EM_MIPS) DefinedAbsolute::MipsGp.st_value = getMipsGpAddr(); Index: lld/trunk/test/ELF/gnu-ifunc-i386.s =================================================================== --- lld/trunk/test/ELF/gnu-ifunc-i386.s +++ lld/trunk/test/ELF/gnu-ifunc-i386.s @@ -0,0 +1,130 @@ +// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o +// RUN: ld.lld -static %t.o -o %tout +// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DISASM +// RUN: llvm-readobj -r -symbols -sections %tout | FileCheck %s --check-prefix=CHECK +// REQUIRES: x86 + +// CHECK: Sections [ +// CHECK: Section { +// CHECK: Index: 1 +// CHECK-NEXT: Name: .rel.plt +// CHECK-NEXT: Type: SHT_REL +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: ] +// CHECK-NEXT: Address: [[RELA:.*]] +// CHECK-NEXT: Offset: 0xD4 +// CHECK-NEXT: Size: 16 +// CHECK-NEXT: Link: 5 +// CHECK-NEXT: Info: 0 +// CHECK-NEXT: AddressAlignment: 4 +// CHECK-NEXT: EntrySize: 8 +// CHECK-NEXT: } +// CHECK: Relocations [ +// CHECK-NEXT: Section ({{.*}}) .rel.plt { +// CHECK-NEXT: 0x1200C R_386_IRELATIVE +// CHECK-NEXT: 0x12010 R_386_IRELATIVE +// CHECK-NEXT: } +// CHECK-NEXT: ] + +// CHECK: Symbols [ +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: __rel_iplt_end +// CHECK-NEXT: Value: 0x100E4 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Absolute +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: __rel_iplt_start +// CHECK-NEXT: Value: [[RELA]] +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Absolute +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: _start +// CHECK-NEXT: Value: 0x11002 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: bar +// CHECK-NEXT: Value: 0x11001 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global +// CHECK-NEXT: Type: GNU_IFunc +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: foo +// CHECK-NEXT: Value: 0x11000 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global +// CHECK-NEXT: Type: GNU_IFunc +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text +// CHECK-NEXT: } +// CHECK-NEXT:] + +// DISASM: Disassembly of section .text: +// DISASM-NEXT: foo: +// DISASM-NEXT: 11000: c3 retl +// DISASM: bar: +// DISASM-NEXT: 11001: c3 retl +// DISASM: _start: +// DISASM-NEXT: 11002: e8 29 00 00 00 calll 41 +// DISASM-NEXT: 11007: e8 34 00 00 00 calll 52 +// DISASM-NEXT: 1100c: ba d4 00 01 00 movl $65748, %edx +// DISASM-NEXT: 11011: ba e4 00 01 00 movl $65764, %edx +// DISASM-NEXT: Disassembly of section .plt: +// DISASM-NEXT: .plt: +// DISASM-NEXT: 11020: ff 35 04 20 01 00 pushl 73732 +// DISASM-NEXT: 11026: ff 25 08 20 01 00 jmpl *73736 +// DISASM-NEXT: 1102c: 90 nop +// DISASM-NEXT: 1102d: 90 nop +// DISASM-NEXT: 1102e: 90 nop +// DISASM-NEXT: 1102f: 90 nop +// DISASM-NEXT: 11030: ff 25 0c 20 01 00 jmpl *73740 +// DISASM-NEXT: 11036: 68 00 00 00 00 pushl $0 +// DISASM-NEXT: 1103b: e9 e0 ff ff ff jmp -32 <.plt> +// DISASM-NEXT: 11040: ff 25 10 20 01 00 jmpl *73744 +// DISASM-NEXT: 11046: 68 08 00 00 00 pushl $8 +// DISASM-NEXT: 1104b: e9 d0 ff ff ff jmp -48 <.plt> + +.text +.type foo STT_GNU_IFUNC +.globl foo +.type foo, @function +foo: + ret + +.type bar STT_GNU_IFUNC +.globl bar +.type bar, @function +bar: + ret + +.globl _start +_start: + call foo + call bar + movl $__rel_iplt_start,%edx + movl $__rel_iplt_end,%edx Index: lld/trunk/test/ELF/gnu-ifunc-nosym-i386.s =================================================================== --- lld/trunk/test/ELF/gnu-ifunc-nosym-i386.s +++ lld/trunk/test/ELF/gnu-ifunc-nosym-i386.s @@ -0,0 +1,29 @@ +// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o +// RUN: ld.lld -static %t.o -o %tout +// RUN: llvm-readobj -symbols %tout | FileCheck %s +// REQUIRES: x86 + +// Check that no __rel_iplt_end/__rel_iplt_start +// appear in symtab if there is no references to them. +// CHECK: Symbols [ +// CHECK-NEXT-NOT: __rel_iplt_end +// CHECK-NEXT-NOT: __rel_iplt_start +// CHECK: ] + +.text +.type foo STT_GNU_IFUNC +.globl foo +.type foo, @function +foo: + ret + +.type bar STT_GNU_IFUNC +.globl bar +.type bar, @function +bar: + ret + +.globl _start +_start: + call foo + call bar Index: lld/trunk/test/ELF/gnu-ifunc-nosym.s =================================================================== --- lld/trunk/test/ELF/gnu-ifunc-nosym.s +++ lld/trunk/test/ELF/gnu-ifunc-nosym.s @@ -0,0 +1,29 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +// RUN: ld.lld -static %t.o -o %tout +// RUN: llvm-readobj -symbols %tout | FileCheck %s +// REQUIRES: x86 + +// Check that no __rela_iplt_end/__rela_iplt_start +// appear in symtab if there is no references to them. +// CHECK: Symbols [ +// CHECK-NEXT-NOT: __rela_iplt_end +// CHECK-NEXT-NOT: __rela_iplt_start +// CHECK: ] + +.text +.type foo STT_GNU_IFUNC +.globl foo +.type foo, @function +foo: + ret + +.type bar STT_GNU_IFUNC +.globl bar +.type bar, @function +bar: + ret + +.globl _start +_start: + call foo + call bar Index: lld/trunk/test/ELF/gnu-ifunc.s =================================================================== --- lld/trunk/test/ELF/gnu-ifunc.s +++ lld/trunk/test/ELF/gnu-ifunc.s @@ -0,0 +1,126 @@ +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +// RUN: ld.lld -static %t.o -o %tout +// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DISASM +// RUN: llvm-readobj -r -symbols -sections %tout | FileCheck %s --check-prefix=CHECK +// REQUIRES: x86 + +// CHECK: Sections [ +// CHECK: Section { +// CHECK: Index: 1 +// CHECK-NEXT: Name: .rela.plt +// CHECK-NEXT: Type: SHT_RELA +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: ] +// CHECK-NEXT: Address: [[RELA:.*]] +// CHECK-NEXT: Offset: 0x158 +// CHECK-NEXT: Size: 48 +// CHECK-NEXT: Link: 5 +// CHECK-NEXT: Info: 0 +// CHECK-NEXT: AddressAlignment: 8 +// CHECK-NEXT: EntrySize: 24 +// CHECK-NEXT: } +// CHECK: Relocations [ +// CHECK-NEXT: Section ({{.*}}) .rela.plt { +// CHECK-NEXT: 0x12018 R_X86_64_IRELATIVE +// CHECK-NEXT: 0x12020 R_X86_64_IRELATIVE +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK: Symbols [ +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Undefined +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: __rela_iplt_end +// CHECK-NEXT: Value: 0x10188 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Absolute +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: __rela_iplt_start +// CHECK-NEXT: Value: [[RELA]] +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: Absolute +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: _start +// CHECK-NEXT: Value: 0x11002 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global +// CHECK-NEXT: Type: None +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: bar +// CHECK-NEXT: Value: 0x11001 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global +// CHECK-NEXT: Type: GNU_IFunc +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: foo +// CHECK-NEXT: Value: 0x11000 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Global +// CHECK-NEXT: Type: GNU_IFunc +// CHECK-NEXT: Other: 0 +// CHECK-NEXT: Section: .text +// CHECK-NEXT: } +// CHECK-NEXT: ] + +// DISASM: Disassembly of section .text: +// DISASM-NEXT: foo: +// DISASM-NEXT: 11000: c3 retq +// DISASM: bar: +// DISASM-NEXT: 11001: c3 retq +// DISASM: _start: +// DISASM-NEXT: 11002: e8 29 00 00 00 callq 41 +// DISASM-NEXT: 11007: e8 34 00 00 00 callq 52 +// DISASM-NEXT: 1100c: ba 58 01 01 00 movl $65880, %edx +// DISASM-NEXT: 11011: ba 88 01 01 00 movl $65928, %edx +// DISASM-NEXT: Disassembly of section .plt: +// DISASM-NEXT: .plt: +// DISASM-NEXT: 11020: ff 35 e2 0f 00 00 pushq 4066(%rip) +// DISASM-NEXT: 11026: ff 25 e4 0f 00 00 jmpq *4068(%rip) +// DISASM-NEXT: 1102c: 0f 1f 40 00 nopl (%rax) +// DISASM-NEXT: 11030: ff 25 e2 0f 00 00 jmpq *4066(%rip) +// DISASM-NEXT: 11036: 68 00 00 00 00 pushq $0 +// DISASM-NEXT: 1103b: e9 e0 ff ff ff jmp -32 +// DISASM-NEXT: 11040: ff 25 da 0f 00 00 jmpq *4058(%rip) +// DISASM-NEXT: 11046: 68 01 00 00 00 pushq $1 +// DISASM-NEXT: 1104b: e9 d0 ff ff ff jmp -48 + +.text +.type foo STT_GNU_IFUNC +.globl foo +.type foo, @function +foo: + ret + +.type bar STT_GNU_IFUNC +.globl bar +.type bar, @function +bar: + ret + +.globl _start +_start: + call foo + call bar + movl $__rela_iplt_start,%edx + movl $__rela_iplt_end,%edx