[ELF] - Implemented R_*_IRELATIVE relocations for x86, x64 targets.

This relocation is similar to R_*_RELATIVE except that the value used in this relocation is the program address returned by the function, which takes no arguments, at the address of
the result of the corresponding R_*_RELATIVE relocation as specified in the processor-specific ABI. The purpose of this relocation to avoid name lookup for locally defined STT_GNU_IFUNC symbols at load-time.

More info can be found in ifunc.txt from https://sites.google.com/site/x32abi/documents.

Differential revision: http://reviews.llvm.org/D15235

llvm-svn: 256144
This commit is contained in:
George Rimar 2015-12-21 10:12:06 +00:00
parent bfb7bf7429
commit a07ff66112
10 changed files with 401 additions and 5 deletions

View File

@ -269,7 +269,9 @@ template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
unsigned Sym = CanBePreempted ? Body->DynamicSymbolTableIndex : 0;
unsigned Reloc;
if (!CanBePreempted || IsDynRelative)
if (!CanBePreempted && Body && isGnuIFunc<ELFT>(*Body))
Reloc = Target->getIRelativeReloc();
else if (!CanBePreempted || IsDynRelative)
Reloc = Target->getRelativeReloc();
else if (LazyReloc)
Reloc = Target->getPltReloc();
@ -320,7 +322,8 @@ template <class ELFT> unsigned RelocationSection<ELFT>::getRelocOffset() {
}
template <class ELFT> void RelocationSection<ELFT>::finalize() {
this->Header.sh_link = Out<ELFT>::DynSymTab->SectionIndex;
this->Header.sh_link = Static ? Out<ELFT>::SymTab->SectionIndex
: Out<ELFT>::DynSymTab->SectionIndex;
this->Header.sh_size = Relocs.size() * this->Header.sh_entsize;
}

View File

@ -232,6 +232,8 @@ public:
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);

View File

@ -183,6 +183,11 @@ public:
// 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<ELFT>(Base::DefinedAbsoluteKind, N, Sym) {}
@ -200,6 +205,12 @@ typename DefinedAbsolute<ELFT>::Elf_Sym DefinedAbsolute<ELFT>::End;
template <class ELFT>
typename DefinedAbsolute<ELFT>::Elf_Sym DefinedAbsolute<ELFT>::MipsGp;
template <class ELFT>
typename DefinedAbsolute<ELFT>::Elf_Sym DefinedAbsolute<ELFT>::RelaIpltStart;
template <class ELFT>
typename DefinedAbsolute<ELFT>::Elf_Sym DefinedAbsolute<ELFT>::RelaIpltEnd;
template <class ELFT> class DefinedCommon : public Defined<ELFT> {
typedef ELFSymbolBody<ELFT> Base;
typedef typename Base::Elf_Sym Elf_Sym;

View File

@ -70,6 +70,17 @@ template <unsigned N> static void checkAlignment(uint64_t V, uint32_t Type) {
error("Improper alignment for relocation " + S);
}
template <class ELFT> bool isGnuIFunc(const SymbolBody &S) {
if (auto *SS = dyn_cast<Defined<ELFT>>(&S))
return SS->Sym.getType() == STT_GNU_IFUNC;
return false;
}
template bool isGnuIFunc<ELF32LE>(const SymbolBody &S);
template bool isGnuIFunc<ELF32BE>(const SymbolBody &S);
template bool isGnuIFunc<ELF64LE>(const SymbolBody &S);
template bool isGnuIFunc<ELF64BE>(const SymbolBody &S);
namespace {
class X86TargetInfo final : public TargetInfo {
public:
@ -258,6 +269,7 @@ X86TargetInfo::X86TargetInfo() {
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::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
}
bool X86TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
return (Type == R_386_PLT32 && canBePreempted(&S, true)) ||
return isGnuIFunc<ELF32LE>(S) ||
(Type == R_386_PLT32 && canBePreempted(&S, true)) ||
(Type == R_386_PC32 && S.isShared());
}
@ -557,6 +570,7 @@ X86_64TargetInfo::X86_64TargetInfo() {
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 @@ unsigned X86_64TargetInfo::getPltRefReloc(unsigned Type) const {
bool X86_64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const {
if (needsCopyRel(Type, S))
return false;
if (isGnuIFunc<ELF64LE>(S))
return true;
switch (Type) {
default:

View File

@ -27,6 +27,7 @@ public:
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 @@ protected:
unsigned GotReloc;
unsigned PltReloc;
unsigned RelativeReloc;
unsigned IRelativeReloc;
unsigned TlsGotReloc = 0;
unsigned TlsLocalDynamicReloc = 0;
unsigned TlsGlobalDynamicReloc = 0;
@ -105,6 +107,8 @@ uint64_t getPPC64TocBase();
template <class ELFT>
typename llvm::object::ELFFile<ELFT>::uintX_t getMipsGpAddr();
template <class ELFT> bool isGnuIFunc(const SymbolBody &S);
extern std::unique_ptr<TargetInfo> Target;
TargetInfo *createTarget();
}

View File

@ -277,6 +277,15 @@ void Writer<ELFT>::scanRelocs(
}
}
// 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<ELFT>(*Body)) {
Body->setUsedInDynamicReloc();
Out<ELFT>::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 @@ static bool compareSections(OutputSectionBase<ELFT> *A,
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 <class ELFT>
static void addIRelocMarkers(SymbolTable<ELFT> &Symtab, bool IsDynamic) {
if (IsDynamic || !Out<ELFT>::RelaPlt || !Out<ELFT>::RelaPlt->hasRelocs())
return;
bool IsRela = shouldUseRela<ELFT>();
auto AddMarker = [&](StringRef Name, typename Writer<ELFT>::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<ELFT>::RelaIpltStart);
AddMarker(IsRela ? "__rela_iplt_end" : "__rel_iplt_end",
DefinedAbsolute<ELFT>::RelaIpltEnd);
}
// Create output section objects and add them to OutputSections.
template <class ELFT> void Writer<ELFT>::createSections() {
// .interp needs to be on the first page in the output file.
@ -720,6 +750,8 @@ template <class ELFT> void Writer<ELFT>::createSections() {
}
}
addIRelocMarkers<ELFT>(Symtab, isOutputDynamic());
std::vector<DefinedCommon<ELFT> *> CommonSymbols;
std::vector<SharedSymbol<ELFT> *> SharedCopySymbols;
for (auto &P : Symtab.getSymbols()) {
@ -762,8 +794,6 @@ template <class ELFT> void Writer<ELFT>::createSections() {
OutputSections.push_back(Out<ELFT>::DynStrTab);
if (Out<ELFT>::RelaDyn->hasRelocs())
OutputSections.push_back(Out<ELFT>::RelaDyn);
if (Out<ELFT>::RelaPlt && Out<ELFT>::RelaPlt->hasRelocs())
OutputSections.push_back(Out<ELFT>::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 @@ template <class ELFT> void Writer<ELFT>::createSections() {
}
}
// 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<ELFT>::RelaPlt && Out<ELFT>::RelaPlt->hasRelocs()) {
OutputSections.push_back(Out<ELFT>::RelaPlt);
Out<ELFT>::RelaPlt->Static = !isOutputDynamic();
}
bool needsGot = !Out<ELFT>::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 @@ template <class ELFT> void Writer<ELFT>::assignAddresses() {
// point to the end of the data segment.
DefinedAbsolute<ELFT>::End.st_value = VA;
// Update __rel_iplt_start/__rel_iplt_end to wrap the
// rela.plt section.
if (Out<ELFT>::RelaPlt) {
uintX_t Start = Out<ELFT>::RelaPlt->getVA();
DefinedAbsolute<ELFT>::RelaIpltStart.st_value = Start;
DefinedAbsolute<ELFT>::RelaIpltEnd.st_value =
Start + Out<ELFT>::RelaPlt->getSize();
}
// Update MIPS _gp absolute symbol so that it points to the static data.
if (Config->EMachine == EM_MIPS)
DefinedAbsolute<ELFT>::MipsGp.st_value = getMipsGpAddr<ELFT>();

View File

@ -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

View File

@ -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

View File

@ -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

126
lld/test/ELF/gnu-ifunc.s Normal file
View File

@ -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