[binutils-gdb] alpha: reject a non-zero addend on a reference to an IFUNC

Sam James sjames@sourceware.org
Sat Sep 5 08:35:24 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b83e869cbe70fc7ba2809babc7ad851b95b509bd

commit b83e869cbe70fc7ba2809babc7ad851b95b509bd
Author: Matt Turner <mattst88@gmail.com>
Date:   Mon Aug 31 22:33:17 2026 -0400

    alpha: reject a non-zero addend on a reference to an IFUNC
    
    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.

Diff:
---
 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/testsuite/ld-alpha/ifunc-addend-shared-local.d |  3 +++
 ld/testsuite/ld-alpha/ifunc-addend-shared.d       | 11 +++++++++++
 8 files changed, 93 insertions(+)

diff --git a/bfd/elf64-alpha.c b/bfd/elf64-alpha.c
index 09e3778f3a9..d8f1a3bbf48 100644
--- a/bfd/elf64-alpha.c
+++ b/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 a/ld/testsuite/ld-alpha/ifunc-addend-literal.d b/ld/testsuite/ld-alpha/ifunc-addend-literal.d
new file mode 100644
index 00000000000..2ea6b7df5a5
--- /dev/null
+++ b/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 a/ld/testsuite/ld-alpha/ifunc-addend-literal.s b/ld/testsuite/ld-alpha/ifunc-addend-literal.s
new file mode 100644
index 00000000000..c7c96d2f460
--- /dev/null
+++ b/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 a/ld/testsuite/ld-alpha/ifunc-addend-local.s b/ld/testsuite/ld-alpha/ifunc-addend-local.s
new file mode 100644
index 00000000000..1aa9138979a
--- /dev/null
+++ b/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 a/ld/testsuite/ld-alpha/ifunc-addend-refquad.d b/ld/testsuite/ld-alpha/ifunc-addend-refquad.d
new file mode 100644
index 00000000000..d4a912a9438
--- /dev/null
+++ b/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 a/ld/testsuite/ld-alpha/ifunc-addend-refquad.s b/ld/testsuite/ld-alpha/ifunc-addend-refquad.s
new file mode 100644
index 00000000000..95d6dfc1daa
--- /dev/null
+++ b/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 a/ld/testsuite/ld-alpha/ifunc-addend-shared-local.d b/ld/testsuite/ld-alpha/ifunc-addend-shared-local.d
new file mode 100644
index 00000000000..d3c25eb82aa
--- /dev/null
+++ b/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 a/ld/testsuite/ld-alpha/ifunc-addend-shared.d b/ld/testsuite/ld-alpha/ifunc-addend-shared.d
new file mode 100644
index 00000000000..31bc77ba4c9
--- /dev/null
+++ b/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


More information about the Binutils-cvs mailing list