From 2c841f491165f631e591c12d331b6c4f0e8d2b2a Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Fri, 24 Jul 2026 07:01:35 +0800
Subject: [PATCH v2] x86: Generate PLT32 relocation for ".long foo@PLT - .L4"
LLVM assembler supports directives like ".long foo@PLT - .L4" for i386
and x86-64. Implement the same feature to generate PLT32 relocation
for directives like ".long foo@PLT - .L4" so that PLT entries are used
to resolve the PC32 relocation against function symbols.
bfd/
PR gas/34423
* elf32-i386.c (elf_i386_reloc_type_lookup): Handle
BFD_RELOC_386_PC32_TO_PLT32.
* elf64-x86-64.c (x86_64_reloc_map): Add
BFD_RELOC_X86_64_PC32_TO_PLT32.
* reloc.c (bfd_reloc_code_real): Add BFD_RELOC_386_PC32_TO_PLT32
and BFD_RELOC_X86_64_PC32_TO_PLT32.
* bfd-in2.h: Regenerated.
* libbfd.h: Likewise.
gas/
PR gas/34423
* config/tc-i386.c (x86_cons): Return
BFD_RELOC_X86_64_PC32_TO_PLT32 or BFD_RELOC_386_PC32_TO_PLT32
for directives like ".long foo@PLT - .L4".
(md_apply_fix): Compute addend for BFD_RELOC_386_PC32_TO_PLT32.
(tc_gen_reloc): Handle BFD_RELOC_X86_64_PC32_TO_PLT32 and
BFD_RELOC_386_PC32_TO_PLT32. Compute addend like
BFD_RELOC_32_PCREL for BFD_RELOC_X86_64_PC32_TO_PLT32.
* testsuite/gas/i386/i386.exp: Run plt test.
* testsuite/gas/i386/ilp32/reloc64.l: Updated.
* testsuite/gas/i386/ilp32/reloc64.s: Replace ".long xtrn@plt - ."
with ".long xtrn@plt - ptr".
* testsuite/gas/i386/ilp32/x86-64-jump-table.d: New file.
* testsuite/gas/i386/plt.d: New file.
* testsuite/gas/i386/plt.s: Likewise.
* testsuite/gas/i386/reloc32.s: Replace ".long xtrn@plt - ."
with ".long xtrn@plt - ptr".
* testsuite/gas/i386/reloc64.l: Updated.
* testsuite/gas/i386/reloc64.s: Replace ".long xtrn@plt - ." with
".long xtrn@plt - _start".
* testsuite/gas/i386/x86-64-jump-table.d: New file.
* testsuite/gas/i386/x86-64-jump-table.d: Likewise.
* testsuite/gas/i386/x86-64-jump-table.s: Likewise.
* testsuite/gas/i386/x86-64.exp: Run x86-64-jump-table.
ld/
PR gas/34423
* testsuite/ld-x86-64/pr34423.c: New file.
* testsuite/ld-x86-64/x86-64-jump-table.s: Likewise.
* testsuite/ld-x86-64/x86-64.exp: Run gas/34423 tests.
Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
bfd/bfd-in2.h | 2 +
bfd/elf32-i386.c | 4 ++
bfd/elf64-x86-64.c | 1 +
bfd/libbfd.h | 2 +
bfd/reloc.c | 4 ++
gas/config/tc-i386.c | 36 +++++++++++++--
gas/testsuite/gas/i386/i386.exp | 2 +
gas/testsuite/gas/i386/ilp32/reloc64.l | 1 +
gas/testsuite/gas/i386/ilp32/reloc64.s | 2 +-
.../gas/i386/ilp32/x86-64-jump-table.d | 4 ++
gas/testsuite/gas/i386/plt.d | 14 ++++++
gas/testsuite/gas/i386/plt.s | 7 +++
gas/testsuite/gas/i386/reloc32.s | 2 +-
gas/testsuite/gas/i386/reloc64.l | 1 +
gas/testsuite/gas/i386/reloc64.s | 2 +-
gas/testsuite/gas/i386/x86-64-jump-table.d | 29 ++++++++++++
gas/testsuite/gas/i386/x86-64-jump-table.s | 31 +++++++++++++
gas/testsuite/gas/i386/x86-64.exp | 2 +
ld/testsuite/ld-x86-64/pr34423.c | 46 +++++++++++++++++++
ld/testsuite/ld-x86-64/x86-64-jump-table.s | 30 ++++++++++++
ld/testsuite/ld-x86-64/x86-64.exp | 16 +++++++
21 files changed, 230 insertions(+), 8 deletions(-)
create mode 100644 gas/testsuite/gas/i386/ilp32/x86-64-jump-table.d
create mode 100644 gas/testsuite/gas/i386/plt.d
create mode 100644 gas/testsuite/gas/i386/plt.s
create mode 100644 gas/testsuite/gas/i386/x86-64-jump-table.d
create mode 100644 gas/testsuite/gas/i386/x86-64-jump-table.s
create mode 100644 ld/testsuite/ld-x86-64/pr34423.c
create mode 100644 ld/testsuite/ld-x86-64/x86-64-jump-table.s
@@ -3899,6 +3899,7 @@ enum bfd_reloc_code_real
/* i386/elf relocations. */
BFD_RELOC_386_GOT32,
BFD_RELOC_386_PLT32,
+ BFD_RELOC_386_PC32_TO_PLT32,
BFD_RELOC_386_TLS_TPOFF,
BFD_RELOC_386_TLS_IE,
BFD_RELOC_386_TLS_GOTIE,
@@ -3937,6 +3938,7 @@ enum bfd_reloc_code_real
BFD_RELOC_X86_64_TLSDESC,
BFD_RELOC_X86_64_PC32_BND,
BFD_RELOC_X86_64_PLT32_BND,
+ BFD_RELOC_X86_64_PC32_TO_PLT32,
BFD_RELOC_X86_64_GOTPCRELX,
BFD_RELOC_X86_64_REX_GOTPCRELX,
BFD_RELOC_X86_64_CODE_4_GOTPCRELX,
@@ -331,6 +331,10 @@ elf_i386_reloc_type_lookup (bfd *abfd,
TRACE ("BFD_RELOC_386_GOT32X");
return &elf_howto_table[R_386_GOT32X - R_386_tls_offset];
+ case BFD_RELOC_386_PC32_TO_PLT32:
+ TRACE ("BFD_RELOC_X86_PC32_TO_PLT32");
+ return &elf_howto_table[R_386_PLT32];
+
case BFD_RELOC_VTABLE_INHERIT:
TRACE ("BFD_RELOC_VTABLE_INHERIT");
return &elf_howto_table[R_386_GNU_VTINHERIT - R_386_vt_offset];
@@ -281,6 +281,7 @@ static const struct elf_reloc_map x86_64_reloc_map[] =
{ BFD_RELOC_X86_64_CODE_6_GOTPCRELX, R_X86_64_CODE_6_GOTPCRELX, },
{ BFD_RELOC_X86_64_CODE_6_GOTTPOFF, R_X86_64_CODE_6_GOTTPOFF, },
{ BFD_RELOC_X86_64_CODE_6_GOTPC32_TLSDESC, R_X86_64_CODE_6_GOTPC32_TLSDESC, },
+ { BFD_RELOC_X86_64_PC32_TO_PLT32, R_X86_64_PLT32, },
{ BFD_RELOC_VTABLE_INHERIT, R_X86_64_GNU_VTINHERIT, },
{ BFD_RELOC_VTABLE_ENTRY, R_X86_64_GNU_VTENTRY, },
};
@@ -1430,6 +1430,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
"BFD_RELOC_MN10300_TLS_TPOFF",
"BFD_RELOC_386_GOT32",
"BFD_RELOC_386_PLT32",
+ "BFD_RELOC_386_PC32_TO_PLT32",
"BFD_RELOC_386_TLS_TPOFF",
"BFD_RELOC_386_TLS_IE",
"BFD_RELOC_386_TLS_GOTIE",
@@ -1466,6 +1467,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
"BFD_RELOC_X86_64_TLSDESC",
"BFD_RELOC_X86_64_PC32_BND",
"BFD_RELOC_X86_64_PLT32_BND",
+ "BFD_RELOC_X86_64_PC32_TO_PLT32",
"BFD_RELOC_X86_64_GOTPCRELX",
"BFD_RELOC_X86_64_REX_GOTPCRELX",
"BFD_RELOC_X86_64_CODE_4_GOTPCRELX",
@@ -2319,6 +2319,8 @@ ENUM
BFD_RELOC_386_GOT32
ENUMX
BFD_RELOC_386_PLT32
+ENUMX
+ BFD_RELOC_386_PC32_TO_PLT32
ENUMX
BFD_RELOC_386_TLS_TPOFF
ENUMX
@@ -2394,6 +2396,8 @@ ENUMX
BFD_RELOC_X86_64_PC32_BND
ENUMX
BFD_RELOC_X86_64_PLT32_BND
+ENUMX
+ BFD_RELOC_X86_64_PC32_TO_PLT32
ENUMX
BFD_RELOC_X86_64_GOTPCRELX
ENUMX
@@ -13553,10 +13553,20 @@ x86_cons (expressionS *exp, int size)
|| got_reloc == BFD_RELOC_32_PLT_PCREL)
&& exp->X_op != O_symbol)
{
- char c = *input_line_pointer;
- *input_line_pointer = 0;
- as_bad (_("invalid PLT expression `%s'"), save);
- *input_line_pointer = c;
+ /* Allow directives like ".long foo@PLT - .L4". */
+ if (size == 4
+ && exp->X_op == O_subtract
+ && S_IS_LOCAL (exp->X_op_symbol))
+ got_reloc = (object_64bit
+ ? BFD_RELOC_X86_64_PC32_TO_PLT32
+ : BFD_RELOC_386_PC32_TO_PLT32);
+ else
+ {
+ char c = *input_line_pointer;
+ *input_line_pointer = 0;
+ as_bad (_("invalid PLT expression `%s'"), save);
+ *input_line_pointer = c;
+ }
}
}
}
@@ -16781,6 +16791,11 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
value = -4;
break;
+ case BFD_RELOC_386_PC32_TO_PLT32:
+ value = (seg->vma - fixP->fx_size + fixP->fx_addnumber
+ + md_pcrel_from (fixP));
+ break;
+
case BFD_RELOC_386_TLS_GD:
case BFD_RELOC_386_TLS_LDM:
case BFD_RELOC_386_TLS_IE_32:
@@ -18626,7 +18641,11 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
}
/* Fall through. */
default:
- if (fixp->fx_pcrel)
+ if (fixp->fx_r_type == BFD_RELOC_X86_64_PC32_TO_PLT32)
+ code = BFD_RELOC_X86_64_PC32_TO_PLT32;
+ else if (fixp->fx_r_type == BFD_RELOC_386_PC32_TO_PLT32)
+ code = BFD_RELOC_386_PLT32;
+ else if (fixp->fx_pcrel)
{
switch (fixp->fx_size)
{
@@ -18751,6 +18770,13 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
case BFD_RELOC_X86_64_TLSDESC_CALL:
rel->addend = fixp->fx_offset - fixp->fx_size;
break;
+ case BFD_RELOC_X86_64_PC32_TO_PLT32:
+ /* This came from a directive like ".long foo@PLT - .L4".
+ Generate R_X86_64_PLT32 with addend computed like
+ R_X86_64_PC32 so that PLT entry is used to resolve
+ this PC32 relocation. */
+ code = BFD_RELOC_32_PLT_PCREL;
+ /* Fall through. */
default:
rel->addend = (section->vma
- fixp->fx_size
@@ -792,6 +792,8 @@ if [gas_32_check] then {
run_dump_test "no-got"
+ run_dump_test "plt"
+
run_dump_test "gotx-default"
run_dump_test "no-gotx-default"
@@ -113,6 +113,7 @@
.*:197: Error: .* too large for field of 1 byte at .*
.*:198: Error: .* too large for field of 2 bytes at .*
.*:198: Error: .* too large for field of 1 byte at .*
+.*:190: Error: can't resolve xtrn - _start
.*:201: Error: .* too large for field of 4 bytes at .*
.*:202: Error: .* too large for field of 2 bytes at .*
.*:203: Error: .* too large for field of 2 bytes at .*
@@ -187,7 +187,7 @@ bad .byte xtrn@tpoff
.quad xtrn - 0x80000000
.long xtrn@got - 4
.long xtrn@got + 4
-bad .long xtrn@plt - .
+bad .long xtrn@plt - _start
.text
bad add $x+0x123456789, %rax
new file mode 100644
@@ -0,0 +1,4 @@
+#source: ../x86-64-jump-table.s
+#readelf: -rsW
+#name: x86-64 (ILP32) PIC jump table
+#dump: ../x86-64-jump-table.d
new file mode 100644
@@ -0,0 +1,14 @@
+#as:
+#readelf: -x .rodata -r
+
+Relocation section '.rel.rodata' at offset 0xd4 contains 4 entries:
+ Offset Info Type Sym.Value Sym. Name
+0+ 00000104 R_386_PLT32 00000000 foo1
+0+4 00000204 R_386_PLT32 00000000 foo2
+0+8 00000304 R_386_PLT32 00000000 foo3
+0+c 00000404 R_386_PLT32 00000000 foo4
+
+Hex dump of section '.rodata':
+ NOTE: This section has relocations against it, but these have NOT been applied to this dump.
+ 0x00000000 00000000 04000000 08000000 0c000000 ................
+#pass
new file mode 100644
@@ -0,0 +1,7 @@
+
+ .section .rodata
+.L4:
+ .long foo1@PLT - .L4
+ .long foo2@PLT - .L4
+ .long foo3@PLT - .L4
+ .long foo4@PLT - .L4
@@ -161,7 +161,7 @@ bad .byte xtrn@ntpoff
bad .byte xtrn@tpoff
.long xtrn@got + 4
.long xtrn@got - 4
-bad .long xtrn@plt - .
+bad .long xtrn@plt - ptr
.text
movl $ptr@PLT, %eax
@@ -165,3 +165,4 @@
.*:220: Info: .*
.*:3: Error: .*
.*:227: Info: .*
+.*:227: Error: can't resolve xtrn - ptr
@@ -224,7 +224,7 @@ bad .byte xtrn@gotplt
mov xtrn(,%ebx), %eax
vgatherdps %xmm2, xtrn(,%xmm1), %xmm0
addr32 vgatherdps %xmm2, xtrn(,%xmm1), %xmm0
-bad .long xtrn@plt - .
+bad .long xtrn@plt - ptr
.text
movabs $ptr@GOT, %rax
new file mode 100644
@@ -0,0 +1,29 @@
+#as:
+#readelf: -rsW
+#name: x86-64 PIC jump table
+#notarget: *-*-solaris*
+
+#...
+Relocation section '.rela.text' at offset 0x[0-9a-f]+ contains 3 entries:
+ +Offset +Info +Type +Sym.* Value +Symbol's Name \+ Addend
+0+8 0+40+2 +R_X86_64_PC32 +0+ +bar0 - 4
+0+f 0+20+2 +R_X86_64_PC32 +0+ +.rodata - 4
+0+20 0+50+4 +R_X86_64_PLT32 +0+ +bar2 - 4
+#...
+Relocation section '.rela.rodata' at offset 0x[0-9a-f]+ contains 5 entries:
+ +Offset +Info +Type +Sym.* Value +Symbol's Name \+ Addend
+0+ 0+40+4 +R_X86_64_PLT32 +0+ +bar0 \+ 0
+0+4 0+60+4 +R_X86_64_PLT32 +0+ +bar1 \+ 4
+0+8 0+10+2 +R_X86_64_PC32 +0+ +.text \+ 27
+0+c 0+70+4 +R_X86_64_PLT32 +0+ +bar3 \+ c
+0+10+ 0+80+4 +R_X86_64_PLT32 +0+ +bar4 \+ 10
+#...
+ +[0-9]+: 0+ +0 SECTION LOCAL +DEFAULT +1 .text
+ +[0-9]+: 0+ +0 SECTION LOCAL +DEFAULT +5 .rodata
+ +[0-9]+: 0+ +36 FUNC +GLOBAL +DEFAULT +1 foo
+ +[0-9]+: 0+ +0 NOTYPE +GLOBAL DEFAULT +UND bar0
+ +[0-9]+: 0+ +0 NOTYPE +GLOBAL DEFAULT +UND bar2
+ +[0-9]+: 0+ +0 NOTYPE +GLOBAL DEFAULT +UND bar1
+ +[0-9]+: 0+ +0 NOTYPE +GLOBAL DEFAULT +UND bar3
+ +[0-9]+: 0+ +0 NOTYPE +GLOBAL DEFAULT +UND bar4
+#pass
new file mode 100644
@@ -0,0 +1,31 @@
+# Check R_X86_64_PLT32 relocation in jump table.
+
+ .text
+ .p2align 4
+ .globl foo
+ .type foo, @function
+foo:
+ .cfi_startproc
+ cmpl $4, %edi
+ ja .L1
+ leaq bar0(%rip), %rdx
+ leaq .L4(%rip), %rdx
+ movl %edi, %edi
+ movslq (%rdx,%rdi,4), %rax
+ addq %rdx, %rax
+ jmp *%rax
+.L1:
+ ret
+.Lbar2:
+ jmp bar2
+ .cfi_endproc
+ .size foo, .-foo
+ .section .rodata
+ .p2align 2
+.L4:
+ .long bar0@plt-.L4
+ .long bar1@PLT-.L4
+ .long .Lbar2-.L4
+ .long bar3@PLT-.L4
+ .long bar4@plt-.L4
+ .section .note.GNU-stack,"",@progbits
@@ -778,6 +778,8 @@ if [is_elf_format] then {
run_dump_test "reloc-section-sym-all"
run_dump_test "reloc-section-sym-internal"
run_dump_test "reloc-section-sym-none"
+
+ run_dump_test "x86-64-jump-table"
}
run_dump_test pr27198
run_dump_test pr29483
new file mode 100644
@@ -0,0 +1,46 @@
+#include <stdio.h>
+
+int
+bar0 (void)
+{
+ return 0;
+}
+
+int
+bar1 (void)
+{
+ return 1;
+}
+
+int
+bar2 (void)
+{
+ return 2;
+}
+
+int
+bar3 (void)
+{
+ return 3;
+}
+
+int
+bar4 (void)
+{
+ return 4;
+}
+
+extern int foo (int);
+
+int
+main ()
+{
+ if (foo (1) == 1
+ && foo (3) == 3
+ && foo (4) == 4
+ && foo (2) == 2
+ && foo (0) == 0)
+ printf ("PASS\n");
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,30 @@
+# Check R_X86_64_PLT32 relocation in jump table.
+
+ .text
+ .p2align 4
+ .globl foo
+ .type foo, @function
+foo:
+ .cfi_startproc
+ cmpl $4, %edi
+ ja .L1
+ leaq .L4(%rip), %rdx
+ movl %edi, %edi
+ movslq (%rdx,%rdi,4), %rax
+ addq %rdx, %rax
+ jmp *%rax
+.L1:
+ ret
+.Lbar2:
+ jmp bar2@PLT
+ .cfi_endproc
+ .size foo, .-foo
+ .section .rodata
+ .p2align 2
+.L4:
+ .long bar0@plt-.L4
+ .long bar1@PLT-.L4
+ .long .Lbar2-.L4
+ .long bar3@PLT-.L4
+ .long bar4@plt-.L4
+ .section .note.GNU-stack,"",@progbits
@@ -1523,6 +1523,14 @@ if { [isnative] && [check_compiler_available] } {
{} \
"pr32067" \
] \
+ [list \
+ "Build libjump-table.so" \
+ "-shared" \
+ "" \
+ { x86-64-jump-table.s } \
+ {} \
+ "libjump-table.so" \
+ ] \
]
if {[istarget "x86_64-*-linux*-gnux32"]} {
@@ -2046,6 +2054,14 @@ if { [isnative] && [check_compiler_available] } {
"plt3" \
"pass.out" \
] \
+ [list \
+ "Run pr34423" \
+ "-Wl,--no-as-needed tmpdir/libjump-table.so" \
+ "" \
+ { pr34423.c } \
+ "pr34423" \
+ "pass.out" \
+ ] \
]
# Run-time tests which require working ifunc attribute support.
--
2.55.0