Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -227,6 +227,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: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -277,7 +277,10 @@ : Target->getDynReloc(Type), Config->Mips64EL); } else { - P->setSymbolAndType(0, Target->getRelativeReloc(), Config->Mips64EL); + unsigned RelReloc = (Body && isGnuIfunc(*Body)) + ? Target->getIRelativeReloc() + : Target->getRelativeReloc(); + P->setSymbolAndType(0, RelReloc, Config->Mips64EL); } if (NeedsGot) { @@ -319,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: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -189,6 +189,10 @@ // 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, RelaIpltEnd; + DefinedAbsolute(StringRef N, const Elf_Sym &Sym) : Defined(Base::DefinedAbsoluteKind, N, Sym) {} @@ -206,6 +210,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: ELF/Target.h =================================================================== --- ELF/Target.h +++ 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; } unsigned getTlsGotReloc() const { return TlsGotReloc; } bool isTlsLocalDynamicReloc(unsigned Type) const { return Type == TlsLocalDynamicReloc; @@ -81,6 +82,7 @@ unsigned GotReloc; unsigned PltReloc; unsigned RelativeReloc; + unsigned IRelativeReloc; unsigned TlsGotReloc = 0; unsigned TlsLocalDynamicReloc = 0; unsigned TlsGlobalDynamicReloc = 0; @@ -98,6 +100,8 @@ template typename llvm::object::ELFFile::uintX_t getMipsGpAddr(); +template bool isGnuIfunc(const SymbolBody &S); + extern std::unique_ptr Target; TargetInfo *createTarget(); } Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ 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: @@ -229,6 +240,7 @@ PCRelReloc = R_386_PC32; GotReloc = R_386_GLOB_DAT; PltReloc = R_386_JUMP_SLOT; + IRelativeReloc = R_386_IRELATIVE; TlsGotReloc = R_386_TLS_TPOFF; TlsGlobalDynamicReloc = R_386_TLS_GD; TlsLocalDynamicReloc = R_386_TLS_LDM; @@ -316,6 +328,8 @@ } bool X86TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { + if (isGnuIfunc(S)) + return true; return Type == R_386_PLT32 || (Type == R_386_PC32 && S.isShared()); } @@ -364,6 +378,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; @@ -441,6 +456,8 @@ bool X86_64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { if (relocNeedsCopy(Type, S)) return false; + if (isGnuIfunc(S)) + return true; switch (Type) { default: Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -260,6 +260,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. @@ -684,6 +693,26 @@ } } + // 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. + if (!isOutputDynamic() && Out::RelaPlt && + Out::RelaPlt->hasRelocs()) { + bool IsRela = Symtab.shouldUseRela(); + StringRef IpltS = IsRela ? "__rela_iplt_start" : "__rel_iplt_start"; + if (SymbolBody *B = Symtab.find(IpltS)) { + if (B->isUndefined()) + Symtab.addAbsoluteSym(IpltS, DefinedAbsolute::RelaIpltStart); + } + StringRef IpltE = IsRela ? "__rela_iplt_end" : "__rel_iplt_end"; + if (SymbolBody *B = Symtab.find(IpltE)) { + if (B->isUndefined()) + Symtab.addAbsoluteSym(IpltE, DefinedAbsolute::RelaIpltEnd); + } + } + std::vector *> CommonSymbols; std::vector *> SharedCopySymbols; for (auto &P : Symtab.getSymbols()) { @@ -726,8 +755,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: @@ -741,6 +768,12 @@ } } + // rel[a].plt can contain R_[*]_IRELATIVE relocations when static linking. + if (Out::RelaPlt && Out::RelaPlt->hasRelocs()) { + OutputSections.push_back(Out::RelaPlt); + Out::RelaPlt->Static = !isOutputDynamic(); + } + // We add the .got section to the result for dynamic MIPS target because // its address and properties are mentioned in the .dynamic section. if (!Out::Got->empty() || @@ -954,6 +987,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 RVA = Out::RelaPlt->getVA(); + DefinedAbsolute::RelaIpltStart.st_value = RVA; + DefinedAbsolute::RelaIpltEnd.st_value = + RVA + 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: test/ELF/gnu-ifunc-i386.s =================================================================== --- test/ELF/gnu-ifunc-i386.s +++ 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: test/ELF/gnu-ifunc-nosym-i386.s =================================================================== --- test/ELF/gnu-ifunc-nosym-i386.s +++ 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: test/ELF/gnu-ifunc-nosym.s =================================================================== --- test/ELF/gnu-ifunc-nosym.s +++ 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: test/ELF/gnu-ifunc.s =================================================================== --- test/ELF/gnu-ifunc.s +++ 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