[PATCH 06/11] alpha: reject relocations that cannot refer to an IFUNC

Matt Turner mattst88@gmail.com
Tue Sep 1 02:33:13 GMT 2026


A reference to an IFUNC has to go through the GOT entry or the data word
that an R_ALPHA_IRELATIVE fills in with the address the resolver
returns. Two kinds of relocation cannot, and nothing diagnosed either.

R_ALPHA_BRADDR, R_ALPHA_BRSGP, the gp-relative relocations and the
pc-relative R_ALPHA_SREL* name the symbol directly, so they reach the
resolver rather than the function it selects. R_ALPHA_REFLONG names it
indirectly but has only a 32-bit field, which cannot hold the address
the resolver returns.

Report each of these and carry on, so that one link reports them all.
---
 bfd/elf64-alpha.c                             | 31 +++++++++++++++++++
 ld/testsuite/ld-alpha/ifunc-branch.d          |  3 ++
 ld/testsuite/ld-alpha/ifunc-branch.s          | 17 ++++++++++
 ld/testsuite/ld-alpha/ifunc-gprel.d           |  3 ++
 ld/testsuite/ld-alpha/ifunc-gprel.s           | 18 +++++++++++
 ld/testsuite/ld-alpha/ifunc-gprel32.d         |  3 ++
 ld/testsuite/ld-alpha/ifunc-gprel32.s         | 17 ++++++++++
 ld/testsuite/ld-alpha/ifunc-reflong-dynamic.d |  3 ++
 ld/testsuite/ld-alpha/ifunc-reflong-static.d  |  3 ++
 ld/testsuite/ld-alpha/ifunc-reflong.s         | 26 ++++++++++++++++
 ld/testsuite/ld-alpha/ifunc-srel.d            |  3 ++
 ld/testsuite/ld-alpha/ifunc-srel.s            | 19 ++++++++++++
 12 files changed, 146 insertions(+)
 create mode 100644 ld/testsuite/ld-alpha/ifunc-branch.d
 create mode 100644 ld/testsuite/ld-alpha/ifunc-branch.s
 create mode 100644 ld/testsuite/ld-alpha/ifunc-gprel.d
 create mode 100644 ld/testsuite/ld-alpha/ifunc-gprel.s
 create mode 100644 ld/testsuite/ld-alpha/ifunc-gprel32.d
 create mode 100644 ld/testsuite/ld-alpha/ifunc-gprel32.s
 create mode 100644 ld/testsuite/ld-alpha/ifunc-reflong-dynamic.d
 create mode 100644 ld/testsuite/ld-alpha/ifunc-reflong-static.d
 create mode 100644 ld/testsuite/ld-alpha/ifunc-reflong.s
 create mode 100644 ld/testsuite/ld-alpha/ifunc-srel.d
 create mode 100644 ld/testsuite/ld-alpha/ifunc-srel.s

diff --git ./bfd/elf64-alpha.c ./bfd/elf64-alpha.c
index 484fe9589fb..ebb2f0ded11 100644
--- ./bfd/elf64-alpha.c
+++ ./bfd/elf64-alpha.c
@@ -4349,6 +4349,37 @@ elf64_alpha_relocate_section (struct bfd_link_info *info,
 	    && gotent->addend == addend)
 	  break;
 
+      /* A reference to an IFUNC has to go through the GOT entry or the data
+	 word that an R_ALPHA_IRELATIVE fills in with the address the
+	 resolver returns.  A relocation that names the symbol directly
+	 reaches the resolver instead, and R_ALPHA_REFLONG has no room for
+	 the address in the first place.  */
+      if (elf64_alpha_ifunc_p (h, sym) && (input_section->flags & SEC_ALLOC))
+	switch (r_type)
+	  {
+	  case R_ALPHA_REFLONG:
+	  case R_ALPHA_BRADDR:
+	  case R_ALPHA_BRSGP:
+	  case R_ALPHA_GPREL16:
+	  case R_ALPHA_GPREL32:
+	  case R_ALPHA_GPRELLOW:
+	  case R_ALPHA_GPRELHIGH:
+	  case R_ALPHA_SREL16:
+	  case R_ALPHA_SREL32:
+	  case R_ALPHA_SREL64:
+	    _bfd_error_handler
+	      /* xgettext:c-format */
+	      (_("%pB: %s relocation against STT_GNU_IFUNC symbol `%s' is "
+		 "not supported"),
+	       input_bfd, howto->name,
+	       elf64_alpha_sym_name (input_bfd, symtab_hdr, h, sym, sec));
+	    ret_val = false;
+	    continue;
+
+	  default:
+	    break;
+	  }
+
       switch (r_type)
 	{
 	case R_ALPHA_GPDISP:
diff --git ./ld/testsuite/ld-alpha/ifunc-branch.d ./ld/testsuite/ld-alpha/ifunc-branch.d
new file mode 100644
index 00000000000..fb0e875b79b
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-branch.d
@@ -0,0 +1,3 @@
+#source: ifunc-branch.s
+#ld: -melf64alpha
+#error: \A[^\n]*: BRADDR relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: BRSGP relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: final link failed[^\n]*\n?\Z
diff --git ./ld/testsuite/ld-alpha/ifunc-branch.s ./ld/testsuite/ld-alpha/ifunc-branch.s
new file mode 100644
index 00000000000..701ab520690
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-branch.s
@@ -0,0 +1,17 @@
+	.text
+
+	.globl	global_ifunc
+	.type	global_ifunc, @gnu_indirect_function
+global_ifunc:
+	ret
+
+	# A direct branch reaches the resolver rather than the function it
+	# selects, so it cannot be used to call an IFUNC.
+	.globl	_start
+	.ent	_start
+_start:
+	ldgp	$29, 0($27)
+	bsr	$26, global_ifunc
+	bsr	$26, global_ifunc	!samegp
+	ret
+	.end	_start
diff --git ./ld/testsuite/ld-alpha/ifunc-gprel.d ./ld/testsuite/ld-alpha/ifunc-gprel.d
new file mode 100644
index 00000000000..26a3f8abbe7
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-gprel.d
@@ -0,0 +1,3 @@
+#source: ifunc-gprel.s
+#ld: -melf64alpha
+#error: \A[^\n]*: GPRELHIGH relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: GPRELLOW relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: GPREL16 relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: final link failed[^\n]*\n?\Z
diff --git ./ld/testsuite/ld-alpha/ifunc-gprel.s ./ld/testsuite/ld-alpha/ifunc-gprel.s
new file mode 100644
index 00000000000..3236bfb7346
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-gprel.s
@@ -0,0 +1,18 @@
+	.text
+
+	.globl	global_ifunc
+	.type	global_ifunc, @gnu_indirect_function
+global_ifunc:
+	ret
+
+	# A gp-relative address is the address of the resolver rather than
+	# of the function it selects.
+	.globl	_start
+	.ent	_start
+_start:
+	ldgp	$29, 0($27)
+	ldah	$1, global_ifunc($29)	!gprelhigh
+	lda	$1, global_ifunc($1)	!gprellow
+	ldq	$1, global_ifunc($29)	!gprel
+	ret
+	.end	_start
diff --git ./ld/testsuite/ld-alpha/ifunc-gprel32.d ./ld/testsuite/ld-alpha/ifunc-gprel32.d
new file mode 100644
index 00000000000..2e4dddbd7d9
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-gprel32.d
@@ -0,0 +1,3 @@
+#source: ifunc-gprel32.s
+#ld: -melf64alpha
+#error: \A[^\n]*: GPREL32 relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: final link failed[^\n]*\n?\Z
diff --git ./ld/testsuite/ld-alpha/ifunc-gprel32.s ./ld/testsuite/ld-alpha/ifunc-gprel32.s
new file mode 100644
index 00000000000..cdef3d47d18
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-gprel32.s
@@ -0,0 +1,17 @@
+	.text
+
+	.globl	global_ifunc
+	.type	global_ifunc, @gnu_indirect_function
+global_ifunc:
+	ret
+
+	.globl	_start
+	.ent	_start
+_start:
+	ret
+	.end	_start
+
+	# A gp-relative offset to an IFUNC is an offset to the resolver
+	# rather than to the function it selects.
+	.data
+	.gprel32 global_ifunc
diff --git ./ld/testsuite/ld-alpha/ifunc-reflong-dynamic.d ./ld/testsuite/ld-alpha/ifunc-reflong-dynamic.d
new file mode 100644
index 00000000000..2edab481e8a
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-reflong-dynamic.d
@@ -0,0 +1,3 @@
+#source: ifunc-reflong.s
+#ld: -melf64alpha --export-dynamic
+#error: \A[^\n]*: REFLONG relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: REFLONG relocation against STT_GNU_IFUNC symbol `local_ifunc' is not supported\n[^\n]*: final link failed[^\n]*\n?\Z
diff --git ./ld/testsuite/ld-alpha/ifunc-reflong-static.d ./ld/testsuite/ld-alpha/ifunc-reflong-static.d
new file mode 100644
index 00000000000..b10ad913f2c
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-reflong-static.d
@@ -0,0 +1,3 @@
+#source: ifunc-reflong.s
+#ld: -melf64alpha
+#error: \A[^\n]*: REFLONG relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: REFLONG relocation against STT_GNU_IFUNC symbol `local_ifunc' is not supported\n[^\n]*: final link failed[^\n]*\n?\Z
diff --git ./ld/testsuite/ld-alpha/ifunc-reflong.s ./ld/testsuite/ld-alpha/ifunc-reflong.s
new file mode 100644
index 00000000000..a153beb52a0
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-reflong.s
@@ -0,0 +1,26 @@
+	.text
+
+	.globl	global_ifunc
+	.type	global_ifunc, @gnu_indirect_function
+global_ifunc:
+	ret
+
+	.type	local_ifunc, @gnu_indirect_function
+local_ifunc:
+	ret
+
+	.globl	_start
+	.ent	_start
+_start:
+	ret
+	.end	_start
+
+	# An IRELATIVE writes the 64-bit address the resolver returns, so a
+	# 32-bit reference to an IFUNC cannot be represented.  A local symbol
+	# is named in the diagnostic by its symbol table entry rather than by
+	# a hash table entry.
+	.data
+	.globl	ptr
+ptr:
+	.long	global_ifunc
+	.long	local_ifunc
diff --git ./ld/testsuite/ld-alpha/ifunc-srel.d ./ld/testsuite/ld-alpha/ifunc-srel.d
new file mode 100644
index 00000000000..46034487b99
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-srel.d
@@ -0,0 +1,3 @@
+#source: ifunc-srel.s
+#ld: -melf64alpha
+#error: \A[^\n]*: SREL16 relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: SREL32 relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: SREL64 relocation against STT_GNU_IFUNC symbol `global_ifunc' is not supported\n[^\n]*: final link failed[^\n]*\n?\Z
diff --git ./ld/testsuite/ld-alpha/ifunc-srel.s ./ld/testsuite/ld-alpha/ifunc-srel.s
new file mode 100644
index 00000000000..d0da109f471
--- /dev/null
+++ ./ld/testsuite/ld-alpha/ifunc-srel.s
@@ -0,0 +1,19 @@
+	.text
+
+	.globl	global_ifunc
+	.type	global_ifunc, @gnu_indirect_function
+global_ifunc:
+	ret
+
+	.globl	_start
+	.ent	_start
+_start:
+	ret
+	.end	_start
+
+	# A pc-relative offset to an IFUNC is an offset to the resolver
+	# rather than to the function it selects.
+	.data
+	.short	global_ifunc - .
+	.long	global_ifunc - .
+	.quad	global_ifunc - .
-- 
2.54.0



More information about the Binutils mailing list