[10/11] alpha: reject a non-zero addend on a reference to an IFUNC

Message ID 20260901023318.3503991-11-mattst88@gmail.com
State New
Headers
Series alpha: support STT_GNU_IFUNC |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Matt Turner Sept. 1, 2026, 2:33 a.m. UTC
  An R_ALPHA_IRELATIVE carries the address of the resolver in its addend,
so there is no room in it for an offset from the symbol. A reference
that gets one, from an R_ALPHA_LITERAL or an R_ALPHA_REFQUAD, therefore
cannot have an addend. Nothing diagnosed that.

A reference that the dynamic linker resolves is not affected: it keeps
the addend of the symbolic relocation it gets instead, and a shared
library that takes the address of one of its own IFUNCs at an offset
still links. A local symbol in such a library does not bind dynamically,
though, so it is rejected there too.
---
 bfd/elf64-alpha.c                             | 21 +++++++++++++++++++
 ld/testsuite/ld-alpha/ifunc-addend-literal.d  |  3 +++
 ld/testsuite/ld-alpha/ifunc-addend-literal.s  | 19 +++++++++++++++++
 ld/testsuite/ld-alpha/ifunc-addend-local.s    | 13 ++++++++++++
 ld/testsuite/ld-alpha/ifunc-addend-refquad.d  |  3 +++
 ld/testsuite/ld-alpha/ifunc-addend-refquad.s  | 20 ++++++++++++++++++
 .../ld-alpha/ifunc-addend-shared-local.d      |  3 +++
 ld/testsuite/ld-alpha/ifunc-addend-shared.d   | 11 ++++++++++
 8 files changed, 93 insertions(+)
 create mode 100644 ld/testsuite/ld-alpha/ifunc-addend-literal.d
 create mode 100644 ld/testsuite/ld-alpha/ifunc-addend-literal.s
 create mode 100644 ld/testsuite/ld-alpha/ifunc-addend-local.s
 create mode 100644 ld/testsuite/ld-alpha/ifunc-addend-refquad.d
 create mode 100644 ld/testsuite/ld-alpha/ifunc-addend-refquad.s
 create mode 100644 ld/testsuite/ld-alpha/ifunc-addend-shared-local.d
 create mode 100644 ld/testsuite/ld-alpha/ifunc-addend-shared.d
  

Patch

diff --git ./bfd/elf64-alpha.c ./bfd/elf64-alpha.c
index 09e3778f3a9..d8f1a3bbf48 100644
--- ./bfd/elf64-alpha.c
+++ ./bfd/elf64-alpha.c
@@ -4686,6 +4686,27 @@  elf64_alpha_relocate_section (struct bfd_link_info *info,
 	    break;
 	  }
 
+      /* An R_ALPHA_IRELATIVE carries the address of the resolver in its
+	 addend, so there is no room in it for an offset from the symbol.
+	 A reference the dynamic linker resolves is not affected: it keeps
+	 the addend of the symbolic relocation it gets instead.  */
+      if (addend != 0
+	  && elf64_alpha_ifunc_reloc_p (r_type)
+	  && elf64_alpha_ifunc_p (h, sym)
+	  && (input_section->flags & SEC_ALLOC)
+	  && (elf64_alpha_ifunc_irelplt_p (h, sym, info)
+	      || (bfd_link_pic (info) && !dynamic_symbol_p)))
+	{
+	  _bfd_error_handler
+	    /* xgettext:c-format */
+	    (_("%pB: %s relocation against STT_GNU_IFUNC symbol `%s' has a "
+	       "non-zero addend"),
+	     input_bfd, howto->name,
+	     elf64_alpha_sym_name (input_bfd, symtab_hdr, h, sym, sec));
+	  ret_val = false;
+	  continue;
+	}
+
       switch (r_type)
 	{
 	case R_ALPHA_GPDISP:
diff --git ./ld/testsuite/ld-alpha/ifunc-addend-literal.d ./ld/testsuite/ld-alpha/ifunc-addend-literal.d
new file mode 100644
index 00000000000..2ea6b7df5a5
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-addend-literal.d
@@ -0,0 +1,3 @@ 
+#source: ifunc-addend-literal.s
+#ld: -melf64alpha
+#error: \A[^\n]*: ELF_LITERAL relocation against STT_GNU_IFUNC symbol `global_ifunc' has a non-zero addend\n[^\n]*: final link failed[^\n]*\n?\Z
diff --git ./ld/testsuite/ld-alpha/ifunc-addend-literal.s ./ld/testsuite/ld-alpha/ifunc-addend-literal.s
new file mode 100644
index 00000000000..c7c96d2f460
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-addend-literal.s
@@ -0,0 +1,19 @@ 
+	.text
+
+	.globl	global_ifunc
+	.type	global_ifunc, @gnu_indirect_function
+global_ifunc:
+	ret
+	ret
+
+	# An IRELATIVE's addend is the address of the resolver, so a
+	# reference to an IFUNC cannot carry an offset.
+	.globl	_start
+	.ent	_start
+_start:
+	ldgp	$29, 0($27)
+	ldq	$27, global_ifunc+4($29)	!literal!1
+	jsr	$26, ($27), 0			!lituse_jsr!1
+	ldgp	$29, 0($26)
+	ret
+	.end	_start
diff --git ./ld/testsuite/ld-alpha/ifunc-addend-local.s ./ld/testsuite/ld-alpha/ifunc-addend-local.s
new file mode 100644
index 00000000000..1aa9138979a
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-addend-local.s
@@ -0,0 +1,13 @@ 
+	.text
+
+	.type	local_ifunc, @gnu_indirect_function
+local_ifunc:
+	ret
+	ret
+
+	# A local symbol is not preemptible, so even in a shared library the
+	# reference becomes an R_ALPHA_IRELATIVE and cannot carry an offset.
+	.data
+	.globl	ptr
+ptr:
+	.quad	local_ifunc+4
diff --git ./ld/testsuite/ld-alpha/ifunc-addend-refquad.d ./ld/testsuite/ld-alpha/ifunc-addend-refquad.d
new file mode 100644
index 00000000000..d4a912a9438
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-addend-refquad.d
@@ -0,0 +1,3 @@ 
+#source: ifunc-addend-refquad.s
+#ld: -melf64alpha
+#error: \A[^\n]*: REFQUAD relocation against STT_GNU_IFUNC symbol `global_ifunc' has a non-zero addend\n[^\n]*: final link failed[^\n]*\n?\Z
diff --git ./ld/testsuite/ld-alpha/ifunc-addend-refquad.s ./ld/testsuite/ld-alpha/ifunc-addend-refquad.s
new file mode 100644
index 00000000000..95d6dfc1daa
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-addend-refquad.s
@@ -0,0 +1,20 @@ 
+	.text
+
+	.globl	global_ifunc
+	.type	global_ifunc, @gnu_indirect_function
+global_ifunc:
+	ret
+	ret
+
+	.globl	_start
+	.ent	_start
+_start:
+	ret
+	.end	_start
+
+	# An IRELATIVE's addend is the address of the resolver, so a
+	# reference to an IFUNC cannot carry an offset.
+	.data
+	.globl	ptr
+ptr:
+	.quad	global_ifunc+4
diff --git ./ld/testsuite/ld-alpha/ifunc-addend-shared-local.d ./ld/testsuite/ld-alpha/ifunc-addend-shared-local.d
new file mode 100644
index 00000000000..d3c25eb82aa
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-addend-shared-local.d
@@ -0,0 +1,3 @@ 
+#source: ifunc-addend-local.s
+#ld: -shared -melf64alpha
+#error: \A[^\n]*: REFQUAD relocation against STT_GNU_IFUNC symbol `local_ifunc' has a non-zero addend\n[^\n]*: final link failed[^\n]*\n?\Z
diff --git ./ld/testsuite/ld-alpha/ifunc-addend-shared.d ./ld/testsuite/ld-alpha/ifunc-addend-shared.d
new file mode 100644
index 00000000000..31bc77ba4c9
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-addend-shared.d
@@ -0,0 +1,11 @@ 
+#source: ifunc-addend-refquad.s
+#ld: -shared -melf64alpha
+#readelf: -Wr
+
+# The IFUNC is preemptible here, so the dynamic linker resolves it and the
+# relocation keeps its addend.  Nothing turns into an IRELATIVE, so the
+# addend is not rejected.
+Relocation section '\.rela\.dyn' .* contains 1 entry:
+#...
+[0-9a-f]+ +[0-9a-f]+ +R_ALPHA_REFQUAD .*global_ifunc \+ 4
+#pass