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

Matt Turner <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.