Skip to content

Commit

Permalink
[ELF] Support TLS GD/LD relaxations for x86-32 -fno-plt
Browse files Browse the repository at this point in the history
For x86-32, {clang,gcc} -fno-plt uses `call *___tls_get_addr@GOT(%reg)` instead
of `call ___tls_get_addr@PLT`. GD to IE/LE relaxations need to shift the offset
by one while LD to LE relaxation needs to use a different code sequence.

While here, fix some comments.

Fix #59769

Differential Revision: https://reviews.llvm.org/D140813
  • Loading branch information
MaskRay committed Jan 1, 2023
1 parent 1e48ed3 commit 8dc7366
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 24 deletions.
57 changes: 35 additions & 22 deletions lld/ELF/Arch/X86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,18 +346,20 @@ void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {

static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
if (rel.type == R_386_TLS_GD) {
// Convert
// Convert (loc[-2] == 0x04)
// leal x@tlsgd(, %ebx, 1), %eax
// call __tls_get_addr@plt
// call ___tls_get_addr@plt
// or
// leal x@tlsgd(%reg), %eax
// call *___tls_get_addr@got(%reg)
// 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
0x81, 0xe8, 0, 0, 0, 0, // subl x@ntpoff(%ebx), %eax
};
memcpy(loc - 3, inst, sizeof(inst));
write32le(loc + 5, val);
uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2;
memcpy(w, inst, sizeof(inst));
write32le(w + 8, val);
} else if (rel.type == R_386_TLS_GOTDESC) {
// Convert leal x@tlsdesc(%ebx), %eax to leal x@ntpoff, %eax.
//
Expand All @@ -379,18 +381,19 @@ static void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {

static void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, uint64_t val) {
if (rel.type == R_386_TLS_GD) {
// Convert
// Convert (loc[-2] == 0x04)
// leal x@tlsgd(, %ebx, 1), %eax
// call __tls_get_addr@plt
// to
// movl %gs:0, %eax
// addl x@gotntpoff(%ebx), %eax
// call ___tls_get_addr@plt
// or
// leal x@tlsgd(%reg), %eax
// call *___tls_get_addr@got(%reg)
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0, %eax
0x03, 0x83, 0, 0, 0, 0, // addl val(%ebx), %eax
0x03, 0x83, 0, 0, 0, 0, // addl x@gottpoff(%ebx), %eax
};
memcpy(loc - 3, inst, sizeof(inst));
write32le(loc + 5, val);
uint8_t *w = loc[-2] == 0x04 ? loc - 3 : loc - 2;
memcpy(w, inst, sizeof(inst));
write32le(w + 8, 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)) {
Expand Down Expand Up @@ -453,17 +456,27 @@ static void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel, uint64_t val) {
return;
}

if (loc[4] == 0xe8) {
// Convert
// leal x(%reg),%eax
// call ___tls_get_addr@plt
// to
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax
0x90, // nop
0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi
};
memcpy(loc - 2, inst, sizeof(inst));
return;
}

// Convert
// leal foo(%reg),%eax
// call ___tls_get_addr
// leal x(%reg),%eax
// call *___tls_get_addr@got(%reg)
// to
// movl %gs:0,%eax
// nop
// leal 0(%esi,1),%esi
const uint8_t inst[] = {
0x65, 0xa1, 0x00, 0x00, 0x00, 0x00, // movl %gs:0,%eax
0x90, // nop
0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi
0x8d, 0xb6, 0x00, 0x00, 0x00, 0x00, // leal (%esi),%esi
};
memcpy(loc - 2, inst, sizeof(inst));
}
Expand Down
14 changes: 12 additions & 2 deletions lld/test/ELF/i386-tls-gdiele.s
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

// NORELOC: Relocations [
// NORELOC-NEXT: Section ({{.*}}) .rel.dyn {
// NORELOC-NEXT: 0x402258 R_386_TLS_TPOFF tlsshared0
// NORELOC-NEXT: 0x40225C R_386_TLS_TPOFF tlsshared1
// NORELOC-NEXT: 0x402270 R_386_TLS_TPOFF tlsshared0
// NORELOC-NEXT: 0x402274 R_386_TLS_TPOFF tlsshared1
// NORELOC-NEXT: }
// NORELOC-NEXT: ]

Expand All @@ -24,6 +24,10 @@
// DISASM-NEXT: subl $8, %eax
// DISASM-NEXT: movl %gs:0, %eax
// DISASM-NEXT: subl $4, %eax
// DISASM-NEXT: movl %gs:0, %eax
// DISASM-NEXT: addl -4100(%ebx), %eax
// DISASM-NEXT: movl %gs:0, %eax
// DISASM-NEXT: subl $4, %eax

.type tlsexe1,@object
.section .tbss,"awT",@nobits
Expand Down Expand Up @@ -59,3 +63,9 @@ leal tlsexe1@tlsgd(,%ebx,1),%eax
call ___tls_get_addr@plt
leal tlsexe2@tlsgd(,%ebx,1),%eax
call ___tls_get_addr@plt

// -fno-plt GD->IE and GD->LE
leal tlsshared1@tlsgd(%edx),%eax
call *___tls_get_addr@GOT(%edx)
leal tlsexe2@tlsgd(%edx),%eax
call *___tls_get_addr@GOT(%edx)
7 changes: 7 additions & 0 deletions lld/test/ELF/i386-tls-opt.s
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
// DISASM-NEXT: nop
// DISASM-NEXT: leal (%esi,%eiz), %esi
// DISASM-NEXT: leal -4(%eax), %edx
// DISASM-NEXT: movl %gs:0, %eax
// DISASM-NEXT: leal (%esi), %esi
// DISASM-NEXT: movl -4(%eax), %edx
// IE -> LE:
// 4294967288 == 0xFFFFFFF8
// 4294967292 == 0xFFFFFFFC
Expand Down Expand Up @@ -60,6 +63,10 @@ leal tls0@dtpoff(%eax),%edx
leal tls1@tlsldm(%ebx),%eax
call ___tls_get_addr@plt
leal tls1@dtpoff(%eax),%edx
// -fno-plt LD -> LE
leal tls1@tlsldm(%edx),%eax
call *___tls_get_addr@GOT(%edx)
movl tls1@dtpoff(%eax), %edx
//IE -> LE:
movl %gs:0,%eax
movl tls0@gotntpoff(%ebx),%eax
Expand Down

0 comments on commit 8dc7366

Please sign in to comment.