Skip to content

Commit

Permalink
[ELF] Implement TLSDESC for x86-32
Browse files Browse the repository at this point in the history
`-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
  • Loading branch information
MaskRay committed Oct 29, 2021
1 parent 12b2cc2 commit e39c138
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 35 deletions.
109 changes: 81 additions & 28 deletions lld/ELF/Arch/X86.cpp
Expand Up @@ -56,6 +56,7 @@ X86::X86() {
iRelativeRel = R_386_IRELATIVE;
relativeRel = R_386_RELATIVE;
symbolicRel = R_386_32;
tlsDescRel = R_386_TLS_DESC;
tlsGotRel = R_386_TLS_TPOFF;
tlsModuleIndexRel = R_386_TLS_DTPMOD32;
tlsOffsetRel = R_386_TLS_DTPOFF32;
Expand All @@ -71,7 +72,8 @@ X86::X86() {
}

int X86::getTlsGdRelaxSkip(RelType type) const {
return 2;
// TLSDESC relocations are processed separately. See relaxTlsGdToLe below.
return type == R_386_TLS_GOTDESC || type == R_386_TLS_DESC_CALL ? 1 : 2;
}

RelExpr X86::getRelExpr(RelType type, const Symbol &s,
Expand Down Expand Up @@ -143,6 +145,10 @@ RelExpr X86::getRelExpr(RelType type, const Symbol &s,
// the byte, we can determine whether the instruction uses the operand as an
// absolute address (R_GOT) or a register-relative address (R_GOTPLT).
return (loc[-1] & 0xc7) == 0x5 ? R_GOT : R_GOTPLT;
case R_386_TLS_GOTDESC:
return R_TLSDESC_GOTPLT;
case R_386_TLS_DESC_CALL:
return R_TLSDESC_CALL;
case R_386_TLS_GOTIE:
return R_GOTPLT;
case R_386_GOTOFF:
Expand All @@ -167,7 +173,8 @@ RelExpr X86::adjustTlsExpr(RelType type, RelExpr expr) const {
case R_RELAX_TLS_GD_TO_IE:
return R_RELAX_TLS_GD_TO_IE_GOTPLT;
case R_RELAX_TLS_GD_TO_LE:
return R_RELAX_TLS_GD_TO_LE_NEG;
return type == R_386_TLS_GD ? R_RELAX_TLS_GD_TO_LE_NEG
: R_RELAX_TLS_GD_TO_LE;
}
}

Expand Down Expand Up @@ -259,6 +266,8 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
case R_386_PC32:
case R_386_PLT32:
case R_386_RELATIVE:
case R_386_TLS_GOTDESC:
case R_386_TLS_DESC_CALL:
case R_386_TLS_DTPMOD32:
case R_386_TLS_DTPOFF32:
case R_386_TLS_LDO_32:
Expand All @@ -273,6 +282,8 @@ int64_t X86::getImplicitAddend(const uint8_t *buf, RelType type) const {
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
return SignExtend64<32>(read32le(buf));
case R_386_TLS_DESC:
return SignExtend64<32>(read32le(buf + 4));
case R_386_NONE:
case R_386_JUMP_SLOT:
// These relocations are defined as not having an implicit addend.
Expand Down Expand Up @@ -323,6 +334,8 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
case R_386_PC32:
case R_386_PLT32:
case R_386_RELATIVE:
case R_386_TLS_GOTDESC:
case R_386_TLS_DESC_CALL:
case R_386_TLS_DTPMOD32:
case R_386_TLS_DTPOFF32:
case R_386_TLS_GD:
Expand All @@ -337,39 +350,79 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
checkInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_386_TLS_DESC:
// The addend is stored in the second 32-bit word.
write32le(loc + 4, val);
break;
default:
llvm_unreachable("unknown relocation");
}
}

void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
// to
// movl %gs:0,%eax
// subl $x@ntpoff,%eax
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
0x81, 0xe8, 0, 0, 0, 0, // subl Val(%ebx), %eax
};
memcpy(loc - 3, inst, sizeof(inst));
write32le(loc + 5, val);
void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
if (rel.type == R_386_TLS_GD) {
// Convert
// leal x@tlsgd(, %ebx, 1), %eax
// call __tls_get_addr@plt
// to
// movl %gs:0, %eax
// subl $x@tpoff, %eax
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
0x81, 0xe8, 0, 0, 0, 0, // subl val(%ebx), %eax
};
memcpy(loc - 3, inst, sizeof(inst));
write32le(loc + 5, val);
} else if (rel.type == R_386_TLS_GOTDESC) {
// Convert leal x@tlsdesc(%ebx), %eax to leal x@ntpoff, %eax.
//
// Note: call *x@tlsdesc(%eax) may not immediately follow this instruction.
if (memcmp(loc - 2, "\x8d\x83", 2)) {
error(getErrorLocation(loc - 2) +
"R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax");
return;
}
loc[-1] = 0x05;
write32le(loc, val);
} else {
// Convert call *x@tlsdesc(%eax) to xchg ax, ax.
assert(rel.type == R_386_TLS_DESC_CALL);
loc[0] = 0x66;
loc[1] = 0x90;
}
}

void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
// to
// movl %gs:0, %eax
// addl x@gotntpoff(%ebx), %eax
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
0x03, 0x83, 0, 0, 0, 0, // addl Val(%ebx), %eax
};
memcpy(loc - 3, inst, sizeof(inst));
write32le(loc + 5, val);
void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
if (rel.type == R_386_TLS_GD) {
// Convert
// leal x@tlsgd(, %ebx, 1), %eax
// call __tls_get_addr@plt
// to
// movl %gs:0, %eax
// addl x@gotntpoff(%ebx), %eax
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
0x03, 0x83, 0, 0, 0, 0, // addl val(%ebx), %eax
};
memcpy(loc - 3, inst, sizeof(inst));
write32le(loc + 5, val);
} else if (rel.type == R_386_TLS_GOTDESC) {
// Convert leal x@tlsdesc(%ebx), %eax to movl x@gotntpoff(%ebx), %eax.
if (memcmp(loc - 2, "\x8d\x83", 2)) {
error(getErrorLocation(loc - 2) +
"R_386_TLS_GOTDESC must be used in leal x@tlsdesc(%ebx), %eax");
return;
}
loc[-2] = 0x8b;
write32le(loc, val);
} else {
// Convert call *x@tlsdesc(%eax) to xchg ax, ax.
assert(rel.type == R_386_TLS_DESC_CALL);
loc[0] = 0x66;
loc[1] = 0x90;
}
}

// In some conditions, relocations can be optimized to avoid using GOT.
Expand Down
2 changes: 2 additions & 0 deletions lld/ELF/InputSection.cpp
Expand Up @@ -832,6 +832,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
return in.got->getGlobalDynAddr(sym) + a;
case R_TLSDESC_PC:
return in.got->getGlobalDynAddr(sym) + a - p;
case R_TLSDESC_GOTPLT:
return in.got->getGlobalDynAddr(sym) + a - in.gotPlt->getVA();
case R_AARCH64_TLSDESC_PAGE:
return getAArch64Page(in.got->getGlobalDynAddr(sym) + a) -
getAArch64Page(p);
Expand Down
13 changes: 7 additions & 6 deletions lld/ELF/Relocations.cpp
Expand Up @@ -230,8 +230,9 @@ static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
R_PLT_PC, R_PLT_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC,
R_PPC32_PLTREL, R_PPC64_CALL_PLT, R_PPC64_RELAX_TOC, R_RISCV_ADD,
R_TLSDESC_CALL, R_TLSDESC_PC, R_AARCH64_TLSDESC_PAGE, R_TLSLD_HINT,
R_TLSIE_HINT, R_AARCH64_GOT_PAGE>(e))
R_TLSDESC_CALL, R_TLSDESC_PC, R_TLSDESC_GOTPLT,
R_AARCH64_TLSDESC_PAGE, R_TLSLD_HINT, R_TLSIE_HINT,
R_AARCH64_GOT_PAGE>(e))
return true;

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

if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC>(
expr) &&
if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
R_TLSDESC_GOTPLT>(expr) &&
config->shared) {
if (in.got->addDynTlsEntry(sym)) {
uint64_t off = in.got->getGlobalDynOffset(sym);
Expand Down Expand Up @@ -1235,7 +1236,7 @@ handleTlsRelocation(RelType type, Symbol &sym, InputSectionBase &c,
}

if (oneof<R_AARCH64_TLSDESC_PAGE, R_TLSDESC, R_TLSDESC_CALL, R_TLSDESC_PC,
R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
R_TLSDESC_GOTPLT, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC>(expr)) {
if (!toExecRelax) {
if (in.got->addDynTlsEntry(sym)) {
uint64_t off = in.got->getGlobalDynOffset(sym);
Expand Down Expand Up @@ -1401,7 +1402,7 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
//
// The 5 types that relative GOTPLT are all x86 and x86-64 specific.
if (oneof<R_GOTPLTONLY_PC, R_GOTPLTREL, R_GOTPLT, R_PLT_GOTPLT,
R_TLSGD_GOTPLT>(expr)) {
R_TLSDESC_GOTPLT, R_TLSGD_GOTPLT>(expr)) {
in.gotPlt->hasGotPltOffRel = true;
} else if (oneof<R_GOTONLY_PC, R_GOTREL, R_PPC64_TOCBASE, R_PPC64_RELAX_TOC>(
expr)) {
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Relocations.h
Expand Up @@ -63,6 +63,7 @@ enum RelExpr {
R_TLSDESC,
R_TLSDESC_CALL,
R_TLSDESC_PC,
R_TLSDESC_GOTPLT,
R_TLSGD_GOT,
R_TLSGD_GOTPLT,
R_TLSGD_PC,
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/Writer.cpp
Expand Up @@ -1960,7 +1960,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
0x800, STV_DEFAULT);
}

if (config->emachine == EM_X86_64) {
if (config->emachine == EM_386 || config->emachine == EM_X86_64) {
// On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
// way that:
//
Expand Down
5 changes: 5 additions & 0 deletions lld/docs/ReleaseNotes.rst
Expand Up @@ -32,6 +32,11 @@ ELF Improvements
Instead, a value of 0 will be written.
(`D110014 <https://reviews.llvm.org/D110014>`_)

Architecture specific changes:

* The x86-32 port now supports TLSDESC (``-mtls-dialect=gnu2``).
(`D112582 <https://reviews.llvm.org/D112582 >`_)

Breaking changes
----------------

Expand Down
113 changes: 113 additions & 0 deletions lld/test/ELF/i386-tlsdesc-gd.s
@@ -0,0 +1,113 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t.o
# RUN: echo '.tbss; .globl c; c: .zero 4' | llvm-mc -filetype=obj -triple=i386 - -o %t1.o
# RUN: ld.lld -shared -soname=t1.so %t1.o -o %t1.so

# RUN: ld.lld -shared -z now %t.o %t1.o -o %t.so
# RUN: llvm-readobj -r -x .got %t.so | FileCheck --check-prefix=GD-REL %s
# RUN: llvm-objdump -h -d --no-show-raw-insn %t.so | FileCheck --check-prefix=GD %s

# RUN: ld.lld -shared -z now %t.o %t1.o -o %t-rela.so -z rela
# RUN: llvm-readobj -r -x .got %t-rela.so | FileCheck --check-prefix=GD-RELA %s

# RUN: ld.lld -z now %t.o %t1.o -o %t
# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s
# RUN: llvm-objdump -h -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s

# RUN: ld.lld -z now %t.o %t1.so -o %t
# RUN: llvm-readobj -r %t | FileCheck --check-prefix=IE-REL %s
# RUN: llvm-objdump -h -d --no-show-raw-insn %t | FileCheck --check-prefix=IE %s

# GD-REL: .rel.dyn {
# GD-REL-NEXT: 0x2250 R_386_TLS_DESC -
# GD-REL-NEXT: 0x2248 R_386_TLS_DESC a
# GD-REL-NEXT: 0x2258 R_386_TLS_DESC c
# GD-REL-NEXT: }
# GD-REL: Hex dump of section '.got':
# GD-REL-NEXT: 0x00002248 00000000 00000000 00000000 0b000000
# GD-REL-NEXT: 0x00002258 00000000 00000000

# GD-RELA: .rela.dyn {
# GD-RELA-NEXT: 0x225C R_386_TLS_DESC - 0xB
# GD-RELA-NEXT: 0x2254 R_386_TLS_DESC a 0x0
# GD-RELA-NEXT: 0x2264 R_386_TLS_DESC c 0x0
# GD-RELA-NEXT: }
# GD-RELA: Hex dump of section '.got':
# GD-RELA-NEXT: 0x00002254 00000000 00000000 00000000 00000000
# GD-RELA-NEXT: 0x00002264 00000000 00000000

# GD: .got 00000018 00002248
# GD: .got.plt 0000000c 00002260

# &.rel.dyn[a]-.got.plt = 0x2248-0x2260 = -24
# GD: leal -24(%ebx), %eax
# GD-NEXT: calll *(%eax)
# GD-NEXT: movl %gs:(%eax), %eax

# &.rel.dyn[b]-.got.plt = 0x2250-0x2260 = -16
# GD-NEXT: leal -16(%ebx), %eax
# GD-NEXT: movl %edx, %ebx
# GD-NEXT: calll *(%eax)
# GD-NEXT: movl %gs:(%eax), %eax

# &.rel.dyn[c]-.got.plt = 0x2258-0x2260 = -8
# GD-NEXT: leal -8(%ebx), %eax
# GD-NEXT: calll *(%eax)
# GD-NEXT: movl %gs:(%eax), %eax

# NOREL: no relocations

## st_value(a) - tls_size = -8
# LE: leal -8, %eax
# LE-NEXT: nop
# LE-NEXT: movl %gs:(%eax), %eax
## st_value(b) - tls_size = -5
# LE: leal -5, %eax
# LE-NEXT: movl %edx, %ebx
# LE-NEXT: nop
# LE-NEXT: movl %gs:(%eax), %eax
## st_value(c) - tls_size = -4
# LE: leal -4, %eax
# LE-NEXT: nop
# LE-NEXT: movl %gs:(%eax), %eax

# IE-REL: .rel.dyn {
# IE-REL-NEXT: 0x40222C R_386_TLS_TPOFF c
# IE-REL-NEXT: }

# IE: .got 00000004 0040222c
# IE: .got.plt 0000000c 00402230

## a and b are relaxed to use LE.
# IE: leal -4, %eax
# IE-NEXT: nop
# IE-NEXT: movl %gs:(%eax), %eax
# IE-NEXT: leal -1, %eax
# IE-NEXT: movl %edx, %ebx
# IE-NEXT: nop
# IE-NEXT: movl %gs:(%eax), %eax
## &.got[a]-.got.plt = 0x2220 - 0x2224 = -4
# IE-NEXT: movl -4(%ebx), %eax
# IE-NEXT: nop
# IE-NEXT: movl %gs:(%eax), %eax

leal a@tlsdesc(%ebx), %eax
call *a@tlscall(%eax)
movl %gs:(%eax), %eax

leal b@tlsdesc(%ebx), %eax
movl %edx, %ebx # GCC -O0 may add an extra insn in between.
call *b@tlscall(%eax)
movl %gs:(%eax), %eax

leal c@tlsdesc(%ebx), %eax
call *c@tlscall(%eax)
movl %gs:(%eax), %eax

.section .tbss
.globl a
.zero 8
a:
.zero 3
b:
.zero 1

0 comments on commit e39c138

Please sign in to comment.