Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -195,11 +195,11 @@ } else if (Target->relocNeedsGot(Type, Body)) { SymVA = Out::Got->getEntryAddr(Body); if (Body.isTLS()) - Type = Target->getTlsGotReloc(); + Type = Target->getTlsGotReloc(Type); } else if (!Target->relocNeedsCopy(Type, Body) && isa>(Body)) { continue; - } else if (Target->isTlsDynReloc(Type) || + } else if (Target->isTlsDynReloc(Type, Body) || Target->isSizeDynReloc(Type, Body)) { continue; } Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -263,22 +263,21 @@ bool CanBePreempted = canBePreempted(Body, NeedsGot); bool LazyReloc = Body && Target->supportsLazyRelocations() && Target->relocNeedsPlt(Type, *Body); + bool IsDynRelative = Type == Target->getRelativeReloc(); - if (CanBePreempted) { - unsigned GotReloc = - Body->isTLS() ? Target->getTlsGotReloc() : Target->getGotReloc(); - if (NeedsGot) - P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), - LazyReloc ? Target->getPltReloc() : GotReloc, - Config->Mips64EL); - else - P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), - NeedsCopy ? Target->getCopyReloc() - : Target->getDynReloc(Type), - Config->Mips64EL); - } else { - P->setSymbolAndType(0, Target->getRelativeReloc(), Config->Mips64EL); - } + unsigned Sym = CanBePreempted ? Body->getDynamicSymbolTableIndex() : 0; + unsigned Rel; + if (!CanBePreempted || IsDynRelative) + Rel = Target->getRelativeReloc(); + else if (LazyReloc) + Rel = Target->getPltReloc(); + else if (NeedsGot) + Rel = Body->isTLS() ? Target->getTlsGotReloc() : Target->getGotReloc(); + else if (NeedsCopy) + Rel = Target->getCopyReloc(); + else + Rel = Target->getDynReloc(Type); + P->setSymbolAndType(Sym, Rel, Config->Mips64EL); if (NeedsGot) { if (LazyReloc) @@ -299,7 +298,7 @@ uintX_t Addend; if (NeedsCopy) Addend = 0; - else if (CanBePreempted) + else if (CanBePreempted || IsDynRelative) Addend = OrigAddend; else if (Body) Addend = getSymVA(cast>(*Body)) + OrigAddend; Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -27,7 +27,6 @@ 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 @@ 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; @@ -55,6 +59,7 @@ virtual bool isRelRelative(uint32_t Type) const; virtual bool isSizeDynReloc(uint32_t Type, const SymbolBody &S) const; virtual bool relocNeedsCopy(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, Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -76,7 +76,8 @@ 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 @@ uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; bool relocNeedsCopy(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 @@ 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(uint32_t 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 @@ 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 @@ 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; } @@ -339,6 +350,8 @@ return Target->isTlsOptimized(Type, &S) && canBePreempted(&S, true); if (Type == R_386_TLS_GOTIE) return !isTlsOptimized(Type, &S); + if (Type == R_386_TLS_IE) + return !isTlsOptimized(Type, &S); return Type == R_386_GOT32 || relocNeedsPlt(Type, S); } @@ -373,6 +386,7 @@ write32le(Loc, V); break; } + case R_386_TLS_IE: case R_386_TLS_LDO_32: write32le(Loc, SA); break; @@ -392,9 +406,14 @@ 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 +427,8 @@ // 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 +498,47 @@ 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(uint32_t 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 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 (*RegSlot == 0xa1) { + *RegSlot = 0xb8; + } else { + *Inst = IsMov ? 0xc7 : 0x81; + *RegSlot = 0xc0 | ((*RegSlot >> 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) + *RegSlot = 0xc0 | ((*RegSlot >> 3) & 7); + else + *RegSlot = 0x80 | Reg | (Reg << 3); + } relocateOne(Loc, BufEnd, R_386_TLS_LE, P, SA); } @@ -572,7 +612,7 @@ 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; } Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -231,9 +231,16 @@ 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::RelaDyn->addReloc({&C, Rel}); + } + bool NeedsGot = false; bool NeedsPlt = false; if (Body) { Index: test/ELF/Inputs/tls-opt-iele-i686-nopic.s =================================================================== --- test/ELF/Inputs/tls-opt-iele-i686-nopic.s +++ test/ELF/Inputs/tls-opt-iele-i686-nopic.s @@ -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 Index: test/ELF/tls-opt-iele-i686-nopic.s =================================================================== --- test/ELF/tls-opt-iele-i686-nopic.s +++ test/ELF/tls-opt-iele-i686-nopic.s @@ -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