[ELF] Support TLS GD/LD relaxations for x86-32 -fno-plt
authorFangrui Song <i@maskray.me>
Sun, 1 Jan 2023 04:50:54 +0000 (20:50 -0800)
committerFangrui Song <i@maskray.me>
Sun, 1 Jan 2023 04:50:54 +0000 (20:50 -0800)
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 https://github.com/llvm/llvm-project/issues/59769

Differential Revision: https://reviews.llvm.org/D140813

lld/ELF/Arch/X86.cpp
lld/test/ELF/i386-tls-gdiele.s
lld/test/ELF/i386-tls-opt.s

index eb068f4..8d4f258 100644 (file)
@@ -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.
     //
@@ -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)) {
@@ -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));
 }
index f349975..e698088 100644 (file)
@@ -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: ]
 
 // 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
@@ -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)
index 4ca640a..9cb63f6 100644 (file)
@@ -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
@@ -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