Skip to content

Commit e39c138

Browse files
committed
[ELF] Implement TLSDESC for x86-32
`-z rela` is also supported. Tested with: ``` cat > ./a.c <<eof #include <assert.h> int foo(); int bar(); int main() { assert(foo() == 2); assert(foo() == 4); assert(bar() == 2); assert(bar() == 4); } eof cat > ./b.c <<eof #include <stdio.h> __thread int tls0; extern __thread int tls1; int foo() { return ++tls0 + ++tls1; } static __thread int tls2, tls3; int bar() { return ++tls2 + ++tls3; } eof echo '__thread int tls1;' > ./c.c sed 's/ /\t/' > ./Makefile <<'eof' .MAKE.MODE = meta curDirOk=true CC := gcc -m32 -g -fpic -mtls-dialect=gnu2 LDFLAGS := -m32 -Wl,-rpath=. all: a0 a1 a2 run: all ./a0 && ./a1 && ./a2 c.so: c.o; ${LINK.c} -shared $> -o $@ bc.so: b.o c.o; ${LINK.c} -shared $> -o $@ b.so: b.o c.so; ${LINK.c} -shared $> -o $@ a0: a.o b.o c.o; ${LINK.c} $> -o $@ a1: a.o b.so; ${LINK.c} $> -o $@ a2: a.o bc.so; ${LINK.c} $> -o $@ eof ``` and glibc `elf/tst-gnu2-tls1`. `/usr/local/bin/ld` points to the freshly built `lld`. `bmake run && bmake CFLAGS=-O1 run` => ok. Differential Revision: https://reviews.llvm.org/D112582
1 parent 12b2cc2 commit e39c138

File tree

9 files changed

+273
-35
lines changed

9 files changed

+273
-35
lines changed

lld/ELF/Arch/X86.cpp

Lines changed: 81 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ X86::X86() {
5656
iRelativeRel = R_386_IRELATIVE;
5757
relativeRel = R_386_RELATIVE;
5858
symbolicRel = R_386_32;
59+
tlsDescRel = R_386_TLS_DESC;
5960
tlsGotRel = R_386_TLS_TPOFF;
6061
tlsModuleIndexRel = R_386_TLS_DTPMOD32;
6162
tlsOffsetRel = R_386_TLS_DTPOFF32;
@@ -71,7 +72,8 @@ X86::X86() {
7172
}
7273

7374
int X86::getTlsGdRelaxSkip(RelType type) const {
74-
return 2;
75+
// TLSDESC relocations are processed separately. See relaxTlsGdToLe below.
76+
return type == R_386_TLS_GOTDESC || type == R_386_TLS_DESC_CALL ? 1 : 2;
7577
}
7678

7779
RelExpr X86::getRelExpr(RelType type, const Symbol &s,
@@ -143,6 +145,10 @@ RelExpr X86::getRelExpr(RelType type, const Symbol &s,
143145
// the byte, we can determine whether the instruction uses the operand as an
144146
// absolute address (R_GOT) or a register-relative address (R_GOTPLT).
145147
return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT;
148+
case R_386_TLS_GOTDESC:
149+
return R_TLSDESC_GOTPLT;
150+
case R_386_TLS_DESC_CALL:
151+
return R_TLSDESC_CALL;
146152
case R_386_TLS_GOTIE:
147153
return R_GOTPLT;
148154
case R_386_GOTOFF:
@@ -167,7 +173,8 @@ RelExpr X86::adjustTlsExpr(RelType type, RelExpr expr) const {
167173
case R_RELAX_TLS_GD_TO_IE:
168174
return R_RELAX_TLS_GD_TO_IE_GOTPLT;
169175
case R_RELAX_TLS_GD_TO_LE:
170-
return R_RELAX_TLS_GD_TO_LE_NEG;
176+
return type == R_386_TLS_GD ? R_RELAX_TLS_GD_TO_LE_NEG
177+
: R_RELAX_TLS_GD_TO_LE;
171178
}
172179
}
173180

@@ -259,6 +266,8 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
259266
case R_386_PC32:
260267
case R_386_PLT32:
261268
case R_386_RELATIVE:
269+
case R_386_TLS_GOTDESC:
270+
case R_386_TLS_DESC_CALL:
262271
case R_386_TLS_DTPMOD32:
263272
case R_386_TLS_DTPOFF32:
264273
case R_386_TLS_LDO_32:
@@ -273,6 +282,8 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
273282
case R_386_TLS_TPOFF:
274283
case R_386_TLS_TPOFF32:
275284
return SignExtend64<32>(read32le(buf));
285+
case R_386_TLS_DESC:
286+
return SignExtend64<32>(read32le(buf + 4));
276287
case R_386_NONE:
277288
case R_386_JUMP_SLOT:
278289
// These relocations are defined as not having an implicit addend.
@@ -323,6 +334,8 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
323334
case R_386_PC32:
324335
case R_386_PLT32:
325336
case R_386_RELATIVE:
337+
case R_386_TLS_GOTDESC:
338+
case R_386_TLS_DESC_CALL:
326339
case R_386_TLS_DTPMOD32:
327340
case R_386_TLS_DTPOFF32:
328341
case R_386_TLS_GD:
@@ -337,39 +350,79 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
337350
checkInt(loc, val, 32, rel);
338351
write32le(loc, val);
339352
break;
353+
case R_386_TLS_DESC:
354+
// The addend is stored in the second 32-bit word.
355+
write32le(loc + 4, val);
356+
break;
340357
default:
341358
llvm_unreachable("unknown relocation");
342359
}
343360
}
344361

345-
void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &, uint64_t val) const {
346-
// Convert
347-
// leal x@tlsgd(, %ebx, 1),
348-
// call __tls_get_addr@plt
349-
// to
350-
// movl %gs:0,%eax
351-
// subl $x@ntpoff,%eax
352-
const uint8_t inst[] = {
353-
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
354-
0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax
355-
};
356-
memcpy(loc - 3, inst, sizeof(inst));
357-
write32le(loc + 5, val);
362+
void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
363+
uint64_t val) const {
364+
if (rel.type == R_386_TLS_GD) {
365+
// Convert
366+
// leal x@tlsgd(, %ebx, 1), %eax
367+
// call __tls_get_addr@plt
368+
// to
369+
// movl %gs:0, %eax
370+
// subl $x@tpoff, %eax
371+
const uint8_t inst[] = {
372+
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
373+
0x81, 0xe8, 0, 0, 0, 0, // subl val(%ebx), %eax
374+
};
375+
memcpy(loc - 3, inst, sizeof(inst));
376+
write32le(loc + 5, val);
377+
} else if (rel.type == R_386_TLS_GOTDESC) {
378+
// Convert leal x@tlsdesc(%ebx), %eax to leal x@ntpoff, %eax.
379+
//
380+
// Note: call *x@tlsdesc(%eax) may not immediately follow this instruction.
381+
if (memcmp(loc - 2, "\x8d\x83", 2)) {
382+
error(getErrorLocation(loc - 2) +
383+
"R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax");
384+
return;
385+
}
386+
loc[-1] = 0x05;
387+
write32le(loc, val);
388+
} else {
389+
// Convert call *x@tlsdesc(%eax) to xchg ax, ax.
390+
assert(rel.type == R_386_TLS_DESC_CALL);
391+
loc[0] = 0x66;
392+
loc[1] = 0x90;
393+
}
358394
}
359395

360-
void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &, uint64_t val) const {
361-
// Convert
362-
// leal x@tlsgd(, %ebx, 1),
363-
// call __tls_get_addr@plt
364-
// to
365-
// movl %gs:0, %eax
366-
// addl x@gotntpoff(%ebx), %eax
367-
const uint8_t inst[] = {
368-
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
369-
0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax
370-
};
371-
memcpy(loc - 3, inst, sizeof(inst));
372-
write32le(loc + 5, val);
396+
void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
397+
uint64_t val) const {
398+
if (rel.type == R_386_TLS_GD) {
399+
// Convert
400+
// leal x@tlsgd(, %ebx, 1), %eax
401+
// call __tls_get_addr@plt
402+
// to
403+
// movl %gs:0, %eax
404+
// addl x@gotntpoff(%ebx), %eax
405+
const uint8_t inst[] = {
406+
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
407+
0x03, 0x83, 0, 0, 0, 0, // addl val(%ebx), %eax
408+
};
409+
memcpy(loc - 3, inst, sizeof(inst));
410+
write32le(loc + 5, val);
411+
} else if (rel.type == R_386_TLS_GOTDESC) {
412+
// Convert leal x@tlsdesc(%ebx), %eax to movl x@gotntpoff(%ebx), %eax.
413+
if (memcmp(loc - 2, "\x8d\x83", 2)) {
414+
error(getErrorLocation(loc - 2) +
415+
"R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax");
416+
return;
417+
}
418+
loc[-2] = 0x8b;
419+
write32le(loc, val);
420+
} else {
421+
// Convert call *x@tlsdesc(%eax) to xchg ax, ax.
422+
assert(rel.type == R_386_TLS_DESC_CALL);
423+
loc[0] = 0x66;
424+
loc[1] = 0x90;
425+
}
373426
}
374427

375428
// In some conditions, relocations can be optimized to avoid using GOT.

lld/ELF/InputSection.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
832832
return in.got->getGlobalDynAddr(sym) + a;
833833
case R_TLSDESC_PC:
834834
return in.got->getGlobalDynAddr(sym) + a - p;
835+
case R_TLSDESC_GOTPLT:
836+
return in.got->getGlobalDynAddr(sym) + a - in.gotPlt->getVA();
835837
case R_AARCH64_TLSDESC_PAGE:
836838
return getAArch64Page(in.got->getGlobalDynAddr(sym) + a) -
837839
getAArch64Page(p);

lld/ELF/Relocations.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,9 @@ static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
230230
R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
231231
R_PLT_PC, R_PLT_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC,
232232
R_PPC32_PLTREL, R_PPC64_CALL_PLT, R_PPC64_RELAX_TOC, R_RISCV_ADD,
233-
R_TLSDESC_CALL, R_TLSDESC_PC, R_AARCH64_TLSDESC_PAGE, R_TLSLD_HINT,
234-
R_TLSIE_HINT, R_AARCH64_GOT_PAGE>(e))
233+
R_TLSDESC_CALL, R_TLSDESC_PC, R_TLSDESC_GOTPLT,
234+
R_AARCH64_TLSDESC_PAGE, R_TLSLD_HINT, R_TLSIE_HINT,
235+
R_AARCH64_GOT_PAGE>(e))
235236
return true;
236237

237238
// These never do, except if the entire file is position dependent or if
@@ -1157,8 +1158,8 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
11571158
if (config->emachine == EM_MIPS)
11581159
return handleMipsTlsRelocation(type, sym, c, offset, addend, expr);
11591160

1160-
if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC>(
1161-
expr) &&
1161+
if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
1162+
R_TLSDESC_GOTPLT>(expr) &&
11621163
config->shared) {
11631164
if (in.got->addDynTlsEntry(sym)) {
11641165
uint64_t off = in.got->getGlobalDynOffset(sym);
@@ -1235,7 +1236,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
12351236
}
12361237

12371238
if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
1238-
R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
1239+
R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
12391240
if (!toExecRelax) {
12401241
if (in.got->addDynTlsEntry(sym)) {
12411242
uint64_t off = in.got->getGlobalDynOffset(sym);
@@ -1401,7 +1402,7 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
14011402
//
14021403
// The 5 types that relative GOTPLT are all x86 and x86-64 specific.
14031404
if (oneof<R_GOTPLTONLY_PC, R_GOTPLTREL, R_GOTPLT, R_PLT_GOTPLT,
1404-
R_TLSGD_GOTPLT>(expr)) {
1405+
R_TLSDESC_GOTPLT, R_TLSGD_GOTPLT>(expr)) {
14051406
in.gotPlt->hasGotPltOffRel = true;
14061407
} else if (oneof<R_GOTONLY_PC, R_GOTREL, R_PPC64_TOCBASE, R_PPC64_RELAX_TOC>(
14071408
expr)) {

lld/ELF/Relocations.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ enum RelExpr {
6363
R_TLSDESC,
6464
R_TLSDESC_CALL,
6565
R_TLSDESC_PC,
66+
R_TLSDESC_GOTPLT,
6667
R_TLSGD_GOT,
6768
R_TLSGD_GOTPLT,
6869
R_TLSGD_PC,

lld/ELF/Writer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1960,7 +1960,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
19601960
0x800, STV_DEFAULT);
19611961
}
19621962

1963-
if (config->emachine == EM_X86_64) {
1963+
if (config->emachine == EM_386 || config->emachine == EM_X86_64) {
19641964
// On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
19651965
// way that:
19661966
//

lld/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ ELF Improvements
3232
Instead, a value of 0 will be written.
3333
(`D110014 <https://reviews.llvm.org/D110014>`_)
3434

35+
Architecture specific changes:
36+
37+
* The x86-32 port now supports TLSDESC (``-mtls-dialect=gnu2``).
38+
(`D112582 <https://reviews.llvm.org/D112582 >`_)
39+
3540
Breaking changes
3641
----------------
3742

lld/test/ELF/i386-tlsdesc-gd.s

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# REQUIRES: x86
2+
# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t.o
3+
# RUN: echo '.tbss; .globl c; c: .zero 4' | llvm-mc -filetype=obj -triple=i386 - -o %t1.o
4+
# RUN: ld.lld -shared -soname=t1.so %t1.o -o %t1.so
5+
6+
# RUN: ld.lld -shared -z now %t.o %t1.o -o %t.so
7+
# RUN: llvm-readobj -r -x .got %t.so | FileCheck --check-prefix=GD-REL %s
8+
# RUN: llvm-objdump -h -d --no-show-raw-insn %t.so | FileCheck --check-prefix=GD %s
9+
10+
# RUN: ld.lld -shared -z now %t.o %t1.o -o %t-rela.so -z rela
11+
# RUN: llvm-readobj -r -x .got %t-rela.so | FileCheck --check-prefix=GD-RELA %s
12+
13+
# RUN: ld.lld -z now %t.o %t1.o -o %t
14+
# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s
15+
# RUN: llvm-objdump -h -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s
16+
17+
# RUN: ld.lld -z now %t.o %t1.so -o %t
18+
# RUN: llvm-readobj -r %t | FileCheck --check-prefix=IE-REL %s
19+
# RUN: llvm-objdump -h -d --no-show-raw-insn %t | FileCheck --check-prefix=IE %s
20+
21+
# GD-REL: .rel.dyn {
22+
# GD-REL-NEXT: 0x2250 R_386_TLS_DESC -
23+
# GD-REL-NEXT: 0x2248 R_386_TLS_DESC a
24+
# GD-REL-NEXT: 0x2258 R_386_TLS_DESC c
25+
# GD-REL-NEXT: }
26+
# GD-REL: Hex dump of section '.got':
27+
# GD-REL-NEXT: 0x00002248 00000000 00000000 00000000 0b000000
28+
# GD-REL-NEXT: 0x00002258 00000000 00000000
29+
30+
# GD-RELA: .rela.dyn {
31+
# GD-RELA-NEXT: 0x225C R_386_TLS_DESC - 0xB
32+
# GD-RELA-NEXT: 0x2254 R_386_TLS_DESC a 0x0
33+
# GD-RELA-NEXT: 0x2264 R_386_TLS_DESC c 0x0
34+
# GD-RELA-NEXT: }
35+
# GD-RELA: Hex dump of section '.got':
36+
# GD-RELA-NEXT: 0x00002254 00000000 00000000 00000000 00000000
37+
# GD-RELA-NEXT: 0x00002264 00000000 00000000
38+
39+
# GD: .got 00000018 00002248
40+
# GD: .got.plt 0000000c 00002260
41+
42+
# &.rel.dyn[a]-.got.plt = 0x2248-0x2260 = -24
43+
# GD: leal -24(%ebx), %eax
44+
# GD-NEXT: calll *(%eax)
45+
# GD-NEXT: movl %gs:(%eax), %eax
46+
47+
# &.rel.dyn[b]-.got.plt = 0x2250-0x2260 = -16
48+
# GD-NEXT: leal -16(%ebx), %eax
49+
# GD-NEXT: movl %edx, %ebx
50+
# GD-NEXT: calll *(%eax)
51+
# GD-NEXT: movl %gs:(%eax), %eax
52+
53+
# &.rel.dyn[c]-.got.plt = 0x2258-0x2260 = -8
54+
# GD-NEXT: leal -8(%ebx), %eax
55+
# GD-NEXT: calll *(%eax)
56+
# GD-NEXT: movl %gs:(%eax), %eax
57+
58+
# NOREL: no relocations
59+
60+
## st_value(a) - tls_size = -8
61+
# LE: leal -8, %eax
62+
# LE-NEXT: nop
63+
# LE-NEXT: movl %gs:(%eax), %eax
64+
## st_value(b) - tls_size = -5
65+
# LE: leal -5, %eax
66+
# LE-NEXT: movl %edx, %ebx
67+
# LE-NEXT: nop
68+
# LE-NEXT: movl %gs:(%eax), %eax
69+
## st_value(c) - tls_size = -4
70+
# LE: leal -4, %eax
71+
# LE-NEXT: nop
72+
# LE-NEXT: movl %gs:(%eax), %eax
73+
74+
# IE-REL: .rel.dyn {
75+
# IE-REL-NEXT: 0x40222C R_386_TLS_TPOFF c
76+
# IE-REL-NEXT: }
77+
78+
# IE: .got 00000004 0040222c
79+
# IE: .got.plt 0000000c 00402230
80+
81+
## a and b are relaxed to use LE.
82+
# IE: leal -4, %eax
83+
# IE-NEXT: nop
84+
# IE-NEXT: movl %gs:(%eax), %eax
85+
# IE-NEXT: leal -1, %eax
86+
# IE-NEXT: movl %edx, %ebx
87+
# IE-NEXT: nop
88+
# IE-NEXT: movl %gs:(%eax), %eax
89+
## &.got[a]-.got.plt = 0x2220 - 0x2224 = -4
90+
# IE-NEXT: movl -4(%ebx), %eax
91+
# IE-NEXT: nop
92+
# IE-NEXT: movl %gs:(%eax), %eax
93+
94+
leal a@tlsdesc(%ebx), %eax
95+
call *a@tlscall(%eax)
96+
movl %gs:(%eax), %eax
97+
98+
leal b@tlsdesc(%ebx), %eax
99+
movl %edx, %ebx # GCC -O0 may add an extra insn in between.
100+
call *b@tlscall(%eax)
101+
movl %gs:(%eax), %eax
102+
103+
leal c@tlsdesc(%ebx), %eax
104+
call *c@tlscall(%eax)
105+
movl %gs:(%eax), %eax
106+
107+
.section .tbss
108+
.globl a
109+
.zero 8
110+
a:
111+
.zero 3
112+
b:
113+
.zero 1

0 commit comments

Comments
 (0)