Raland D87318 [LLD][PowerPC] Add support for R_PPC64_GOT_TLSGD_PCREL34 used in TLS...
authorFangrui Song <i@maskray.me>
Thu, 1 Oct 2020 19:36:08 +0000 (12:36 -0700)
committerFangrui Song <i@maskray.me>
Thu, 1 Oct 2020 19:36:33 +0000 (12:36 -0700)
Add Thread Local Storage support for the 34 bit relocation R_PPC64_GOT_TLSGD_PCREL34 used in General Dynamic.

The compiler will produce code that looks like:
```
pla r3, x@got@tlsgd@pcrel            R_PPC64_GOT_TLSGD_PCREL34
bl __tls_get_addr@notoc(x@tlsgd)     R_PPC64_TLSGD
                                     R_PPC64_REL24_NOTOC
```
LLD should be able to correctly compute the relocation for  R_PPC64_GOT_TLSGD_PCREL34 as well as do the following two relaxations where possible:
General Dynamic to Local Exec:
```
paddi r3, r13, x@tprel
nop
```
and General Dynamic to Initial Exec:
```
pld r3, x@got@tprel@pcrel
add r3, r3, r13
```
Note:
This patch adds support for the PC Relative (no TOC) version of General Dynamic on top of the existing support for the TOC version of General Dynamic.
The ABI does not provide any way to tell by looking only at the relocation `R_PPC64_TLSGD` when it is being used in a TOC instruction sequence or and when it is being used in a no TOC sequence. The TOC sequence should always be 4 byte aligned. This patch adds one to the offset of the relocation when it is being used in a no TOC sequence. In this way LLD can tell by looking at the alignment of the offset of `R_PPC64_TLSGD` whether or not it is being used as part of a TOC or no TOC sequence.

Reviewed By: NeHuang, sfertile, MaskRay

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

lld/ELF/Arch/PPC64.cpp
lld/ELF/Relocations.cpp
lld/test/ELF/ppc64-tls-pcrel-gd.s [new file with mode: 0644]

index 06dd863..2e7b20d 100644 (file)
@@ -727,15 +727,38 @@ void PPC64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
     writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13
     relocateNoSym(loc, R_PPC64_TPREL16_HA, val);
     break;
-  case R_PPC64_TLSGD:
-    write32(loc, NOP);
-    write32(loc + 4, 0x38630000); // addi r3, r3
-    // Since we are relocating a half16 type relocation and Loc + 4 points to
-    // the start of an instruction we need to advance the buffer by an extra
-    // 2 bytes on BE.
-    relocateNoSym(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0),
-                  R_PPC64_TPREL16_LO, val);
+  case R_PPC64_GOT_TLSGD_PCREL34:
+    // Relax from paddi r3, 0, x@got@tlsgd@pcrel, 1 to
+    //            paddi r3, r13, x@tprel, 0
+    writePrefixedInstruction(loc, 0x06000000386d0000);
+    relocateNoSym(loc, R_PPC64_TPREL34, val);
+    break;
+  case R_PPC64_TLSGD: {
+    // PC Relative Relaxation:
+    // Relax from bl __tls_get_addr@notoc(x@tlsgd) to
+    //            nop
+    // TOC Relaxation:
+    // Relax from bl __tls_get_addr(x@tlsgd)
+    //            nop
+    // to
+    //            nop
+    //            addi r3, r3, x@tprel@l
+    const uintptr_t locAsInt = reinterpret_cast<uintptr_t>(loc);
+    if (locAsInt % 4 == 0) {
+      write32(loc, NOP);            // nop
+      write32(loc + 4, 0x38630000); // addi r3, r3
+      // Since we are relocating a half16 type relocation and Loc + 4 points to
+      // the start of an instruction we need to advance the buffer by an extra
+      // 2 bytes on BE.
+      relocateNoSym(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0),
+                    R_PPC64_TPREL16_LO, val);
+    } else if (locAsInt % 4 == 1) {
+      write32(loc - 1, NOP);
+    } else {
+      errorOrWarn("R_PPC64_TLSGD has unexpected byte alignment");
+    }
     break;
+  }
   default:
     llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
   }
@@ -947,6 +970,8 @@ RelExpr PPC64::getRelExpr(RelType type, const Symbol &s,
   case R_PPC64_GOT_TLSGD16_HI:
   case R_PPC64_GOT_TLSGD16_LO:
     return R_TLSGD_GOT;
+  case R_PPC64_GOT_TLSGD_PCREL34:
+    return R_TLSGD_PC;
   case R_PPC64_GOT_TLSLD16:
   case R_PPC64_GOT_TLSLD16_HA:
   case R_PPC64_GOT_TLSLD16_HI:
@@ -1261,6 +1286,7 @@ void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     break;
   case R_PPC64_PCREL34:
   case R_PPC64_GOT_PCREL34:
+  case R_PPC64_GOT_TLSGD_PCREL34:
   case R_PPC64_GOT_TPREL_PCREL34:
   case R_PPC64_TPREL34: {
     const uint64_t si0Mask = 0x00000003ffff0000;
@@ -1340,7 +1366,8 @@ RelExpr PPC64::adjustRelaxExpr(RelType type, const uint8_t *data,
     if ((readPrefixedInstruction(data) & 0xfc000000) == 0xe4000000)
       return R_PPC64_RELAX_GOT_PC;
   }
-  if (expr == R_RELAX_TLS_GD_TO_IE)
+
+  if (type != R_PPC64_GOT_TLSGD_PCREL34 && expr == R_RELAX_TLS_GD_TO_IE)
     return R_RELAX_TLS_GD_TO_IE_GOT_OFF;
   if (expr == R_RELAX_TLS_LD_TO_LE)
     return R_RELAX_TLS_LD_TO_LE_ABS;
@@ -1381,10 +1408,35 @@ void PPC64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
     relocateNoSym(loc, R_PPC64_GOT_TPREL16_LO_DS, val);
     return;
   }
-  case R_PPC64_TLSGD:
-    write32(loc, NOP);            // bl __tls_get_addr(sym@tlsgd) --> nop
-    write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13
+  case R_PPC64_GOT_TLSGD_PCREL34: {
+    // Relax from paddi r3, 0, sym@got@tlsgd@pcrel, 1 to
+    //            pld r3, sym@got@tprel@pcrel
+    writePrefixedInstruction(loc, 0x04100000e4600000);
+    relocateNoSym(loc, R_PPC64_GOT_TPREL_PCREL34, val);
+    return;
+  }
+  case R_PPC64_TLSGD: {
+    // PC Relative Relaxation:
+    // Relax from bl __tls_get_addr@notoc(x@tlsgd) to
+    //            nop
+    // TOC Relaxation:
+    // Relax from bl __tls_get_addr(x@tlsgd)
+    //            nop
+    // to
+    //            nop
+    //            add r3, r3, r13
+    const uintptr_t locAsInt = reinterpret_cast<uintptr_t>(loc);
+    if (locAsInt % 4 == 0) {
+      write32(loc, NOP);            // bl __tls_get_addr(sym@tlsgd) --> nop
+      write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13
+    } else if (locAsInt % 4 == 1) {
+      // bl __tls_get_addr(sym@tlsgd) --> add r3, r3, r13
+      write32(loc - 1, 0x7c636a14);
+    } else {
+      errorOrWarn("R_PPC64_TLSGD has unexpected byte alignment");
+    }
     return;
+  }
   default:
     llvm_unreachable("unsupported relocation for TLS GD to IE relaxation");
   }
index 4c6a70d..ea6aa3c 100644 (file)
@@ -1357,6 +1357,19 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
     if (type == R_PPC64_TOC16_LO && sym.isSection() && isa<Defined>(sym) &&
         cast<Defined>(sym).section->name == ".toc")
       ppc64noTocRelax.insert({&sym, addend});
+
+    if (type == R_PPC64_TLSGD && expr == R_TLSDESC_CALL) {
+      if (i == end) {
+        errorOrWarn("R_PPC64_TLSGD may not be the last relocation" +
+                    getLocation(sec, sym, offset));
+        return;
+      }
+
+      // Offset the 4-byte aligned R_PPC64_TLSGD by one byte in the NOTOC case,
+      // so we can discern it later from the toc-case.
+      if (i->getType(/*isMips64EL=*/false) == R_PPC64_REL24_NOTOC)
+        ++offset;
+    }
   }
 
   // Relax relocations.
diff --git a/lld/test/ELF/ppc64-tls-pcrel-gd.s b/lld/test/ELF/ppc64-tls-pcrel-gd.s
new file mode 100644 (file)
index 0000000..6dc8b80
--- /dev/null
@@ -0,0 +1,94 @@
+# REQUIRES: ppc
+# RUN: split-file %s %t
+
+# RUN: llvm-mc -filetype=obj -triple=powerpc64le %t/asm -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=powerpc64le %t/defs -o %t-defs.o
+# RUN: ld.lld --shared %t-defs.o --soname=t-defs -o %t-defs.so
+# RUN: ld.lld -T %t/lds --shared %t.o -o %t-gd.so
+# RUN: ld.lld -T %t/lds %t.o %t-defs.so -o %t-gdtoie
+# RUN: ld.lld -T %t/lds %t.o %t-defs.o -o %t-gdtole
+
+# RUN: llvm-readelf -r %t-gd.so | FileCheck %s --check-prefix=GD-RELOC
+# RUN: llvm-readelf -s %t-gd.so | FileCheck %s --check-prefix=GD-SYM
+# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t-gd.so | FileCheck %s --check-prefix=GD
+
+# RUN: llvm-readelf -r %t-gdtoie | FileCheck %s --check-prefix=GDTOIE-RELOC
+# RUN: llvm-readelf -s %t-gdtoie | FileCheck %s --check-prefix=GDTOIE-SYM
+# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t-gdtoie | FileCheck %s --check-prefix=GDTOIE
+
+# RUN: llvm-readelf -r %t-gdtole | FileCheck %s --check-prefix=GDTOLE-RELOC
+# RUN: llvm-readelf -s %t-gdtole | FileCheck %s --check-prefix=GDTOLE-SYM
+# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t-gdtole | FileCheck %s --check-prefix=GDTOLE
+
+## This test checks the General Dynamic PC Relative TLS implementation for lld.
+## GD - General Dynamic with no relaxation possible
+## GDTOIE - General Dynamic relaxed to Initial Exec
+## GDTOLE - General Dynamic relaxed to Local Exec
+
+#--- lds
+SECTIONS {
+  .text_addr 0x1001000 : { *(.text_addr) }
+}
+
+#--- defs
+.section .tbss,"awT",@nobits
+.globl  x
+x:
+  .long 0
+.globl  y
+y:
+  .long 0
+
+#--- asm
+
+# GD-RELOC: Relocation section '.rela.dyn' at offset 0x100b8 contains 4 entries:
+# GD-RELOC: 0000000001001160  0000000200000044 R_PPC64_DTPMOD64       0000000000000000 x + 0
+# GD-RELOC: 0000000001001168  000000020000004e R_PPC64_DTPREL64       0000000000000000 x + 0
+# GD-RELOC: 0000000001001170  0000000300000044 R_PPC64_DTPMOD64       0000000000000000 y + 0
+# GD-RELOC: 0000000001001178  000000030000004e R_PPC64_DTPREL64       0000000000000000 y + 0
+
+# GD-SYM:   Symbol table '.dynsym' contains 4 entries:
+# GD-SYM:   2: 0000000000000000     0 TLS     GLOBAL DEFAULT   UND x
+# GD-SYM:   3: 0000000000000000     0 TLS     GLOBAL DEFAULT   UND y
+
+
+# GDTOIE-RELOC: Relocation section '.rela.dyn' at offset 0x{{.*}} contains 2 entries:
+# GDTOIE-RELOC: 00000000010010e0  0000000200000049 R_PPC64_TPREL64        0000000000000000 x + 0
+# GDTOIE-RELOC: 00000000010010e8  0000000300000049 R_PPC64_TPREL64        0000000000000000 y + 0
+
+# GDTOIE-SYM: Symbol table '.dynsym' contains 4 entries:
+# GDTOIE-SYM:   2: 0000000000000000     0 TLS     GLOBAL DEFAULT   UND x
+# GDTOIE-SYM:   3: 0000000000000000     0 TLS     GLOBAL DEFAULT   UND y
+
+
+# GDTOLE-RELOC: There are no relocations in this file.
+
+# GDTOLE-SYM: Symbol table '.symtab' contains 5 entries:
+# GDTOLE-SYM: 3: 0000000000000000     0 TLS     GLOBAL DEFAULT     3 x
+# GDTOLE-SYM: 4: 0000000000000004     0 TLS     GLOBAL DEFAULT     3 y
+
+# GD-LABEL: <GDTwoVal>:
+# GD-NEXT:    paddi 3, 0, 352, 1
+# GD-NEXT:    bl
+# GD-NEXT:    paddi 3, 0, 356, 1
+# GD-NEXT:    bl
+# GD-NEXT:    blr
+# GDTOIE-LABEL: <GDTwoVal>:
+# GDTOIE-NEXT:    pld 3, 224(0), 1
+# GDTOIE-NEXT:    add 3, 3, 13
+# GDTOIE-NEXT:    pld 3, 220(0), 1
+# GDTOIE-NEXT:    add 3, 3, 13
+# GDTOIE-NEXT:    blr
+# GDTOLE-LABEL: <GDTwoVal>:
+# GDTOLE-NEXT:    paddi 3, 13, -28672, 0
+# GDTOLE-NEXT:    nop
+# GDTOLE-NEXT:    paddi 3, 13, -28668, 0
+# GDTOLE-NEXT:    nop
+# GDTOLE-NEXT:    blr
+.section .text_addr, "ax", %progbits
+GDTwoVal:
+  paddi 3, 0, x@got@tlsgd@pcrel, 1
+  bl __tls_get_addr@notoc(x@tlsgd)
+  paddi 3, 0, y@got@tlsgd@pcrel, 1
+  bl __tls_get_addr@notoc(y@tlsgd)
+  blr