[PATCH 1/4] alpha: add IFUNC (STT_GNU_IFUNC) and R_ALPHA_IRELATIVE support
Matt Turner <[email protected]>
| Newsgroups | gmane.comp.gnu.binutils |
|---|---|
| Message-ID | <[email protected]> |
Define a new R_ALPHA_IRELATIVE relocation (42) for use with GNU
indirect functions (IFUNC) on Alpha.
In the BFD backend (elf64-alpha.c):
- Add HOWTO table entry and BFD_RELOC_IRELATIVE mapping for the new
relocation.
- Classify R_ALPHA_IRELATIVE as reloc_class_ifunc in
elf64_alpha_reloc_type_class.
- Recognize STT_GNU_IFUNC in elf64_alpha_want_plt so IFUNC symbols are
eligible for PLT entries.
- Allow IFUNC symbols to receive PLT entries even when not dynamic in
elf64_alpha_adjust_dynamic_symbol.
- Emit R_ALPHA_IRELATIVE (with the resolver address as addend) instead
of R_ALPHA_JMP_SLOT for locally-defined IFUNC symbols in
elf64_alpha_finish_dynamic_symbol.
- Handle data references (R_ALPHA_REFQUAD) to IFUNC symbols by emitting
R_ALPHA_IRELATIVE in elf64_alpha_relocate_section.
- Use R_ALPHA_IRELATIVE instead of R_ALPHA_RELATIVE for GOT entries
that reference local IFUNC symbols.
Remove Alpha from the IFUNC test exclusion list in
ld/testsuite/ld-ifunc/ifunc.exp.
---
bfd/elf64-alpha.c | 66 ++++++++++++++++++++++++++++++++++++++-------
include/elf/alpha.h | 2 ++
2 files changed, 59 insertions(+), 9 deletions(-)
diff --git ./bfd/elf64-alpha.c ./bfd/elf64-alpha.c
index 10175882d88..6925bdd0a4d 100644
--- ./bfd/elf64-alpha.c
+++ ./bfd/elf64-alpha.c
@@ -1020,6 +1020,21 @@ static reloc_howto_type elf64_alpha_howto_table[] =
0xffff, /* src_mask */
0xffff, /* dst_mask */
false), /* pcrel_offset */
+
+ /* A dynamic relocation for an IFUNC resolver. */
+ HOWTO (R_ALPHA_IRELATIVE, /* type */
+ 0, /* rightshift */
+ 8, /* size */
+ 64, /* bitsize */
+ false, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "IRELATIVE", /* name */
+ false, /* partial_inplace */
+ 0, /* src_mask */
+ MINUS_ONE, /* dst_mask */
+ false), /* pcrel_offset */
};
/* A mapping from BFD reloc types to Alpha ELF reloc types. */
@@ -1062,6 +1077,7 @@ static const struct elf_reloc_map elf64_alpha_reloc_map[] =
{BFD_RELOC_ALPHA_TPREL_HI16, R_ALPHA_TPRELHI},
{BFD_RELOC_ALPHA_TPREL_LO16, R_ALPHA_TPRELLO},
{BFD_RELOC_ALPHA_TPREL16, R_ALPHA_TPREL16},
+ {BFD_RELOC_IRELATIVE, R_ALPHA_IRELATIVE},
};
/* Given a BFD reloc type, return a HOWTO structure. */
@@ -1733,6 +1749,7 @@ static bool
elf64_alpha_want_plt (struct alpha_elf_link_hash_entry *ah)
{
return ((ah->root.type == STT_FUNC
+ || ah->root.type == STT_GNU_IFUNC
|| ah->root.root.type == bfd_link_hash_undefweak
|| ah->root.root.type == bfd_link_hash_undefined)
&& (ah->flags & ALPHA_ELF_LINK_HASH_LU_PLT) != 0
@@ -2033,8 +2050,10 @@ elf64_alpha_adjust_dynamic_symbol (struct bfd_link_info *info,
about whether this symbol should get a .plt entry. Irritatingly, it
is common for folk to leave undefined symbols in shared libraries,
and they still expect lazy binding; accept undefined symbols in lieu
- of STT_FUNC. */
- if (alpha_elf_dynamic_symbol_p (h, info) && elf64_alpha_want_plt (ah))
+ of STT_FUNC. IFUNC symbols always need a PLT entry for the
+ IRELATIVE resolver mechanism, even when not dynamic. */
+ if ((alpha_elf_dynamic_symbol_p (h, info) || h->type == STT_GNU_IFUNC)
+ && elf64_alpha_want_plt (ah))
{
h->needs_plt = true;
@@ -4334,13 +4353,21 @@ elf64_alpha_relocate_section (struct bfd_link_info *info,
/* If the symbol has been forced local, output a
RELATIVE reloc, otherwise it will be handled in
- finish_dynamic_symbol. */
+ finish_dynamic_symbol. Use IRELATIVE for local
+ IFUNC symbols. */
if (bfd_link_pic (info)
&& !dynamic_symbol_p
&& !undef_weak_ref)
- elf64_alpha_emit_dynrel (info->output_bfd, info, sgot, srelgot,
- gotent->got_offset, 0,
- R_ALPHA_RELATIVE, value);
+ {
+ long r_type_dyn = R_ALPHA_RELATIVE;
+
+ if (h != NULL && h->root.type == STT_GNU_IFUNC)
+ r_type_dyn = R_ALPHA_IRELATIVE;
+
+ elf64_alpha_emit_dynrel (info->output_bfd, info, sgot, srelgot,
+ gotent->got_offset, 0,
+ r_type_dyn, value);
+ }
}
value = (sgot->output_section->vma
@@ -4500,6 +4527,14 @@ elf64_alpha_relocate_section (struct bfd_link_info *info,
dynindx = 0;
dynaddend = value - dtp_base;
}
+ else if (h != NULL
+ && h->root.type == STT_GNU_IFUNC
+ && (input_section->flags & SEC_ALLOC))
+ {
+ dynindx = 0;
+ dyntype = R_ALPHA_IRELATIVE;
+ dynaddend = value;
+ }
else if (bfd_link_pic (info)
&& r_symndx != STN_UNDEF
&& (input_section->flags & SEC_ALLOC)
@@ -4772,8 +4807,9 @@ elf64_alpha_finish_dynamic_symbol (struct bfd_link_info *info,
bfd_vma got_addr, plt_addr;
bfd_vma plt_index;
struct alpha_elf_got_entry *gotent;
+ bool is_ifunc = h->type == STT_GNU_IFUNC;
- BFD_ASSERT (h->dynindx != -1);
+ BFD_ASSERT (is_ifunc || h->dynindx != -1);
splt = elf_hash_table (info)->splt;
BFD_ASSERT (splt != NULL);
@@ -4830,8 +4866,18 @@ elf64_alpha_finish_dynamic_symbol (struct bfd_link_info *info,
/* Fill in the entry in the .rela.plt section. */
outrel.r_offset = got_addr;
- outrel.r_info = ELF64_R_INFO(h->dynindx, R_ALPHA_JMP_SLOT);
- outrel.r_addend = 0;
+ if (is_ifunc && !alpha_elf_dynamic_symbol_p (h, info))
+ {
+ outrel.r_info = ELF64_R_INFO (0, R_ALPHA_IRELATIVE);
+ outrel.r_addend = (h->root.u.def.value
+ + h->root.u.def.section->output_section->vma
+ + h->root.u.def.section->output_offset);
+ }
+ else
+ {
+ outrel.r_info = ELF64_R_INFO (h->dynindx, R_ALPHA_JMP_SLOT);
+ outrel.r_addend = 0;
+ }
loc = srel->contents + plt_index * sizeof (Elf64_External_Rela);
bfd_elf64_swap_reloca_out (info->output_bfd, &outrel, loc);
@@ -5308,6 +5354,8 @@ elf64_alpha_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
return reloc_class_plt;
case R_ALPHA_COPY:
return reloc_class_copy;
+ case R_ALPHA_IRELATIVE:
+ return reloc_class_ifunc;
default:
return reloc_class_normal;
}
diff --git ./include/elf/alpha.h ./include/elf/alpha.h
index 9b7b4cfe16a..f7a0f040508 100644
--- ./include/elf/alpha.h
+++ ./include/elf/alpha.h
@@ -118,6 +118,8 @@ START_RELOC_NUMBERS (elf_alpha_reloc_type)
RELOC_NUMBER (R_ALPHA_TPRELLO, 40)
RELOC_NUMBER (R_ALPHA_TPREL16, 41)
+ RELOC_NUMBER (R_ALPHA_IRELATIVE, 42)
+
END_RELOC_NUMBERS (R_ALPHA_max)
#define LITUSE_ALPHA_ADDR 0
--
2.54.0