-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[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
Conversation
@llvm/pr-subscribers-lld @llvm/pr-subscribers-lld-elf Author: Feng Zou (fzou1) ChangesFor
add
in #117277. Linker can treat R_X86_64_CODE_6_GOTTPOFF as R_X86_64_GOTTPOFF or convert the instructions above to
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 Full diff: https://github.com/llvm/llvm-project/pull/117675.diff 2 Files Affected:
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
|
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
a8f2fa2
to
8d56798
Compare
Done. Please help review. Thanks. |
lld/ELF/Arch/X86_64.cpp
Outdated
return; | ||
} | ||
if (loc[-2] == 0x3 || loc[-2] == 0x1) { | ||
// "addq foo@gottpoff(%rip), %reg1, %reg2" -> "addq $foo, %reg1, %reg2" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Updated comments.
lld/ELF/Arch/X86_64.cpp
Outdated
// "{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) |
There was a problem hiding this comment.
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];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lld/ELF/Arch/X86_64.cpp
Outdated
// "{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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
lld/ELF/Arch/X86_64.cpp
Outdated
// 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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
lld/ELF/Arch/X86_64.cpp
Outdated
} 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!"; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this 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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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! |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
@MaskRay , I've addressed the comments above. any further comments? Thanks. |
@@ -0,0 +1,36 @@ | |||
# REQUIRES: x86 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. Thanks.
lld/test/ELF/x86-64-tls-ie-err.s
Outdated
|
||
andq 0(%rip), %rax, %r16 | ||
.reloc .-4, R_X86_64_CODE_6_GOTTPOFF, tls0-4 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
delete trailing blank line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
@KanRobert , @MaskRay , Thank you for your review. |
For
add
in #117277.
Linker can treat R_X86_64_CODE_6_GOTTPOFF as R_X86_64_GOTTPOFF or convert the instructions above to
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