[PATCH] x86-64: Generate R_X86_64_PLT32 for ".long foo@PLT - .L4"

"H.J. Lu" <[email protected]> Sat, 25 Jul 2026 10:35:20 +0800
Newsgroups gmane.comp.gnu.binutils
Message-ID <CAMe9rOpFTW9DN_0ZjSEaL6rN0QM+RsKg7PAmwrys1gRgEoLzpA@mail.gmail.com>
Add BFD_RELOC_X86_64_PLT32 and use it to generate R_X86_64_PLT32 for
directives like ".long foo@PLT - .L4" so that PLT entries are used to
resolve the PC32 relocation against function symbols for such directives.

bfd/

PR gas/34423
* elf64-x86-64.c (x86_64_reloc_map): Add BFD_RELOC_X86_64_PLT32.
* reloc.c (bfd_reloc_code_real): Add BFD_RELOC_X86_64_PLT32.
* bfd-in2.h: Regenerated.
* libbfd.h: Likewise.

gas/

PR gas/34423
* config/tc-i386.c (x86_cons): Return BFD_RELOC_X86_64_PLT32 for
directives like ".long foo@PLT - .L4".
(tc_gen_reloc): Generate R_X86_64_PLT32 for BFD_RELOC_X86_64_PLT32
with addend computed like R_X86_64_PC32.
* testsuite/gas/i386/reloc64.l: Updated.
* testsuite/gas/i386/reloc64.s: Replace ".long xtrn@plt - ." with
".long xtrn@plt - _start".
* 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/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.
* testsuite/gas/i386/ilp32/x86-64-jump-table.d: New file.

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.


-- 
H.J.
0001-x86-64-Generate-R_X86_64_PLT32-for-.long-foo-PLT-.L4.patch (text/x-patch, 13.2 KB)
From bf54e3d61bbfab91a860f43c93698d1c87f246f4 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <[email protected]>
Date: Fri, 24 Jul 2026 07:01:35 +0800
Subject: [PATCH] x86-64: Generate R_X86_64_PLT32 for ".long foo@PLT - .L4"

Add BFD_RELOC_X86_64_PLT32 and use it to generate R_X86_64_PLT32 for
directives like ".long foo@PLT - .L4" so that PLT entries are used to
resolve the PC32 relocation against function symbols for such directives.

bfd/

	PR gas/34423
	* elf64-x86-64.c (x86_64_reloc_map): Add BFD_RELOC_X86_64_PLT32.
	* reloc.c (bfd_reloc_code_real): Add BFD_RELOC_X86_64_PLT32.
	* bfd-in2.h: Regenerated.
	* libbfd.h: Likewise.

gas/

	PR gas/34423
	* config/tc-i386.c (x86_cons): Return BFD_RELOC_X86_64_PLT32 for
	directives like ".long foo@PLT - .L4".
	(tc_gen_reloc): Generate R_X86_64_PLT32 for BFD_RELOC_X86_64_PLT32
	with addend computed like R_X86_64_PC32.
	* testsuite/gas/i386/reloc64.l: Updated.
	* testsuite/gas/i386/reloc64.s: Replace ".long xtrn@plt - ." with
	".long xtrn@plt - _start".
	* 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/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.
	* testsuite/gas/i386/ilp32/x86-64-jump-table.d: New file.

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 <[email protected]>
---
 bfd/bfd-in2.h                                 |  1 +
 bfd/elf64-x86-64.c                            |  1 +
 bfd/libbfd.h                                  |  1 +
 bfd/reloc.c                                   |  2 +
 gas/config/tc-i386.c                          | 17 ++++++-
 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/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 +++++++
 16 files changed, 183 insertions(+), 3 deletions(-)
 create mode 100644 gas/testsuite/gas/i386/ilp32/x86-64-jump-table.d
 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

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index e49d67316f3..033ca41cc74 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -3919,6 +3919,7 @@ enum bfd_reloc_code_real
   /* x86-64/elf relocations.  */
   BFD_RELOC_X86_64_GOT32,
   BFD_RELOC_X86_64_GOTPCREL,
+  BFD_RELOC_X86_64_PLT32,
   BFD_RELOC_X86_64_32S,
   BFD_RELOC_X86_64_DTPMOD64,
   BFD_RELOC_X86_64_DTPOFF64,
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 575995d4957..98988cfae35 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -270,6 +270,7 @@ static const struct elf_reloc_map x86_64_reloc_map[] =
   { BFD_RELOC_IRELATIVE,	R_X86_64_IRELATIVE, },
   { BFD_RELOC_X86_64_PC32_BND,	R_X86_64_PC32_BND, },
   { BFD_RELOC_X86_64_PLT32_BND,	R_X86_64_PLT32_BND, },
+  { BFD_RELOC_X86_64_PLT32,	R_X86_64_PLT32, },
   { BFD_RELOC_X86_64_GOTPCRELX, R_X86_64_GOTPCRELX, },
   { BFD_RELOC_X86_64_REX_GOTPCRELX, R_X86_64_REX_GOTPCRELX, },
   { BFD_RELOC_X86_64_CODE_4_GOTPCRELX, R_X86_64_CODE_4_GOTPCRELX, },
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 27bc0875cf6..dcfc0b98337 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -1448,6 +1448,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_386_GOT32X",
   "BFD_RELOC_X86_64_GOT32",
   "BFD_RELOC_X86_64_GOTPCREL",
+  "BFD_RELOC_X86_64_PLT32",
   "BFD_RELOC_X86_64_32S",
   "BFD_RELOC_X86_64_DTPMOD64",
   "BFD_RELOC_X86_64_DTPOFF64",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 6b9e00da5d6..995522876b0 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2358,6 +2358,8 @@ ENUM
   BFD_RELOC_X86_64_GOT32
 ENUMX
   BFD_RELOC_X86_64_GOTPCREL
+ENUMX
+  BFD_RELOC_X86_64_PLT32
 ENUMX
   BFD_RELOC_X86_64_32S
 ENUMX
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index d1ef80ce6d1..432c9f11c33 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -13549,6 +13549,12 @@ x86_cons (expressionS *exp, int size)
 	      as_bad (_("missing or invalid expression `%s'"), save);
 	      *input_line_pointer = c;
 	    }
+	  else if (got_reloc == BFD_RELOC_32_PLT_PCREL
+		   && size == 4
+		   && exp->X_op == O_subtract
+		   && S_IS_LOCAL (exp->X_op_symbol))
+	    /* Allow directives like ".long foo@PLT - .L4".  */
+	    got_reloc = BFD_RELOC_X86_64_PLT32;
 	  else if ((got_reloc == BFD_RELOC_386_PLT32
 		    || got_reloc == BFD_RELOC_32_PLT_PCREL)
 		   && exp->X_op != O_symbol)
@@ -18626,7 +18632,9 @@ 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_PLT32)
+	code = BFD_RELOC_X86_64_PLT32;
+      else if (fixp->fx_pcrel)
 	{
 	  switch (fixp->fx_size)
 	    {
@@ -18751,6 +18759,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_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
diff --git a/gas/testsuite/gas/i386/ilp32/reloc64.l b/gas/testsuite/gas/i386/ilp32/reloc64.l
index 14a111d763f..6435ae1a0be 100644
--- a/gas/testsuite/gas/i386/ilp32/reloc64.l
+++ b/gas/testsuite/gas/i386/ilp32/reloc64.l
@@ -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 .*
diff --git a/gas/testsuite/gas/i386/ilp32/reloc64.s b/gas/testsuite/gas/i386/ilp32/reloc64.s
index db2d2acdf5d..a4ccdb29916 100644
--- a/gas/testsuite/gas/i386/ilp32/reloc64.s
+++ b/gas/testsuite/gas/i386/ilp32/reloc64.s
@@ -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
diff --git a/gas/testsuite/gas/i386/ilp32/x86-64-jump-table.d b/gas/testsuite/gas/i386/ilp32/x86-64-jump-table.d
new file mode 100644
index 00000000000..2cff87d4445
--- /dev/null
+++ b/gas/testsuite/gas/i386/ilp32/x86-64-jump-table.d
@@ -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
diff --git a/gas/testsuite/gas/i386/reloc64.l b/gas/testsuite/gas/i386/reloc64.l
index c60c45d139a..28bdc823be4 100644
--- a/gas/testsuite/gas/i386/reloc64.l
+++ b/gas/testsuite/gas/i386/reloc64.l
@@ -165,3 +165,4 @@
 .*:220:  Info: .*
 .*:3: Error: .*
 .*:227:  Info: .*
+.*:227: Error: can't resolve xtrn - ptr
diff --git a/gas/testsuite/gas/i386/reloc64.s b/gas/testsuite/gas/i386/reloc64.s
index 5c0f4136a09..f248964813f 100644
--- a/gas/testsuite/gas/i386/reloc64.s
+++ b/gas/testsuite/gas/i386/reloc64.s
@@ -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
diff --git a/gas/testsuite/gas/i386/x86-64-jump-table.d b/gas/testsuite/gas/i386/x86-64-jump-table.d
new file mode 100644
index 00000000000..32c957b6ba6
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-jump-table.d
@@ -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
diff --git a/gas/testsuite/gas/i386/x86-64-jump-table.s b/gas/testsuite/gas/i386/x86-64-jump-table.s
new file mode 100644
index 00000000000..aefffad8f65
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-jump-table.s
@@ -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	[email protected]
+        .long	[email protected]
+        .long	.Lbar2-.L4
+        .long	[email protected]
+        .long	[email protected]
+        .section	.note.GNU-stack,"",@progbits
diff --git a/gas/testsuite/gas/i386/x86-64.exp b/gas/testsuite/gas/i386/x86-64.exp
index 58382b55e31..710f749a8dc 100644
--- a/gas/testsuite/gas/i386/x86-64.exp
+++ b/gas/testsuite/gas/i386/x86-64.exp
@@ -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
diff --git a/ld/testsuite/ld-x86-64/pr34423.c b/ld/testsuite/ld-x86-64/pr34423.c
new file mode 100644
index 00000000000..fc5a943a54a
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr34423.c
@@ -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;
+}
diff --git a/ld/testsuite/ld-x86-64/x86-64-jump-table.s b/ld/testsuite/ld-x86-64/x86-64-jump-table.s
new file mode 100644
index 00000000000..bd06f714469
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/x86-64-jump-table.s
@@ -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	[email protected]
+        .long	[email protected]
+        .long	.Lbar2-.L4
+        .long	[email protected]
+        .long	[email protected]
+        .section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index 07607810d15..985d96dcfba 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -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