[lld] e39c138 - [ELF] Implement TLSDESC for x86-32
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 28 17:52:07 PDT 2021
Author: Fangrui Song
Date: 2021-10-28T17:52:03-07:00
New Revision: e39c138f4522baef5cd060fd8b5e715d72e77a6a
URL: https://github.com/llvm/llvm-project/commit/e39c138f4522baef5cd060fd8b5e715d72e77a6a
DIFF: https://github.com/llvm/llvm-project/commit/e39c138f4522baef5cd060fd8b5e715d72e77a6a.diff
LOG: [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
Added:
lld/test/ELF/i386-tlsdesc-gd.s
lld/test/ELF/i386-tlsdesc-ld.s
lld/test/ELF/invalid/i386-tlsdesc-gd.s
Modified:
lld/ELF/Arch/X86.cpp
lld/ELF/InputSection.cpp
lld/ELF/Relocations.cpp
lld/ELF/Relocations.h
lld/ELF/Writer.cpp
lld/docs/ReleaseNotes.rst
Removed:
################################################################################
diff --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp
index 726160c7b5017..5d34b769e80ec 100644
--- a/lld/ELF/Arch/X86.cpp
+++ b/lld/ELF/Arch/X86.cpp
@@ -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;
@@ -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,
@@ -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:
@@ -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;
}
}
@@ -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:
@@ -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.
@@ -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:
@@ -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 at tlsgd(, %ebx, 1),
- // call __tls_get_addr at plt
- // to
- // movl %gs:0,%eax
- // subl $x at 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 at tlsgd(, %ebx, 1), %eax
+ // call __tls_get_addr at plt
+ // to
+ // movl %gs:0, %eax
+ // subl $x at 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 at tlsdesc(%ebx), %eax to leal x at ntpoff, %eax.
+ //
+ // Note: call *x at 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 at tlsdesc(%ebx), %eax");
+ return;
+ }
+ loc[-1] = 0x05;
+ write32le(loc, val);
+ } else {
+ // Convert call *x at 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 at tlsgd(, %ebx, 1),
- // call __tls_get_addr at plt
- // to
- // movl %gs:0, %eax
- // addl x at 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 at tlsgd(, %ebx, 1), %eax
+ // call __tls_get_addr at plt
+ // to
+ // movl %gs:0, %eax
+ // addl x at 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 at tlsdesc(%ebx), %eax to movl x at gotntpoff(%ebx), %eax.
+ if (memcmp(loc - 2, "\x8d\x83", 2)) {
+ error(getErrorLocation(loc - 2) +
+ "R_386_TLS_GOTDESC must be used in leal x at tlsdesc(%ebx), %eax");
+ return;
+ }
+ loc[-2] = 0x8b;
+ write32le(loc, val);
+ } else {
+ // Convert call *x at 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.
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index b447918984942..de6333162fd60 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -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);
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 6c2076f142b82..71d1024d7017f 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -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
@@ -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);
@@ -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);
@@ -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)) {
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index d7569ac9ac48d..86e6cf4bc1f5a 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -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,
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 516b5b9cd147a..ff16922d80fd6 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -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:
//
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 91a1417b0db73..f196a9fd42ef9 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -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
----------------
diff --git a/lld/test/ELF/i386-tlsdesc-gd.s b/lld/test/ELF/i386-tlsdesc-gd.s
new file mode 100644
index 0000000000000..a2fc0f8f66450
--- /dev/null
+++ b/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 at tlsdesc(%ebx), %eax
+call *a at tlscall(%eax)
+movl %gs:(%eax), %eax
+
+leal b at tlsdesc(%ebx), %eax
+movl %edx, %ebx # GCC -O0 may add an extra insn in between.
+call *b at tlscall(%eax)
+movl %gs:(%eax), %eax
+
+leal c at tlsdesc(%ebx), %eax
+call *c at tlscall(%eax)
+movl %gs:(%eax), %eax
+
+.section .tbss
+.globl a
+.zero 8
+a:
+.zero 3
+b:
+.zero 1
diff --git a/lld/test/ELF/i386-tlsdesc-ld.s b/lld/test/ELF/i386-tlsdesc-ld.s
new file mode 100644
index 0000000000000..b69c5c393ff59
--- /dev/null
+++ b/lld/test/ELF/i386-tlsdesc-ld.s
@@ -0,0 +1,49 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t.o
+
+# RUN: ld.lld -shared -z now %t.o -o %t.so
+# RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=LD-REL %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=LD %s
+
+# RUN: ld.lld -z now %t.o -o %t
+# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s
+
+## Check _TLS_MODULE_BASE_ used by LD produces a dynamic relocation with a value of 0.
+# LD-REL: .rel.dyn {
+# LD-REL-NEXT: R_386_TLS_DESC -
+# LD-REL-NEXT: }
+
+## 0x2318-0x1267 = 4273
+## dtpoff(a) = 8, dtpoff(b) = 12
+# LD: leal -8(%ebx), %eax
+# LD-NEXT: calll *(%eax)
+# LD-NEXT: leal 8(%eax), %ecx
+# LD-NEXT: leal 12(%eax), %edx
+
+## When producing an executable, the LD code sequence can be relaxed to LE.
+## It is the same as GD->LE.
+## tpoff(_TLS_MODULE_BASE_) = 0, tpoff(a) = -8, tpoff(b) = -4
+
+# NOREL: no relocations
+
+# LE: leal 0, %eax
+# LE-NEXT: nop
+# LE-NEXT: leal -8(%eax), %ecx
+# LE-NEXT: leal -4(%eax), %edx
+# LE-NEXT: addl %gs:0, %ecx
+# LE-NEXT: addl %gs:0, %edx
+
+leal _TLS_MODULE_BASE_ at tlsdesc(%ebx), %eax
+call *_TLS_MODULE_BASE_ at tlscall(%eax)
+leal a at dtpoff(%eax), %ecx
+leal b at dtpoff(%eax), %edx
+addl %gs:0, %ecx
+addl %gs:0, %edx
+
+.section .tbss
+.zero 8
+a:
+.zero 4
+b:
+.zero 4
diff --git a/lld/test/ELF/invalid/i386-tlsdesc-gd.s b/lld/test/ELF/invalid/i386-tlsdesc-gd.s
new file mode 100644
index 0000000000000..804fbf0789314
--- /dev/null
+++ b/lld/test/ELF/invalid/i386-tlsdesc-gd.s
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t.o
+# RUN: echo '.tbss; .globl a; a:' | llvm-mc -filetype=obj -triple=i386 - -o %t1.o
+# RUN: ld.lld -shared %t1.o -o %t1.so
+
+## GD to LE relaxation.
+# RUN: not ld.lld %t.o %t1.o -o /dev/null 2>&1 | FileCheck -DINPUT=%t.o %s
+## GD to IE relaxation.
+# RUN: not ld.lld %t.o %t1.so -o /dev/null 2>&1 | FileCheck -DINPUT=%t.o %s
+
+# CHECK: error: [[INPUT]]:(.text+0x0): R_386_TLS_GOTDESC must be used in leal x at tlsdesc(%ebx), %eax
+
+leal a at tlsdesc(%ebx), %ecx
+call *a at tlscall(%eax)
More information about the llvm-commits
mailing list