[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
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.