Skip to content

[X86][LLD] Handle R_X86_64_CODE_6_GOTTPOFF relocation type #117675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Dec 5, 2024

Conversation

fzou1
Copy link
Contributor

@fzou1 fzou1 commented Nov 26, 2024

For

add %reg1, name@GOTTPOFF(%rip), %reg2
add name@GOTTPOFF(%rip), %reg1, %reg2
{nf} add %reg1, name@GOTTPOFF(%rip), %reg2
{nf} add name@GOTTPOFF(%rip), %reg1, %reg2
{nf} add name@GOTTPOFF(%rip), %reg

add

R_X86_64_CODE_6_GOTTPOFF = 50

in #117277.

Linker can treat R_X86_64_CODE_6_GOTTPOFF as R_X86_64_GOTTPOFF or convert the instructions above to

add $name@tpoff, %reg1, %reg2
add $name@tpoff, %reg1, %reg2
{nf} add $name@tpoff, %reg1, %reg2
{nf} add $name@tpoff, %reg1, %reg2
{nf} add $name@tpoff, %reg

if the first byte of the instruction at the relocation offset - 6 is 0x62 (namely, encoded w/EVEX prefix) when possible.

Binutils patch: bminor/binutils-gdb@5bc71c2
Binutils mailthread: https://sourceware.org/pipermail/binutils/2024-February/132351.html
ABI discussion: https://groups.google.com/g/x86-64-abi/c/FhEZjCtDLFw/m/VHDjN4orAgAJ
Blog: https://kanrobert.github.io/rfc/All-about-APX-relocation

@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2024

@llvm/pr-subscribers-lld

@llvm/pr-subscribers-lld-elf

Author: Feng Zou (fzou1)

Changes

For

add %reg1, name@<!-- -->GOTTPOFF(%rip), %reg2
add name@<!-- -->GOTTPOFF(%rip), %reg1, %reg2
{nf} add %reg1, name@<!-- -->GOTTPOFF(%rip), %reg2
{nf} add name@<!-- -->GOTTPOFF(%rip), %reg1, %reg2
{nf} add name@<!-- -->GOTTPOFF(%rip), %reg

add

R_X86_64_CODE_6_GOTTPOFF = 50

in #117277.

Linker can treat R_X86_64_CODE_6_GOTTPOFF as R_X86_64_GOTTPOFF or convert the instructions above to

add $name@<!-- -->tpoff, %reg1, %reg2
add $name@<!-- -->tpoff, %reg1, %reg2
{nf} add $name@<!-- -->tpoff, %reg1, %reg2
{nf} add $name@<!-- -->tpoff, %reg1, %reg2
{nf} add $name@<!-- -->tpoff, %reg

if the first byte of the instruction at the relocation offset - 6 is 0x62 (namely, encoded w/EVEX prefix) when possible.

Binutils patch: bminor/binutils-gdb@5bc71c2
Binutils mailthread: https://sourceware.org/pipermail/binutils/2024-February/132351.html
ABI discussion: https://groups.google.com/g/x86-64-abi/c/FhEZjCtDLFw/m/VHDjN4orAgAJ
Blog: https://kanrobert.github.io/rfc/All-about-APX-relocation


Full diff: https://github.com/llvm/llvm-project/pull/117675.diff

2 Files Affected:

  • (modified) lld/ELF/Arch/X86_64.cpp (+58-31)
  • (modified) lld/test/ELF/tls-opt.s (+17)
diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index 2dcce5c224d5d6..749ab7ca7db7e7 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -396,6 +396,7 @@ RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
   case R_X86_64_REX_GOTPCRELX:
   case R_X86_64_CODE_4_GOTPCRELX:
   case R_X86_64_GOTTPOFF:
+  case R_X86_64_CODE_6_GOTTPOFF:
     return R_GOT_PC;
   case R_X86_64_GOTOFF64:
     return R_GOTPLTREL;
@@ -547,44 +548,68 @@ void X86_64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
   }
 }
 
-// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
-// R_X86_64_TPOFF32 so that it does not use GOT.
+// In some conditions, R_X86_64_GOTTPOFF/R_X86_64_CODE_6_GOTTPOFF relocation can
+// be optimized to R_X86_64_TPOFF32 so that it does not use GOT.
 void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
                             uint64_t val) const {
   uint8_t *inst = loc - 3;
   uint8_t reg = loc[-1] >> 3;
   uint8_t *regSlot = loc - 1;
 
-  // Note that ADD with RSP or R12 is converted to ADD instead of LEA
-  // because LEA with these registers needs 4 bytes to encode and thus
-  // wouldn't fit the space.
-
-  if (memcmp(inst, "\x48\x03\x25", 3) == 0) {
-    // "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp"
-    memcpy(inst, "\x48\x81\xc4", 3);
-  } else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) {
-    // "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12"
-    memcpy(inst, "\x49\x81\xc4", 3);
-  } else if (memcmp(inst, "\x4c\x03", 2) == 0) {
-    // "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]"
-    memcpy(inst, "\x4d\x8d", 2);
-    *regSlot = 0x80 | (reg << 3) | reg;
-  } else if (memcmp(inst, "\x48\x03", 2) == 0) {
-    // "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg"
-    memcpy(inst, "\x48\x8d", 2);
-    *regSlot = 0x80 | (reg << 3) | reg;
-  } else if (memcmp(inst, "\x4c\x8b", 2) == 0) {
-    // "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]"
-    memcpy(inst, "\x49\xc7", 2);
-    *regSlot = 0xc0 | reg;
-  } else if (memcmp(inst, "\x48\x8b", 2) == 0) {
-    // "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg"
-    memcpy(inst, "\x48\xc7", 2);
-    *regSlot = 0xc0 | reg;
+  if (rel.type == R_X86_64_GOTTPOFF) {
+    // Note that ADD with RSP or R12 is converted to ADD instead of LEA
+    // because LEA with these registers needs 4 bytes to encode and thus
+    // wouldn't fit the space.
+
+    if (memcmp(inst, "\x48\x03\x25", 3) == 0) {
+      // "addq foo@gottpoff(%rip),%rsp" -> "addq $foo,%rsp"
+      memcpy(inst, "\x48\x81\xc4", 3);
+    } else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) {
+      // "addq foo@gottpoff(%rip),%r12" -> "addq $foo,%r12"
+      memcpy(inst, "\x49\x81\xc4", 3);
+    } else if (memcmp(inst, "\x4c\x03", 2) == 0) {
+      // "addq foo@gottpoff(%rip),%r[8-15]" -> "leaq foo(%r[8-15]),%r[8-15]"
+      memcpy(inst, "\x4d\x8d", 2);
+      *regSlot = 0x80 | (reg << 3) | reg;
+    } else if (memcmp(inst, "\x48\x03", 2) == 0) {
+      // "addq foo@gottpoff(%rip),%reg -> "leaq foo(%reg),%reg"
+      memcpy(inst, "\x48\x8d", 2);
+      *regSlot = 0x80 | (reg << 3) | reg;
+    } else if (memcmp(inst, "\x4c\x8b", 2) == 0) {
+      // "movq foo@gottpoff(%rip),%r[8-15]" -> "movq $foo,%r[8-15]"
+      memcpy(inst, "\x49\xc7", 2);
+      *regSlot = 0xc0 | reg;
+    } else if (memcmp(inst, "\x48\x8b", 2) == 0) {
+      // "movq foo@gottpoff(%rip),%reg" -> "movq $foo,%reg"
+      memcpy(inst, "\x48\xc7", 2);
+      *regSlot = 0xc0 | reg;
+    } else {
+      ErrAlways(ctx)
+          << getErrorLoc(ctx, loc - 3)
+          << "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only";
+    }
+  } else if (rel.type == R_X86_64_CODE_6_GOTTPOFF) {
+    if (loc[-6] != 0x62) {
+      Err(ctx) << getErrorLoc(ctx, loc - 6)
+               << "Invalid prefix with R_X86_64_CODE_6_GOTTPOFF!";
+      return;
+    }
+    if (loc[-2] == 0x3 || loc[-2] == 0x1) {
+      // "addq foo@gottpoff(%rip), %reg1, %reg2" -> "addq $foo, %reg1, %reg2"
+      loc[-2] = 0x81;
+      // Move R bits to B bits in EVEX payloads and ModRM byte.
+      if ((loc[-5] & (1 << 7)) == 0)
+        loc[-5] = (loc[-5] | (1 << 7)) & ~(1 << 5);
+      if ((loc[-5] & (1 << 4)) == 0)
+        loc[-5] = loc[-5] | (1 << 4) | (1 << 3);
+      *regSlot = 0xc0 | reg;
+    } else {
+      Err(ctx)
+          << getErrorLoc(ctx, loc - 6)
+          << "R_X86_64_CODE_6_GOTTPOFF must be used in ADDQ instructions only";
+    }
   } else {
-    ErrAlways(ctx)
-        << getErrorLoc(ctx, loc - 3)
-        << "R_X86_64_GOTTPOFF must be used in MOVQ or ADDQ instructions only";
+    llvm_unreachable("Unsupported relocation type!");
   }
 
   // The original code used a PC relative relocation.
@@ -741,6 +766,7 @@ int64_t X86_64::getImplicitAddend(const uint8_t *buf, RelType type) const {
   case R_X86_64_CODE_4_GOTPCRELX:
   case R_X86_64_PC32:
   case R_X86_64_GOTTPOFF:
+  case R_X86_64_CODE_6_GOTTPOFF:
   case R_X86_64_PLT32:
   case R_X86_64_TLSGD:
   case R_X86_64_TLSLD:
@@ -850,6 +876,7 @@ void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
     }
     break;
   case R_X86_64_GOTTPOFF:
+  case R_X86_64_CODE_6_GOTTPOFF:
     if (rel.expr == R_RELAX_TLS_IE_TO_LE) {
       relaxTlsIeToLe(loc, rel, val);
     } else {
diff --git a/lld/test/ELF/tls-opt.s b/lld/test/ELF/tls-opt.s
index ce90ba4f869ce4..784f575b69ede5 100644
--- a/lld/test/ELF/tls-opt.s
+++ b/lld/test/ELF/tls-opt.s
@@ -20,6 +20,14 @@
 // DISASM-NEXT:   leaq -4(%r15), %r15
 // DISASM-NEXT:   addq $-4, %rsp
 // DISASM-NEXT:   addq $-4, %r12
+// DISASM-NEXT:   addq $-10, %r16, %r16
+// DISASM-NEXT:   addq $-10, %r16, %r20
+// DISASM-NEXT:   addq $-10, %r16, %rax
+// DISASM-NEXT:   addq $-10, %rax, %r16
+// DISASM-NEXT:   addq $-10, %r8, %r16
+// DISASM-NEXT:   addq $-10, %rax, %r12
+// DISASM-NEXT:   {nf} addq $-10, %r8, %r16
+// DISASM-NEXT:   {nf} addq $-10, %rax, %r12
 
 // LD to LE:
 // DISASM-NEXT:   movq %fs:0, %rax
@@ -69,6 +77,15 @@ _start:
  addq tls1@GOTTPOFF(%rip), %r15
  addq tls1@GOTTPOFF(%rip), %rsp
  addq tls1@GOTTPOFF(%rip), %r12
+# NDD
+ addq tls0@GOTTPOFF(%rip), %r16, %r16
+ addq tls0@GOTTPOFF(%rip), %r16, %r20
+ addq tls0@GOTTPOFF(%rip), %r16, %rax
+ addq tls0@GOTTPOFF(%rip), %rax, %r16
+ addq %r8, tls0@GOTTPOFF(%rip), %r16
+ addq tls0@GOTTPOFF(%rip), %rax, %r12
+ {nf} addq %r8, tls0@GOTTPOFF(%rip), %r16
+ {nf} addq tls0@GOTTPOFF(%rip), %rax, %r12
 
  // LD to LE
  leaq tls0@tlsld(%rip), %rdi

@fzou1
Copy link
Contributor Author

fzou1 commented Nov 26, 2024

I'll rebase after #116634 is merged.

For

    add %reg1, name@GOTTPOFF(%rip), %reg2
    add name@GOTTPOFF(%rip), %reg1, %reg2
    {nf} add %reg1, name@GOTTPOFF(%rip), %reg2
    {nf} add name@GOTTPOFF(%rip), %reg1, %reg2
    {nf} add name@GOTTPOFF(%rip), %reg

add

    R_X86_64_CODE_6_GOTTPOFF = 50

in llvm#117277.

Linker can treat R_X86_64_CODE_6_GOTTPOFF as R_X86_64_GOTTPOFF or convert the
instructions above to

    add $name@tpoff, %reg1, %reg2
    add $name@tpoff, %reg1, %reg2
    {nf} add $name@tpoff, %reg1, %reg2
    {nf} add $name@tpoff, %reg1, %reg2
    {nf} add $name@tpoff, %reg

if the first byte of the instruction at the relocation offset - 6 is 0x62
(namely, encoded w/EVEX prefix) when possible.

Binutils patch: bminor/binutils-gdb@5bc71c2
Binutils mailthread: https://sourceware.org/pipermail/binutils/2024-February/132351.html
ABI discussion: https://groups.google.com/g/x86-64-abi/c/FhEZjCtDLFw/m/VHDjN4orAgAJ
Blog: https://kanrobert.github.io/rfc/All-about-APX-relocation
@fzou1 fzou1 force-pushed the lld-code-6-gottpoff branch from a8f2fa2 to 8d56798 Compare November 27, 2024 02:39
@fzou1
Copy link
Contributor Author

fzou1 commented Nov 27, 2024

I'll rebase after #116634 is merged.

Done. Please help review. Thanks.

return;
}
if (loc[-2] == 0x3 || loc[-2] == 0x1) {
// "addq foo@gottpoff(%rip), %reg1, %reg2" -> "addq $foo, %reg1, %reg2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is incomplete. You handle several variants here but only mention ADD64rm_ND.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Updated comments.

// "{nf} addq name@GOTTPOFF(%rip), %reg" -> "{nf} addq $foo, %reg"
loc[-2] = 0x81;
// Move R bits to B bits in EVEX payloads and ModRM byte.
if ((loc[-5] & (1 << 7)) == 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define a variable for loc[-5]?

const uint8_t evex_payload0 = loc[-5];

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// "{nf} addq name@GOTTPOFF(%rip), %reg" -> "{nf} addq $foo, %reg"
loc[-2] = 0x81;
// Move R bits to B bits in EVEX payloads and ModRM byte.
if ((loc[-5] & (1 << 7)) == 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to check NF and ND bit too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// Move R bits to B bits in EVEX payloads and ModRM byte.
if ((loc[-5] & (1 << 7)) == 0)
loc[-5] = (loc[-5] | (1 << 7)) & ~(1 << 5);
if ((loc[-5] & (1 << 4)) == 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove the if condition at line 644 and 646?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. We need to check if the R bits are 0 (reverted) and set corresponding B bits.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, there is a problem with the newly added relocation optimization. Unlike the relocation w/ REX prefix, we don't check the bits strictly. For example, we don't check if the W, X, pp and zero bits in EVEX payload2. This brings an potential issue that the linker hides the bug of assembler.

Copy link
Contributor

@KanRobert KanRobert Nov 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can easily edit the binary in obj file to produce an invalid encoding with R_X86_64_CODE_6_GOTTPOFF relocation, e.g. set the zero bits in payload2 to ones. And then feed the modified the obj to linker, it would error nothing with this implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean it's better to check the bits strictly as for REX2 prefix? Since we may not use EGPR in instructions with NDD/NF, so it's not necessary to check X bits. I've added check for W bits. Seemed it's complicated to check pp bits. I'll take a look and will add later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Please help review. Thanks.

Copy link
Contributor

@KanRobert KanRobert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

} else if (rel.type == R_X86_64_CODE_6_GOTTPOFF) {
if (loc[-6] != 0x62) {
Err(ctx) << getErrorLoc(ctx, loc - 6)
<< "Invalid prefix with R_X86_64_CODE_6_GOTTPOFF!";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invalid. we don't capitalize error messages

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new error messages must be tested

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

loc[-5] = evexPayload0 | (1 << 4) | (1 << 3);
*regSlot = 0xc0 | reg;
} else {
Err(ctx) << getErrorLoc(ctx, loc - 6)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new error messages must be tested

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.




# RUN: yaml2obj --docnum=2 %s -o %t2.o
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding negative tests but YAML is not very readable. You can use assembly tests with .reloc ., R_.... See x86-64-reloc-32.s

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added one more assembly test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the new assembly test okay?


# RUN: yaml2obj --docnum=2 %s -o %t2.o
# RUN: not ld.lld %t2.o -o /dev/null 2>&1 | FileCheck --check-prefix=ERR2 %s
# ERR2: invalid prefix with R_X86_64_CODE_4_GOTTPOFF!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We always test error: in error messages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@fzou1
Copy link
Contributor Author

fzou1 commented Dec 4, 2024

@MaskRay , I've addressed the comments above. any further comments? Thanks.

@@ -0,0 +1,36 @@
# REQUIRES: x86
Copy link
Member

@MaskRay MaskRay Dec 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when testing a specific feature, we place negative tests beside the positive tests.

Perhaps move this to ELF/x86-64-tls-ie-err.s

These x86-64 TLS tests are not very well organized

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to ELF/x86-64-tls-ie-err.s.

@@ -0,0 +1,36 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-DFILE=%t.o (see systemz-reloc-pcdbl.s as an example) and expand {{.*}} to [[FILE]] below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Thanks.


andq 0(%rip), %rax, %r16
.reloc .-4, R_X86_64_CODE_6_GOTTPOFF, tls0-4

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete trailing blank line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@fzou1 fzou1 merged commit 636beb6 into llvm:main Dec 5, 2024
8 checks passed
@fzou1 fzou1 deleted the lld-code-6-gottpoff branch December 5, 2024 08:26
@fzou1
Copy link
Contributor Author

fzou1 commented Dec 5, 2024

@KanRobert , @MaskRay , Thank you for your review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants