[ELF] - implemented @indntpoff (x86) relocation and its optimization.

@indntpoff is similar to @gotntpoff, but for use in position dependent code. While @gotntpoff resolves to GOT slot address relative to the
start of the GOT in the movl or addl instructions, @indntpoff resolves to the
absolute GOT slot address. ("ELF Handling For Thread-Local Storage", Ulrich Drepper).

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

llvm-svn: 255884
This commit is contained in:
George Rimar 2015-12-17 09:32:21 +00:00
parent 003be4fd58
commit 6f17e09307
7 changed files with 256 additions and 31 deletions

View File

@ -198,11 +198,11 @@ void InputSectionBase<ELFT>::relocate(uint8_t *Buf, uint8_t *BufEnd,
} else if (Target->relocNeedsGot(Type, Body)) {
SymVA = Out<ELFT>::Got->getEntryAddr(Body);
if (Body.isTls())
Type = Target->getTlsGotReloc();
Type = Target->getTlsGotReloc(Type);
} else if (!Target->needsCopyRel(Type, Body) &&
isa<SharedSymbol<ELFT>>(Body)) {
continue;
} else if (Target->isTlsDynReloc(Type) ||
} else if (Target->isTlsDynReloc(Type, Body) ||
Target->isSizeDynReloc(Type, Body)) {
continue;
} else if (Config->EMachine == EM_MIPS) {

View File

@ -263,10 +263,11 @@ template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
bool CanBePreempted = canBePreempted(Body, NeedsGot);
bool LazyReloc = Body && Target->supportsLazyRelocations() &&
Target->relocNeedsPlt(Type, *Body);
bool IsDynRelative = Type == Target->getRelativeReloc();
unsigned Sym = CanBePreempted ? Body->DynamicSymbolTableIndex : 0;
unsigned Reloc;
if (!CanBePreempted)
if (!CanBePreempted || IsDynRelative)
Reloc = Target->getRelativeReloc();
else if (LazyReloc)
Reloc = Target->getPltReloc();
@ -297,7 +298,7 @@ template <class ELFT> void RelocationSection<ELFT>::writeTo(uint8_t *Buf) {
uintX_t Addend;
if (NeedsCopy)
Addend = 0;
else if (CanBePreempted)
else if (CanBePreempted || IsDynRelative)
Addend = OrigAddend;
else if (Body)
Addend = getSymVA<ELFT>(cast<ELFSymbolBody<ELFT>>(*Body)) + OrigAddend;

View File

@ -76,7 +76,8 @@ public:
X86TargetInfo();
void writeGotPltHeaderEntries(uint8_t *Buf) const override;
unsigned getDynReloc(unsigned Type) const override;
bool isTlsDynReloc(unsigned Type) const override;
unsigned getTlsGotReloc(unsigned Type) const override;
bool isTlsDynReloc(unsigned Type, const SymbolBody &S) const override;
void writeGotPltEntry(uint8_t *Buf, uint64_t Plt) const override;
void writePltZeroEntry(uint8_t *Buf, uint64_t GotEntryAddr,
uint64_t PltEntryAddr) const override;
@ -84,6 +85,7 @@ public:
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const override;
bool needsCopyRel(uint32_t Type, const SymbolBody &S) const override;
bool relocNeedsDynRelative(unsigned Type) const override;
bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const override;
bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const override;
void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
@ -101,15 +103,15 @@ private:
uint64_t SA) const;
void relocateTlsGdToLe(uint8_t *Loc, uint8_t *BufEnd, uint64_t P,
uint64_t SA) const;
void relocateTlsIeToLe(uint8_t *Loc, uint8_t *BufEnd, uint64_t P,
uint64_t SA) const;
void relocateTlsIeToLe(unsigned Type, uint8_t *Loc, uint8_t *BufEnd,
uint64_t P, uint64_t SA) const;
};
class X86_64TargetInfo final : public TargetInfo {
public:
X86_64TargetInfo();
unsigned getPltRefReloc(unsigned Type) const override;
bool isTlsDynReloc(unsigned Type) const override;
bool isTlsDynReloc(unsigned Type, const SymbolBody &S) const override;
void writeGotPltHeaderEntries(uint8_t *Buf) const override;
void writeGotPltEntry(uint8_t *Buf, uint64_t Plt) const override;
void writePltZeroEntry(uint8_t *Buf, uint64_t GotEntryAddr,
@ -253,6 +255,7 @@ X86TargetInfo::X86TargetInfo() {
PCRelReloc = R_386_PC32;
GotReloc = R_386_GLOB_DAT;
PltReloc = R_386_JUMP_SLOT;
RelativeReloc = R_386_RELATIVE;
TlsGotReloc = R_386_TLS_TPOFF;
TlsGlobalDynamicReloc = R_386_TLS_GD;
TlsLocalDynamicReloc = R_386_TLS_LDM;
@ -280,10 +283,18 @@ unsigned X86TargetInfo::getDynReloc(unsigned Type) const {
return Type;
}
bool X86TargetInfo::isTlsDynReloc(unsigned Type) const {
unsigned X86TargetInfo::getTlsGotReloc(unsigned Type) const {
if (Type == R_386_TLS_IE)
return Type;
return TlsGotReloc;
}
bool X86TargetInfo::isTlsDynReloc(unsigned Type, const SymbolBody &S) const {
if (Type == R_386_TLS_LE || Type == R_386_TLS_LE_32 ||
Type == R_386_TLS_GOTIE)
return Config->Shared;
if (Type == R_386_TLS_IE)
return canBePreempted(&S, true);
return Type == R_386_TLS_GD;
}
@ -337,7 +348,7 @@ bool X86TargetInfo::needsCopyRel(uint32_t Type, const SymbolBody &S) const {
bool X86TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
if (S.isTls() && Type == R_386_TLS_GD)
return Target->isTlsOptimized(Type, &S) && canBePreempted(&S, true);
if (Type == R_386_TLS_GOTIE)
if (Type == R_386_TLS_GOTIE || Type == R_386_TLS_IE)
return !isTlsOptimized(Type, &S);
return Type == R_386_GOT32 || relocNeedsPlt(Type, S);
}
@ -373,6 +384,7 @@ void X86TargetInfo::relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
write32le(Loc, V);
break;
}
case R_386_TLS_IE:
case R_386_TLS_LDO_32:
write32le(Loc, SA);
break;
@ -392,9 +404,14 @@ bool X86TargetInfo::isTlsOptimized(unsigned Type, const SymbolBody *S) const {
return false;
return Type == R_386_TLS_LDO_32 || Type == R_386_TLS_LDM ||
Type == R_386_TLS_GD ||
(Type == R_386_TLS_IE && !canBePreempted(S, true)) ||
(Type == R_386_TLS_GOTIE && !canBePreempted(S, true));
}
bool X86TargetInfo::relocNeedsDynRelative(unsigned Type) const {
return Config->Shared && Type == R_386_TLS_IE;
}
unsigned X86TargetInfo::relocateTlsOptimize(uint8_t *Loc, uint8_t *BufEnd,
uint32_t Type, uint64_t P,
uint64_t SA,
@ -408,7 +425,8 @@ unsigned X86TargetInfo::relocateTlsOptimize(uint8_t *Loc, uint8_t *BufEnd,
// The next relocation should be against __tls_get_addr, so skip it
return 1;
case R_386_TLS_GOTIE:
relocateTlsIeToLe(Loc, BufEnd, P, SA);
case R_386_TLS_IE:
relocateTlsIeToLe(Type, Loc, BufEnd, P, SA);
return 0;
case R_386_TLS_LDM:
relocateTlsLdToLe(Loc, BufEnd, P, SA);
@ -478,27 +496,47 @@ void X86TargetInfo::relocateTlsLdToLe(uint8_t *Loc, uint8_t *BufEnd, uint64_t P,
memcpy(Loc - 2, Inst, sizeof(Inst));
}
// In some conditions, R_386_TLS_GOTIE relocation can be optimized to
// R_386_TLS_LE so that it does not use GOT.
// This function does that. Read "ELF Handling For Thread-Local Storage,
// 5.1 IA-32 Linker Optimizations" (http://www.akkadia.org/drepper/tls.pdf)
// In some conditions, relocations can be optimized to avoid using GOT.
// This function does that for Initial Exec to Local Exec case.
// Read "ELF Handling For Thread-Local Storage, 5.1
// IA-32 Linker Optimizations" (http://www.akkadia.org/drepper/tls.pdf)
// by Ulrich Drepper for details.
void X86TargetInfo::relocateTlsIeToLe(uint8_t *Loc, uint8_t *BufEnd, uint64_t P,
void X86TargetInfo::relocateTlsIeToLe(unsigned Type, uint8_t *Loc,
uint8_t *BufEnd, uint64_t P,
uint64_t SA) const {
// Ulrich's document section 6.2 says that @gotntpoff can be
// used with MOVL or ADDL instructions.
// "MOVL foo@GOTTPOFF(%RIP), %REG" is transformed to "MOVL $foo, %REG".
// "ADDL foo@GOTNTPOFF(%RIP), %REG" is transformed to "LEAL foo(%REG), %REG"
// Note: gold converts to ADDL instead of LEAL.
// Ulrich's document section 6.2 says that @gotntpoff can
// be used with MOVL or ADDL instructions.
// @indntpoff is similar to @gotntpoff, but for use in
// position dependent code.
uint8_t *Inst = Loc - 2;
uint8_t *RegSlot = Loc - 1;
uint8_t *Op = Loc - 1;
uint8_t Reg = (Loc[-1] >> 3) & 7;
bool IsMov = *Inst == 0x8b;
*Inst = IsMov ? 0xc7 : 0x8d;
if (IsMov)
*RegSlot = 0xc0 | ((*RegSlot >> 3) & 7);
else
*RegSlot = 0x80 | Reg | (Reg << 3);
if (Type == R_386_TLS_IE) {
// For R_386_TLS_IE relocation we perform the next transformations:
// MOVL foo@INDNTPOFF,%EAX is transformed to MOVL $foo,%EAX
// MOVL foo@INDNTPOFF,%REG is transformed to MOVL $foo,%REG
// ADDL foo@INDNTPOFF,%REG is transformed to ADDL $foo,%REG
// First one is special because when EAX is used the sequence is 5 bytes
// long, otherwise it is 6 bytes.
if (*Op == 0xa1) {
*Op = 0xb8;
} else {
*Inst = IsMov ? 0xc7 : 0x81;
*Op = 0xc0 | ((*Op >> 3) & 7);
}
} else {
// R_386_TLS_GOTIE relocation can be optimized to
// R_386_TLS_LE so that it does not use GOT.
// "MOVL foo@GOTTPOFF(%RIP), %REG" is transformed to "MOVL $foo, %REG".
// "ADDL foo@GOTNTPOFF(%RIP), %REG" is transformed to "LEAL foo(%REG), %REG"
// Note: gold converts to ADDL instead of LEAL.
*Inst = IsMov ? 0xc7 : 0x8d;
if (IsMov)
*Op = 0xc0 | ((*Op >> 3) & 7);
else
*Op = 0x80 | Reg | (Reg << 3);
}
relocateOne(Loc, BufEnd, R_386_TLS_LE, P, SA);
}
@ -571,7 +609,7 @@ bool X86_64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const {
return Type == R_X86_64_GOTPCREL || relocNeedsPlt(Type, S);
}
bool X86_64TargetInfo::isTlsDynReloc(unsigned Type) const {
bool X86_64TargetInfo::isTlsDynReloc(unsigned Type, const SymbolBody &S) const {
return Type == R_X86_64_GOTTPOFF || Type == R_X86_64_TLSGD;
}

View File

@ -27,7 +27,6 @@ public:
unsigned getGotReloc() const { return GotReloc; }
unsigned getPltReloc() const { return PltReloc; }
unsigned getRelativeReloc() const { return RelativeReloc; }
unsigned getTlsGotReloc() const { return TlsGotReloc; }
bool isTlsLocalDynamicReloc(unsigned Type) const {
return Type == TlsLocalDynamicReloc;
}
@ -42,8 +41,13 @@ public:
unsigned getGotHeaderEntriesNum() const { return GotHeaderEntriesNum; }
unsigned getGotPltHeaderEntriesNum() const { return GotPltHeaderEntriesNum; }
virtual unsigned getDynReloc(unsigned Type) const { return Type; }
virtual bool isTlsDynReloc(unsigned Type) const { return false; }
virtual bool isTlsDynReloc(unsigned Type, const SymbolBody &S) const {
return false;
}
virtual unsigned getPltRefReloc(unsigned Type) const;
virtual unsigned getTlsGotReloc(unsigned Type = -1) const {
return TlsGotReloc;
}
virtual void writeGotHeaderEntries(uint8_t *Buf) const;
virtual void writeGotPltHeaderEntries(uint8_t *Buf) const;
virtual void writeGotPltEntry(uint8_t *Buf, uint64_t Plt) const = 0;
@ -54,6 +58,7 @@ public:
int32_t Index, unsigned RelOff) const = 0;
virtual bool isRelRelative(uint32_t Type) const;
virtual bool isSizeDynReloc(uint32_t Type, const SymbolBody &S) const;
virtual bool relocNeedsDynRelative(unsigned Type) const { return false; }
virtual bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const = 0;
virtual bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const = 0;
virtual void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,

View File

@ -236,9 +236,16 @@ void Writer<ELFT>::scanRelocs(
continue;
}
if (Body && Body->isTls() && !Target->isTlsDynReloc(Type))
if (Body && Body->isTls() && !Target->isTlsDynReloc(Type, *Body))
continue;
if (Target->relocNeedsDynRelative(Type)) {
RelType *Rel = new (Alloc) RelType;
Rel->setSymbolAndType(0, Target->getRelativeReloc(), Config->Mips64EL);
Rel->r_offset = RI.r_offset;
Out<ELFT>::RelaDyn->addReloc({&C, Rel});
}
bool NeedsGot = false;
bool NeedsPlt = false;
if (Body) {

View File

@ -0,0 +1,15 @@
.type tlsshared0,@object
.section .tbss,"awT",@nobits
.globl tlsshared0
.align 4
tlsshared0:
.long 0
.size tlsshared0, 4
.type tlsshared1,@object
.section .tbss,"awT",@nobits
.globl tlsshared1
.align 4
tlsshared1:
.long 0
.size tlsshared1, 4

View File

@ -0,0 +1,159 @@
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %s -o %t.o
// RUN: llvm-mc -filetype=obj -triple=i686-pc-linux %p/Inputs/tls-opt-iele-i686-nopic.s -o %tso.o
// RUN: ld.lld -shared %tso.o -o %tso
// RUN: ld.lld %t.o %tso -o %t1
// RUN: llvm-readobj -s -r %t1 | FileCheck --check-prefix=GOTREL %s
// RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASM %s
// RUN: ld.lld -shared %t.o %tso -o %t1
// RUN: llvm-readobj -s -r %t1 | FileCheck --check-prefix=GOTRELSHARED %s
// RUN: llvm-objdump -d %t1 | FileCheck --check-prefix=DISASMSHARED %s
// GOTREL: Section {
// GOTREL: Index:
// GOTREL: Name: .got
// GOTREL-NEXT: Type: SHT_PROGBITS
// GOTREL-NEXT: Flags [
// GOTREL-NEXT: SHF_ALLOC
// GOTREL-NEXT: SHF_WRITE
// GOTREL-NEXT: ]
// GOTREL-NEXT: Address: 0x12050
// GOTREL-NEXT: Offset: 0x2050
// GOTREL-NEXT: Size: 8
// GOTREL-NEXT: Link: 0
// GOTREL-NEXT: Info: 0
// GOTREL-NEXT: AddressAlignment: 4
// GOTREL-NEXT: EntrySize: 0
// GOTREL-NEXT: }
// GOTREL: Relocations [
// GOTREL-NEXT: Section ({{.*}}) .rel.dyn {
// GOTREL-NEXT: 0x12050 R_386_TLS_TPOFF tlsshared0 0x0
// GOTREL-NEXT: 0x12054 R_386_TLS_TPOFF tlsshared1 0x0
// GOTREL-NEXT: }
// GOTREL-NEXT: ]
// DISASM: Disassembly of section .text:
// DISASM-NEXT: _start:
// 4294967288 = 0xFFFFFFF8
// 4294967292 = 0xFFFFFFFC
// 73808 = (.got)[0] = 0x12050
// 73812 = (.got)[1] = 0x12054
// DISASM-NEXT: 11000: c7 c1 f8 ff ff ff movl $4294967288, %ecx
// DISASM-NEXT: 11006: 65 8b 01 movl %gs:(%ecx), %eax
// DISASM-NEXT: 11009: b8 f8 ff ff ff movl $4294967288, %eax
// DISASM-NEXT: 1100e: 65 8b 00 movl %gs:(%eax), %eax
// DISASM-NEXT: 11011: 81 c1 f8 ff ff ff addl $4294967288, %ecx
// DISASM-NEXT: 11017: 65 8b 01 movl %gs:(%ecx), %eax
// DISASM-NEXT: 1101a: c7 c1 fc ff ff ff movl $4294967292, %ecx
// DISASM-NEXT: 11020: 65 8b 01 movl %gs:(%ecx), %eax
// DISASM-NEXT: 11023: b8 fc ff ff ff movl $4294967292, %eax
// DISASM-NEXT: 11028: 65 8b 00 movl %gs:(%eax), %eax
// DISASM-NEXT: 1102b: 81 c1 fc ff ff ff addl $4294967292, %ecx
// DISASM-NEXT: 11031: 65 8b 01 movl %gs:(%ecx), %eax
// DISASM-NEXT: 11034: 8b 0d 50 20 01 00 movl 73808, %ecx
// DISASM-NEXT: 1103a: 65 8b 01 movl %gs:(%ecx), %eax
// DISASM-NEXT: 1103d: 03 0d 54 20 01 00 addl 73812, %ecx
// DISASM-NEXT: 11043: 65 8b 01 movl %gs:(%ecx), %eax
// GOTRELSHARED: Section {
// GOTRELSHARED: Index: 8
// GOTRELSHARED: Name: .got
// GOTRELSHARED-NEXT: Type: SHT_PROGBITS
// GOTRELSHARED-NEXT: Flags [
// GOTRELSHARED-NEXT: SHF_ALLOC
// GOTRELSHARED-NEXT: SHF_WRITE
// GOTRELSHARED-NEXT: ]
// GOTRELSHARED-NEXT: Address: 0x2050
// GOTRELSHARED-NEXT: Offset: 0x2050
// GOTRELSHARED-NEXT: Size: 16
// GOTRELSHARED-NEXT: Link: 0
// GOTRELSHARED-NEXT: Info: 0
// GOTRELSHARED-NEXT: AddressAlignment: 4
// GOTRELSHARED-NEXT: EntrySize: 0
// GOTRELSHARED-NEXT: }
// GOTRELSHARED: Relocations [
// GOTRELSHARED-NEXT: Section ({{.*}}) .rel.dyn {
// GOTRELSHARED-NEXT: 0x1002 R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x2050 R_386_TLS_TPOFF tlslocal0 0x0
// GOTRELSHARED-NEXT: 0x100A R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x1013 R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x101C R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x2054 R_386_TLS_TPOFF tlslocal1 0x0
// GOTRELSHARED-NEXT: 0x1024 R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x102D R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x1036 R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x2058 R_386_TLS_TPOFF tlsshared0 0x0
// GOTRELSHARED-NEXT: 0x103F R_386_RELATIVE - 0x0
// GOTRELSHARED-NEXT: 0x205C R_386_TLS_TPOFF tlsshared1 0x0
// GOTRELSHARED-NEXT: }
// GOTRELSHARED-NEXT: ]
// DISASMSHARED: Disassembly of section .text:
// DISASMSHARED-NEXT: _start:
// (.got)[0] = 0x2050 = 8272
// (.got)[1] = 0x2054 = 8276
// (.got)[2] = 0x2058 = 8280
// (.got)[3] = 0x205C = 8284
// DISASMSHARED-NEXT: 1000: 8b 0d 50 20 00 00 movl 8272, %ecx
// DISASMSHARED-NEXT: 1006: 65 8b 01 movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 1009: a1 50 20 00 00 movl 8272, %eax
// DISASMSHARED-NEXT: 100e: 65 8b 00 movl %gs:(%eax), %eax
// DISASMSHARED-NEXT: 1011: 03 0d 50 20 00 00 addl 8272, %ecx
// DISASMSHARED-NEXT: 1017: 65 8b 01 movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 101a: 8b 0d 54 20 00 00 movl 8276, %ecx
// DISASMSHARED-NEXT: 1020: 65 8b 01 movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 1023: a1 54 20 00 00 movl 8276, %eax
// DISASMSHARED-NEXT: 1028: 65 8b 00 movl %gs:(%eax), %eax
// DISASMSHARED-NEXT: 102b: 03 0d 54 20 00 00 addl 8276, %ecx
// DISASMSHARED-NEXT: 1031: 65 8b 01 movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 1034: 8b 0d 58 20 00 00 movl 8280, %ecx
// DISASMSHARED-NEXT: 103a: 65 8b 01 movl %gs:(%ecx), %eax
// DISASMSHARED-NEXT: 103d: 03 0d 5c 20 00 00 addl 8284, %ecx
// DISASMSHARED-NEXT: 1043: 65 8b 01 movl %gs:(%ecx), %eax
.type tlslocal0,@object
.section .tbss,"awT",@nobits
.globl tlslocal0
.align 4
tlslocal0:
.long 0
.size tlslocal0, 4
.type tlslocal1,@object
.section .tbss,"awT",@nobits
.globl tlslocal1
.align 4
tlslocal1:
.long 0
.size tlslocal1, 4
.section .text
.globl ___tls_get_addr
.type ___tls_get_addr,@function
___tls_get_addr:
.section .text
.globl _start
_start:
movl tlslocal0@indntpoff,%ecx
movl %gs:(%ecx),%eax
movl tlslocal0@indntpoff,%eax
movl %gs:(%eax),%eax
addl tlslocal0@indntpoff,%ecx
movl %gs:(%ecx),%eax
movl tlslocal1@indntpoff,%ecx
movl %gs:(%ecx),%eax
movl tlslocal1@indntpoff,%eax
movl %gs:(%eax),%eax
addl tlslocal1@indntpoff,%ecx
movl %gs:(%ecx),%eax
movl tlsshared0@indntpoff,%ecx
movl %gs:(%ecx),%eax
addl tlsshared1@indntpoff,%ecx
movl %gs:(%ecx),%eax