Use the plt entry as the address of some symbols.

This is the function equivalent of a copy relocation.

Since functions are expected to change sizes, we cannot use copy
relocations. In situations where one would be needed, what is done
instead is:
* Create a plt entry
* Output an undefined symbol whose addr is the plt entry.

The dynamic linker makes sure any shared library uses the plt entry as
the function address.

llvm-svn: 260224
This commit is contained in:
Rafael Espindola 2016-02-09 15:11:01 +00:00
parent ad3293744a
commit a0a65f973a
9 changed files with 83 additions and 49 deletions

View File

@ -1328,7 +1328,7 @@ void SymbolTableSection<ELFT>::writeGlobalSymbols(uint8_t *Buf) {
OutSec = Out<ELFT>::Bss;
break;
case SymbolBody::SharedKind: {
if (cast<SharedSymbol<ELFT>>(Body)->NeedsCopy)
if (cast<SharedSymbol<ELFT>>(Body)->needsCopy())
OutSec = Out<ELFT>::Bss;
break;
}

View File

@ -56,9 +56,12 @@ typename ELFFile<ELFT>::uintX_t SymbolBody::getVA() const {
return Out<ELFT>::Bss->getVA() + cast<DefinedCommon>(this)->OffsetInBss;
case SharedKind: {
auto *SS = cast<SharedSymbol<ELFT>>(this);
if (SS->NeedsCopy)
if (!SS->NeedsCopyOrPltAddr)
return 0;
if (SS->IsFunc)
return getPltVA<ELFT>();
else
return Out<ELFT>::Bss->getVA() + SS->OffsetInBss;
return 0;
}
case UndefinedElfKind:
case UndefinedKind:

View File

@ -130,7 +130,8 @@ protected:
SymbolBody(Kind K, StringRef Name, bool IsWeak, uint8_t Visibility,
bool IsTls, bool IsFunc)
: SymbolKind(K), IsWeak(IsWeak), Visibility(Visibility),
MustBeInDynSym(false), IsTls(IsTls), IsFunc(IsFunc), Name(Name) {
MustBeInDynSym(false), NeedsCopyOrPltAddr(false), IsTls(IsTls),
IsFunc(IsFunc), Name(Name) {
IsUsedInRegularObj = K != SharedKind && K != LazyKind;
}
@ -148,9 +149,14 @@ public:
// If true, the symbol is added to .dynsym symbol table.
unsigned MustBeInDynSym : 1;
// True if the linker has to generate a copy relocation for this shared
// symbol or if the symbol should point to its plt entry.
unsigned NeedsCopyOrPltAddr : 1;
protected:
unsigned IsTls : 1;
unsigned IsFunc : 1;
StringRef Name;
Symbol *Backref = nullptr;
};
@ -280,10 +286,10 @@ public:
SharedFile<ELFT> *File;
// True if the linker has to generate a copy relocation for this shared
// symbol. OffsetInBss is significant only when NeedsCopy is true.
bool NeedsCopy = false;
// OffsetInBss is significant only when needsCopy() is true.
uintX_t OffsetInBss = 0;
bool needsCopy() const { return this->NeedsCopyOrPltAddr && !this->IsFunc; }
};
// This class represents a symbol defined in an archive file. It is

View File

@ -90,8 +90,8 @@ public:
int32_t Index, unsigned RelOff) const override;
bool needsCopyRel(uint32_t Type, const SymbolBody &S) const override;
bool needsDynRelative(unsigned Type) const override;
bool needsGot(uint32_t Type, const SymbolBody &S) const override;
bool needsPlt(uint32_t Type, const SymbolBody &S) const override;
bool needsGot(uint32_t Type, SymbolBody &S) const override;
bool needsPlt(uint32_t Type, SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
uint64_t SA, uint64_t ZA = 0,
uint8_t *PairedLoc = nullptr) const override;
@ -121,8 +121,8 @@ public:
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
bool needsCopyRel(uint32_t Type, const SymbolBody &S) const override;
bool needsGot(uint32_t Type, const SymbolBody &S) const override;
bool needsPlt(uint32_t Type, const SymbolBody &S) const override;
bool needsGot(uint32_t Type, SymbolBody &S) const override;
bool needsPlt(uint32_t Type, SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
uint64_t SA, uint64_t ZA = 0,
uint8_t *PairedLoc = nullptr) const override;
@ -157,8 +157,8 @@ public:
PPC64TargetInfo();
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
bool needsGot(uint32_t Type, const SymbolBody &S) const override;
bool needsPlt(uint32_t Type, const SymbolBody &S) const override;
bool needsGot(uint32_t Type, SymbolBody &S) const override;
bool needsPlt(uint32_t Type, SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
uint64_t SA, uint64_t ZA = 0,
uint8_t *PairedLoc = nullptr) const override;
@ -176,8 +176,8 @@ public:
unsigned getTlsGotRel(unsigned Type = -1) const override;
bool isTlsDynRel(unsigned Type, const SymbolBody &S) const override;
bool needsCopyRel(uint32_t Type, const SymbolBody &S) const override;
bool needsGot(uint32_t Type, const SymbolBody &S) const override;
bool needsPlt(uint32_t Type, const SymbolBody &S) const override;
bool needsGot(uint32_t Type, SymbolBody &S) const override;
bool needsPlt(uint32_t Type, SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
uint64_t SA, uint64_t ZA = 0,
uint8_t *PairedLoc = nullptr) const override;
@ -197,8 +197,8 @@ public:
unsigned getDynRel(unsigned Type) const override;
void writeGotHeader(uint8_t *Buf) const override;
bool needsCopyRel(uint32_t Type, const SymbolBody &S) const override;
bool needsGot(uint32_t Type, const SymbolBody &S) const override;
bool needsPlt(uint32_t Type, const SymbolBody &S) const override;
bool needsGot(uint32_t Type, SymbolBody &S) const override;
bool needsPlt(uint32_t Type, SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
uint64_t SA, uint64_t ZA = 0,
uint8_t *PairedLoc = nullptr) const override;
@ -263,13 +263,9 @@ bool TargetInfo::isHintRel(uint32_t Type) const { return false; }
bool TargetInfo::isRelRelative(uint32_t Type) const { return true; }
bool TargetInfo::isSizeRel(uint32_t Type) const { return false; }
bool TargetInfo::needsGot(uint32_t Type, const SymbolBody &S) const {
return false;
}
bool TargetInfo::needsGot(uint32_t Type, SymbolBody &S) const { return false; }
bool TargetInfo::needsPlt(uint32_t Type, const SymbolBody &S) const {
return false;
}
bool TargetInfo::needsPlt(uint32_t Type, SymbolBody &S) const { return false; }
unsigned TargetInfo::relaxTls(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA,
@ -376,7 +372,7 @@ bool X86TargetInfo::needsCopyRel(uint32_t Type, const SymbolBody &S) const {
return false;
}
bool X86TargetInfo::needsGot(uint32_t Type, const SymbolBody &S) const {
bool X86TargetInfo::needsGot(uint32_t Type, SymbolBody &S) const {
if (S.isTls() && Type == R_386_TLS_GD)
return Target->canRelaxTls(Type, &S) && canBePreempted(&S, true);
if (Type == R_386_TLS_GOTIE || Type == R_386_TLS_IE)
@ -384,7 +380,7 @@ bool X86TargetInfo::needsGot(uint32_t Type, const SymbolBody &S) const {
return Type == R_386_GOT32 || needsPlt(Type, S);
}
bool X86TargetInfo::needsPlt(uint32_t Type, const SymbolBody &S) const {
bool X86TargetInfo::needsPlt(uint32_t Type, SymbolBody &S) const {
return isGnuIFunc<ELF32LE>(S) ||
(Type == R_386_PLT32 && canBePreempted(&S, true)) ||
(Type == R_386_PC32 && S.isShared());
@ -646,7 +642,7 @@ bool X86_64TargetInfo::needsCopyRel(uint32_t Type, const SymbolBody &S) const {
return false;
}
bool X86_64TargetInfo::needsGot(uint32_t Type, const SymbolBody &S) const {
bool X86_64TargetInfo::needsGot(uint32_t Type, SymbolBody &S) const {
if (Type == R_X86_64_TLSGD)
return Target->canRelaxTls(Type, &S) && canBePreempted(&S, true);
if (Type == R_X86_64_GOTTPOFF)
@ -658,7 +654,7 @@ bool X86_64TargetInfo::isTlsDynRel(unsigned Type, const SymbolBody &S) const {
return Type == R_X86_64_GOTTPOFF || Type == R_X86_64_TLSGD;
}
bool X86_64TargetInfo::needsPlt(uint32_t Type, const SymbolBody &S) const {
bool X86_64TargetInfo::needsPlt(uint32_t Type, SymbolBody &S) const {
if (needsCopyRel(Type, S))
return false;
if (isGnuIFunc<ELF64LE>(S))
@ -684,17 +680,19 @@ bool X86_64TargetInfo::needsPlt(uint32_t Type, const SymbolBody &S) const {
// For the static linking part, we just return true and everything else
// will use the the PLT entry as the address.
//
// The remaining (unimplemented) problem is making sure pointer equality
// still works. We need the help of the dynamic linker for that. We
// let it know that we have a direct reference to a so symbol by creating
// an undefined symbol with a non zero st_value. Seeing that, the
// dynamic linker resolves the symbol to the value of the symbol we created.
// This is true even for got entries, so pointer equality is maintained.
// To avoid an infinite loop, the only entry that points to the
// real function is a dedicated got entry used by the plt. That is
// identified by special relocation types (R_X86_64_JUMP_SLOT,
// The remaining problem is making sure pointer equality still works. We
// need the help of the dynamic linker for that. We let it know that we have
// a direct reference to a so symbol by creating an undefined symbol with a
// non zero st_value. Seeing that, the dynamic linker resolves the symbol to
// the value of the symbol we created. This is true even for got entries, so
// pointer equality is maintained. To avoid an infinite loop, the only entry
// that points to the real function is a dedicated got entry used by the
// plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT,
// R_386_JMP_SLOT, etc).
return S.isShared();
if (!S.isShared())
return false;
S.NeedsCopyOrPltAddr = true;
return true;
case R_X86_64_PLT32:
return canBePreempted(&S, true);
}
@ -989,7 +987,7 @@ void PPC64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
write32be(Buf + 28, 0x4e800420); // bctr
}
bool PPC64TargetInfo::needsGot(uint32_t Type, const SymbolBody &S) const {
bool PPC64TargetInfo::needsGot(uint32_t Type, SymbolBody &S) const {
if (needsPlt(Type, S))
return true;
@ -1005,7 +1003,7 @@ bool PPC64TargetInfo::needsGot(uint32_t Type, const SymbolBody &S) const {
}
}
bool PPC64TargetInfo::needsPlt(uint32_t Type, const SymbolBody &S) const {
bool PPC64TargetInfo::needsPlt(uint32_t Type, SymbolBody &S) const {
// These are function calls that need to be redirected through a PLT stub.
return Type == R_PPC64_REL24 && canBePreempted(&S, false);
}
@ -1240,7 +1238,7 @@ bool AArch64TargetInfo::needsCopyRel(uint32_t Type, const SymbolBody &S) const {
}
}
bool AArch64TargetInfo::needsGot(uint32_t Type, const SymbolBody &S) const {
bool AArch64TargetInfo::needsGot(uint32_t Type, SymbolBody &S) const {
switch (Type) {
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
@ -1252,7 +1250,7 @@ bool AArch64TargetInfo::needsGot(uint32_t Type, const SymbolBody &S) const {
}
}
bool AArch64TargetInfo::needsPlt(uint32_t Type, const SymbolBody &S) const {
bool AArch64TargetInfo::needsPlt(uint32_t Type, SymbolBody &S) const {
if (isGnuIFunc<ELF64LE>(S))
return true;
switch (Type) {
@ -1439,12 +1437,12 @@ bool MipsTargetInfo<ELFT>::needsCopyRel(uint32_t Type,
}
template <class ELFT>
bool MipsTargetInfo<ELFT>::needsGot(uint32_t Type, const SymbolBody &S) const {
bool MipsTargetInfo<ELFT>::needsGot(uint32_t Type, SymbolBody &S) const {
return Type == R_MIPS_GOT16 || Type == R_MIPS_CALL16;
}
template <class ELFT>
bool MipsTargetInfo<ELFT>::needsPlt(uint32_t Type, const SymbolBody &S) const {
bool MipsTargetInfo<ELFT>::needsPlt(uint32_t Type, SymbolBody &S) const {
return false;
}

View File

@ -54,8 +54,8 @@ public:
virtual bool isSizeRel(uint32_t Type) const;
virtual bool needsDynRelative(unsigned Type) const { return false; }
virtual bool needsGot(uint32_t Type, const SymbolBody &S) const;
virtual bool needsPlt(uint32_t Type, const SymbolBody &S) const;
virtual bool needsGot(uint32_t Type, SymbolBody &S) const;
virtual bool needsPlt(uint32_t Type, SymbolBody &S) const;
virtual void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA, uint64_t ZA = 0,
uint8_t *PairedLoc = nullptr) const = 0;

View File

@ -302,10 +302,10 @@ void Writer<ELFT>::scanRelocs(
// If a symbol in a DSO is referenced directly instead of through GOT,
// we need to create a copy relocation for the symbol.
if (auto *B = dyn_cast_or_null<SharedSymbol<ELFT>>(Body)) {
if (B->NeedsCopy)
if (B->needsCopy())
continue;
if (Target->needsCopyRel(Type, *B)) {
B->NeedsCopy = true;
B->NeedsCopyOrPltAddr = true;
Out<ELFT>::RelaDyn->addReloc(
{Target->CopyRel, DynamicReloc<ELFT>::Off_Bss, B});
continue;
@ -968,7 +968,7 @@ template <class ELFT> bool Writer<ELFT>::createSections() {
if (auto *C = dyn_cast<DefinedCommon>(Body))
CommonSymbols.push_back(C);
if (auto *SC = dyn_cast<SharedSymbol<ELFT>>(Body))
if (SC->NeedsCopy)
if (SC->needsCopy())
CopyRelSymbols.push_back(SC);
if (!includeInSymtab<ELFT>(*Body))

View File

@ -0,0 +1,4 @@
.globl set_data
.type set_data,@function
set_data:
retq

View File

@ -43,4 +43,4 @@ nop
.text
.globl _start
_start:
callq do
callq do@plt

View File

@ -0,0 +1,23 @@
// REQUIRES: x86
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/undef-with-plt-addr.s -o %t2.o
// RUN: ld.lld %t2.o -o %t2.so -shared
// RUN: ld.lld %t.o %t2.so -o %t3
// RUN: llvm-readobj -t -s %t3 | FileCheck %s
.globl _start
_start:
movabsq $set_data, %rax
// Test that set_data has an address in the .plt
// CHECK: Name: .plt
// CHECK-NEXT: Type: SHT_PROGBITS
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_EXECINSTR
// CHECK-NEXT: ]
// CHECK-NEXT: Address: 0x11010
// CHECK: Name: set_data
// CHECK-NEXT: Value: 0x11020