[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