This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC][PATCH] MIPS ifunc for binutils


This is the initial MIPS ifunc patch for binutils. This patch should be
applied in conjunction with the glibc patch.

Note: This patch is for initial code review and has no test cases included.
They will be forth coming with submission for commit.

This is targeting o32. Patches for 64 bit, n32, MicroMips, Mips16,
and possibly vxworks will follow this patch. I have also not
yet implemented ifunc with local, hidden, or protected
symbol status.

I attempted to follow the ARM implementation, but the 
traditional MIPS GOT design forced me to dirverge a bit.
All ifunc functions are represented in .iplt section stubs and 
in the .igot.plt section table. Each igot.plt entry has an
R_MIPS_IRELOCATE relocation record against it with the initial
igot entry having link time address of the ifunc routine and 
after the relocation action, the final runtime target routine
address.

The only change to the traditional MIPS GOT was to use the .iplt stub
address for the defined ifunc function instead of the function address.
This should allow seamless multigot support

***************************************
If an iplt is needed:

        Generate iplt stubs (.iplt)
        Generate igot table (.igot.plt)
        Generate IRELATIVE relocations for igot

Iplts are needed for a.outs only.

Non-shared a.outs: 

        Always goes through the iplt with IRELATIVE relocations against
        the igot.

Shared a.outs (both fPIC and not):

        All a.out references are direct with got entries containing ifunc
        addresses that have IRELATIVE relocations against them. The dynamic
        linker will catch the IRELATIVE relocations and do the fixup based on
        the GOT contents
        
        Dso references are through the iplt with IRELATIVE relocations
        against the igot and the dynsym entries being the iplt address
        and the symbol type changed STT_FUNC. The dynamic linker will update
        the igot table in the a.out in the same manner as it does the GOT.

Dsos:

        No iplt, igot or IRELATIVE relocations.
        
        The GOT and .dynsym will have the ifunc values and the dynsym type
        will remain STT_GNU_IFUNC. The dynamic linker will detect that 
        the symbol is an STT_GNU_IFUNC and do the fixup based on the defining
        dynsym address.

In all cases the contents of the igot is the ifunc address with an
R_MIPS_IRELATIVE relocation against it.

In all cases the contents of the got for the ifunc functions are the ifunc
addresses.

***************************************

bfd/ChangeLog

2013-08-16  Jack Carter  <jack.carter@imgtec.com>

	* bfd-in2.h (BFD_RELOC_MIPS_IRELATIVE): New relocation.
	* bfd/elf32-mips.c 
	(elf_mips_eh_howto table): Howto for BFD_RELOC_MIPS_IRELATIVE.
	(bfd_elf32_bfd_reloc_name_lookup): Case for elf_mips_irelative_howto.
	(mips_elf32_rtype_to_howto): Case for BFD_RELOC_MIPS_IRELATIVE.
	* elfxx-mips.c
	(struct mips_elf_link_hash_entry): Offset indexes into iplt and 
	igot.
	(mips_elf_link_hash_entry): Flag to indicate sym needs an iplt.
	(mips_elf_link_hash_table): New shortcut pointers to iplt related 
	sections. New size of an iplt stub.
	(mips_elf_allocate_ireloc): New function forward declaration.
	(mips_elf_allocate_iplt): New function forward declaration.
	(MIPS_ELF_RELOC_SECTION): Macro to pick suffic for relocation section.
	(MIPS_STATIC_LINK): Macro to determine if this is a static link.
	(MIPS_AOUT_LINK): Macro to determine if this link results in an a.out.
	(mips_exec_iplt_entry): Template for exec iplt instruction sequence.
	(mips_dso_iplt_entry): Template for dso iplt instruction sequence.
	(mips_elf_link_hash_newfunc): Initialization of new 
	mips_elf_link_hash_entry elements.
	(mips_elf_check_symbols): Allocate an iplt for any IFUNC symbols.
	(mips_elf_create_ifunc_sections): New function. Create ELF sections 
	associated with ifunc use.
	(ifunc_create_dynamic_relocation): If the symbol needs an iplt, 
	assigned the value of the symbol to be the address of the iplt entry.
	(mips_elf_create_dynamic_relocation): Call to 
	ifunc_create_dynamic_relocation().
	(_bfd_mips_elf_add_symbol_hook): Mark output as has_gnu_symbols.
	(mips_elf_allocate_iplt): New function.
	(_bfd_mips_elf_check_relocs): Check if we need to create iplt sections.
	If symbol is an IFUNC, don't convert it to an STT_FUNC.
	(mips_elf_allocate_ireloc): New function.
	(allocate_dynrelocs): Call mips_elf_allocate_ireloc if R_MIPS_IRELATIVE.
	(_bfd_mips_elf_adjust_dynamic_symbol): Add STT_GNU_IFUNC to sanity 
	check.
	(mips_elf_create_iplt): New function.
	(_bfd_mips_elf_finish_dynamic_symbol): If the symbol has been marked as,
	needing an iplt call the iplt creation routine. 
	If the got symbol is an IFUNC, assign an R_MIPS_IRELATIVE relocation
	to it.
	Set the iplt symbol value to the iplt entry address.
	* libbfd.h
	(bfd_reloc_code_real_names): Entry for BFD_RELOC_MIPS_IRELATIVE.
	* reloc.c
	(ENUMDOC): BFD_RELOC_MIPS_IRELATIVE entry.

include/ChangeLog

2013-08-16  Jack Carter  <jack.carter@imgtec.com>

	* elf/mips.h
	(START_RELOC_NUMBERS): Entry for R_MIPS_IRELATIVE.
























diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 0cf9a29..bb2d0fc 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2966,6 +2966,9 @@ to compensate for the borrow when the low bits are added.  */
   BFD_RELOC_MOXIE_10_PCREL,
 
 
+/* MIPS support for STT_GNU_IFUNC.  */
+  BFD_RELOC_MIPS_IRELATIVE,
+
 /* Fujitsu Frv Relocations.  */
   BFD_RELOC_FRV_LABEL16,
   BFD_RELOC_FRV_LABEL24,
diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c
index 0e62708..20d9066 100644
--- a/bfd/elf32-mips.c
+++ b/bfd/elf32-mips.c
@@ -1554,6 +1554,22 @@ static reloc_howto_type elf_mips_eh_howto =
 	 0xffffffff,	        /* dst_mask */
 	 FALSE);		/* pcrel_offset */
 
+/* STT_GNU_IFUNC support: */
+static reloc_howto_type elf_mips_irelative_howto =
+  HOWTO (R_MIPS_IRELATIVE,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield,/* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* special_function */
+	 "R_MIPS_IRELATIVE",	/* name */
+	 TRUE,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE);		/* pcrel_offset */
+
 /* Set the GP value for OUTPUT_BFD.  Returns FALSE if this is a
    dangerous relocation.  */
 
@@ -2028,6 +2044,8 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code)
       return &elf_mips_jump_slot_howto;
     case BFD_RELOC_MIPS_EH:
       return &elf_mips_eh_howto;
+    case BFD_RELOC_MIPS_IRELATIVE:
+      return &elf_mips_irelative_howto;
     }
 }
 
@@ -2075,6 +2093,8 @@ bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
     return &elf_mips_jump_slot_howto;
   if (strcasecmp (elf_mips_eh_howto.name, r_name) == 0)
     return &elf_mips_eh_howto;
+  if (strcasecmp (elf_mips_irelative_howto.name, r_name) == 0)
+    return &elf_mips_irelative_howto;
 
   return NULL;
 }
@@ -2101,6 +2121,8 @@ mips_elf32_rtype_to_howto (unsigned int r_type,
       return &elf_mips_jump_slot_howto;
     case R_MIPS_EH:
       return &elf_mips_eh_howto;
+    case R_MIPS_IRELATIVE:
+      return &elf_mips_irelative_howto;
     default:
       if (r_type >= R_MICROMIPS_min && r_type < R_MICROMIPS_max)
 	return &elf_micromips_howto_table_rel[r_type - R_MICROMIPS_min];
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index fd7900d..11009ad 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -372,6 +372,12 @@ struct mips_elf_link_hash_entry
      being called returns a floating point value.  */
   asection *call_fp_stub;
 
+  /* Offset into the IPLT table.  */
+  unsigned int iplt_offset;
+
+  /* Offset into the IGOT table.  */
+  unsigned int igot_offset;
+
   /* The highest GGA_* value that satisfies all references to this symbol.  */
   unsigned int global_got_area : 2;
 
@@ -410,6 +416,9 @@ struct mips_elf_link_hash_entry
 
   /* Does this symbol resolve to a PLT entry?  */
   unsigned int use_plt_entry : 1;
+
+  /* Does this symbol need an IPLT stub?  */
+  unsigned int needs_iplt: 1;
 };
 
 /* MIPS ELF linker hash table.  */
@@ -454,6 +463,9 @@ struct mips_elf_link_hash_table
   asection *srelplt2;
   asection *sgotplt;
   asection *splt;
+  asection *sigotplt;
+  asection *siplt;
+  asection *sreliplt;
   asection *sstubs;
   asection *sgot;
 
@@ -482,6 +494,9 @@ struct mips_elf_link_hash_table
   /* The index of the next .got.plt entry to create.  */
   bfd_vma plt_got_index;
 
+  /* The size of an IPLT entry in bytes.  */
+  bfd_vma iplt_entry_size;
+
   /* The number of functions that need a lazy-binding stub.  */
   bfd_vma lazy_stub_count;
 
@@ -741,6 +756,13 @@ static bfd_boolean mips_elf_create_dynamic_relocation
    bfd_vma *, asection *);
 static bfd_vma mips_elf_adjust_gp
   (bfd *, struct mips_got_info *, bfd *);
+static void
+mips_elf_allocate_ireloc
+  (struct bfd_link_info *, struct mips_elf_link_hash_entry *);
+static bfd_boolean
+mips_elf_allocate_iplt
+  (bfd *, struct mips_elf_link_hash_table *, struct bfd_link_info *,
+   struct mips_elf_link_hash_entry *);
 
 /* This will be used when we sort the dynamic relocation records.  */
 static bfd *reldyn_sorting_bfd;
@@ -811,6 +833,11 @@ static bfd *reldyn_sorting_bfd;
 /* The name of the stub section.  */
 #define MIPS_ELF_STUB_SECTION_NAME(abfd) ".MIPS.stubs"
 
+/* Return the relocation section associated with NAME.  HTAB is the
+   bfd's elf32_arm_link_hash_entry.  */
+#define MIPS_ELF_RELOC_SECTION(INFO, NAME) \
+  (mips_elf_hash_table (INFO)->is_vxworks ? ".rel" NAME : ".rela" NAME)
+
 /* The size of an external REL relocation.  */
 #define MIPS_ELF_REL_SIZE(abfd) \
   (get_elf_backend_data (abfd)->s->sizeof_rel)
@@ -957,6 +984,13 @@ static bfd *reldyn_sorting_bfd;
 #define ELF_R_INFO(bfd, s, t)					\
   (ELF32_R_INFO (s, t))
 #endif
+/* Is this a non-shared link?  */
+#define MIPS_STATIC_LINK(info)					\
+    ((elf_hash_table(info)->dynamic_sections_created) == FALSE)
+/* Is this a non-fPIC a.out?  */
+#define MIPS_AOUT_LINK(info)					\
+    (info->executable == TRUE)
+
 ?
   /* The mips16 compiler uses a couple of special sections to handle
      floating point arguments.
@@ -1157,6 +1191,29 @@ static const bfd_vma mips_vxworks_shared_plt_entry[] =
   0x10000000,	/* b .PLT_resolver	*/
   0x24180000	/* li t8, <pltindex>	*/
 };
+
+/* The format of non-dso IPLT entries.  */
+static const bfd_vma mips_exec_iplt_entry[] =
+{
+  0x3c0f0000,   /* lui $15, %hi(.got.iplt entry)	*/
+  0x01f90000,   /* l[wd] $25, %lo(.got.iplt entry)($15)	*/
+  0x03200008,   /* jr $25				*/
+  0x00000000	/* nop					*/
+};
+
+/* The format of dso IPLT entries.  */
+static const bfd_vma mips_dso_iplt_entry[] =
+{
+  0x03e07821,	/* addu t7, ra, r0				*/
+  0x04110001,	/* bal 8					*/
+  0x3c190000,	/* lui t9, %hi(<.got.plt slot offset>)		*/
+  0x033fc821,	/* addu t9, t9, ra				*/
+  0x8f390000,	/* l[wd] t9, %lo(<.got.plt slot offset>)(t9) 	*/
+  0x03200008,	/* jr $t9					*/
+  0x01e0f821,	/* addu ra, t7, r0				*/
+  0x00000000	/* nop						*/
+};
+
 ?
 /* microMIPS 32-bit opcode helper installer.  */
 
@@ -1255,6 +1312,9 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
       ret->has_nonpic_branches = FALSE;
       ret->needs_lazy_stub = FALSE;
       ret->use_plt_entry = FALSE;
+      ret->needs_iplt = FALSE;
+      ret->iplt_offset = -1;
+      ret->igot_offset = 0;
     }
 
   return (struct bfd_hash_entry *) ret;
@@ -1944,6 +2004,15 @@ mips_elf_check_symbols (struct mips_elf_link_hash_entry *h, void *data)
   if (!hti->info->relocatable)
     mips_elf_check_mips16_stubs (hti->info, h);
 
+  /* If the referenced symbol is ifunc, allocate an iplt stub for it.  */
+  if (h && (h->needs_iplt == FALSE) && (h->root.type == STT_GNU_IFUNC))
+    {
+      struct bfd_link_info *info = hti->info;
+      if (!(mips_elf_allocate_iplt (info->output_bfd,
+				    mips_elf_hash_table (info), info, h)))
+	return FALSE;
+    }
+
   if (mips_elf_local_pic_function_p (h))
     {
       /* PR 12845: If H is in a section that has been garbage
@@ -4975,6 +5044,70 @@ mips_elf_create_compact_rel_section
   return TRUE;
 }
 
+/* Create the .iplt, .rel(a).iplt and .igot.plt sections.  */
+
+static bfd_boolean
+mips_elf_create_ifunc_sections(bfd *abfd, struct bfd_link_info *info)
+{
+  struct mips_elf_link_hash_table * volatile htab;
+  const struct elf_backend_data *bed;
+  bfd *dynobj;
+  asection *s;
+  flagword flags;
+
+  /* Don't do anything if a dso link.  */
+  if (!MIPS_AOUT_LINK(info))
+    return TRUE;
+
+  htab = mips_elf_hash_table (info);
+  dynobj = (htab->root.dynobj) ? htab->root.dynobj : abfd;
+  bed = get_elf_backend_data (dynobj);
+  flags = bed->dynamic_sec_flags;
+
+  if (htab->root.iplt == NULL)
+    {
+      s = bfd_make_section_anyway_with_flags(dynobj, ".iplt",
+					     flags | SEC_READONLY | SEC_CODE);
+      if (s == NULL
+	  || !bfd_set_section_alignment(dynobj, s, bed->plt_alignment))
+	return FALSE;
+      htab->root.iplt = s;
+      htab->siplt = s;
+      if (!info->shared)
+	  htab->iplt_entry_size = 4 * ARRAY_SIZE (mips_exec_iplt_entry);
+      else
+	  htab->iplt_entry_size = 4 * ARRAY_SIZE (mips_dso_iplt_entry);
+    }
+
+  if (htab->root.igotplt == NULL )
+    {
+      s = bfd_make_section_anyway_with_flags(dynobj, ".igot.plt", flags);
+      if (s == NULL
+	  || !bfd_set_section_alignment(dynobj, s, bed->s->log_file_align))
+	return FALSE;
+      htab->root.igotplt = s;
+      htab->sigotplt = s;
+      mips_elf_section_data (s)->elf.this_hdr.sh_flags |= SHF_ALLOC |
+							  SHF_WRITE |
+							  SHF_MIPS_GPREL;
+    }
+
+  if (htab->sreliplt == NULL )
+    {
+      s = bfd_make_section_with_flags (dynobj,
+				       ".rel.iplt", /* This is magic currently */
+				       flags | SEC_READONLY);
+      if (s == NULL
+	  || !bfd_set_section_alignment(dynobj, s, bed->s->log_file_align))
+	return FALSE;
+      htab->sreliplt = s;
+      mips_elf_section_data (s)->elf.this_hdr.sh_entsize =
+	MIPS_ELF_REL_SIZE(dynobj);
+    }
+
+  return TRUE;
+}
+
 /* Create the .got section to hold the global offset table.  */
 
 static bfd_boolean
@@ -5261,12 +5394,21 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
 	       && h->root.root.u.def.section)
 	{
 	  sec = h->root.root.u.def.section;
-	  if (sec->output_section)
-	    symbol = (h->root.root.u.def.value
-		      + sec->output_section->vma
-		      + sec->output_offset);
+	  /* If this symbol is an ifunc, point to the iplt stub for it,  */
+	  if (h->needs_iplt)
+	    {
+	      BFD_ASSERT (htab->siplt != NULL);
+	      symbol = (htab->siplt->output_section->vma
+		  + htab->siplt->output_offset
+		  + h->iplt_offset);
+	    }
 	  else
-	    symbol = h->root.root.u.def.value;
+	    if (sec->output_section)
+	      symbol = (h->root.root.u.def.value
+		  + sec->output_section->vma
+		  + sec->output_offset);
+	    else
+	      symbol = h->root.root.u.def.value;
 	}
       else if (h->root.root.type == bfd_link_hash_undefweak)
 	/* We allow relocations against undefined weak symbols, giving
@@ -6119,6 +6261,70 @@ mips_elf_perform_relocation (struct bfd_link_info *info,
   return TRUE;
 }
 ?
+/* Ifunc variant of mips_elf_create_dynamic_relocation()  */
+static bfd_boolean
+ifunc_create_dynamic_relocation(bfd *output_bfd,
+    struct bfd_link_info *info,
+    Elf_Internal_Rela *outrel,
+    struct mips_elf_link_hash_entry *h,
+    bfd_vma symbol)
+{
+  long indx;
+  bfd_vma t_off;
+  struct mips_elf_link_hash_table *htab = mips_elf_hash_table (info);
+  asection *sreloc = mips_elf_rel_dyn_section (info, FALSE);
+
+  /* Adjust the output offset of the relocation to reference the
+	 correct location in the output file.  */
+  if (h->global_got_area == GGA_NONE)
+    {
+      t_off = mips_elf_local_got_index(output_bfd,
+				       output_bfd,
+				       info,
+				       symbol,
+				       h->root.dynindx,
+				       h,
+				       0);
+    }
+  else
+    {
+      t_off = mips_elf_global_got_index(output_bfd,
+					info,
+					output_bfd,
+					&h->root,
+					0);
+    }
+  t_off += (htab->sgot->output_section->vma +
+	    htab->sgot->output_offset);
+
+  outrel[0].r_offset = t_off;
+  outrel[1].r_offset = t_off;
+  outrel[2].r_offset = t_off;
+
+  indx = h->root.dynindx;
+  outrel[0].r_info = ELF_R_INFO (output_bfd, (unsigned long) indx,
+				 R_MIPS_IRELATIVE);
+
+  if (ABI_64_P (output_bfd))
+    {
+      (*get_elf_backend_data (output_bfd)->s->swap_reloc_out) (
+	output_bfd, &outrel[0],
+	(sreloc->contents
+	 + sreloc->reloc_count * sizeof (Elf64_Mips_External_Rel)));
+    }
+  else
+    {
+      bfd_elf32_swap_reloc_out (
+	output_bfd, &outrel[0],
+	(sreloc->contents
+	 + sreloc->reloc_count * sizeof (Elf32_External_Rel)));
+    }
+  /* We've now added another relocation.  */
+  ++sreloc->reloc_count;
+
+  return TRUE;
+}
+
 /* Create a rel.dyn relocation for the dynamic linker to resolve.  REL
    is the original relocation, which is now being transformed into a
    dynamic relocation.  The ADDENDP is adjusted if necessary; the
@@ -6161,6 +6367,9 @@ mips_elf_create_dynamic_relocation (bfd *output_bfd,
 	_bfd_elf_section_offset (output_bfd, info, input_section, rel[2].r_offset);
     }
 
+  if (r_type == R_MIPS_IRELATIVE)
+    return ifunc_create_dynamic_relocation(output_bfd, info, outrel, h, symbol);
+
   if (outrel[0].r_offset == MINUS_ONE)
     /* The relocation field has been deleted.  */
     return TRUE;
@@ -7146,6 +7355,10 @@ _bfd_mips_elf_add_symbol_hook (bfd *abfd, struct bfd_link_info *info,
 			       flagword *flagsp ATTRIBUTE_UNUSED,
 			       asection **secp, bfd_vma *valp)
 {
+
+  if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+    elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+
   if (SGI_COMPAT (abfd)
       && (abfd->flags & DYNAMIC) != 0
       && strcmp (*namep, "_rld_new_interface") == 0)
@@ -7659,6 +7872,40 @@ mips_elf_make_plt_record (bfd *abfd)
   return entry;
 }
 
+/* Reserve space in the iplt and igot tables for another ifunc entry.
+   Don't do anything if this is a dso link.  */
+static bfd_boolean
+mips_elf_allocate_iplt (bfd *abfd, struct mips_elf_link_hash_table *mhtab,
+			struct bfd_link_info *info,
+			struct mips_elf_link_hash_entry *mh)
+{
+
+  asection *s;
+
+  if (mh->needs_iplt || !MIPS_AOUT_LINK(info))
+    return TRUE;
+
+  BFD_ASSERT (mhtab->siplt != NULL);
+
+  s = mhtab->siplt;
+  mh->iplt_offset = s->size;
+  s->size += mhtab->iplt_entry_size;
+
+  /* Create a symbol for the stub.  */
+  mips_elf_create_stub_symbol (info, mh, ".iplt.",s, mh->iplt_offset,
+			       mhtab->iplt_entry_size);
+
+  BFD_ASSERT (mhtab->sigotplt != NULL);
+  mh->igot_offset = mhtab->sigotplt->size;
+  mhtab->sigotplt->size += MIPS_ELF_GOT_SIZE (abfd);
+  mips_elf_allocate_ireloc (info, mh);
+
+/* This should be the only place needs_iplt is set */
+  mh->needs_iplt = TRUE;
+
+  return TRUE;
+}
+
 /* Look through the relocs for a section during the first phase, and
    allocate space in the global offset table and record the need for
    standard MIPS and compressed procedure linkage table entries.  */
@@ -7695,6 +7942,14 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   bed = get_elf_backend_data (abfd);
   rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel;
 
+  /* This needs to happen early. If the sections aren't needed
+     they will not get generated.  */
+  if (htab->root.dynobj == NULL)
+    htab->root.dynobj = abfd;
+  if (!htab->siplt &&
+      !(mips_elf_create_ifunc_sections (htab->root.dynobj, info)))
+    return FALSE;
+
   /* Check for the mips16 stub sections.  */
 
   name = bfd_get_section_name (abfd, sec);
@@ -8177,10 +8432,14 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	      /* We need a stub, not a plt entry for the undefined
 		 function.  But we record it as if it needs plt.  See
-		 _bfd_elf_adjust_dynamic_symbol.  */
-	      h->needs_plt = 1;
-	      h->type = STT_FUNC;
-	    }
+		 _bfd_elf_adjust_dynamic_symbol.  If it is an ifunc
+		 symbol it will go into an iplt section and not plt.  */
+	      if (h->type != STT_GNU_IFUNC)
+		{
+		  h->needs_plt = 1;
+		  h->type = STT_FUNC;
+		}
+	      }
 	  break;
 
 	case R_MIPS_GOT_PAGE:
@@ -8635,6 +8894,28 @@ _bfd_mips_relax_section (bfd *abfd, asection *sec,
   return FALSE;
 }
 ?
+/* Reserve space for R_MIPS_IRELATIVE relocations.  If the link is
+   dynamic, the relocations should go in SRELOC, otherwise they should
+   go in the special .rel.iplt section.  */
+
+static void
+mips_elf_allocate_ireloc (struct bfd_link_info *info,
+			  struct mips_elf_link_hash_entry *hmips ATTRIBUTE_UNUSED)
+{
+  struct mips_elf_link_hash_table *htab;
+  asection *srel = NULL;
+  bfd *dynobj = elf_hash_table (info)->dynobj;
+  BFD_ASSERT (dynobj != NULL);
+
+  htab = mips_elf_hash_table (info);
+  srel = (MIPS_STATIC_LINK(info)) ? htab->sreliplt
+				  : mips_elf_rel_dyn_section (info, FALSE);
+  BFD_ASSERT (srel != NULL);
+
+  srel->size += ((htab->is_vxworks) ? MIPS_ELF_RELA_SIZE (dynobj)
+				    : MIPS_ELF_REL_SIZE (dynobj));
+}
+
 /* Allocate space for global sym dynamic relocs.  */
 
 static bfd_boolean
@@ -8651,6 +8932,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   dynobj = elf_hash_table (info)->dynobj;
   hmips = (struct mips_elf_link_hash_entry *) h;
 
+  /* Record any ifunc symbols */
+  if (h && hmips->needs_iplt)
+    {
+      mips_elf_allocate_ireloc (info, hmips);
+      return TRUE;
+    }
+
   /* VxWorks executables are handled elsewhere; we only need to
      allocate relocations in shared objects.  */
   if (htab->is_vxworks && !info->shared)
@@ -8740,6 +9028,7 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* Make sure we know what is going on here.  */
   BFD_ASSERT (dynobj != NULL
 	      && (h->needs_plt
+		  || h->type == STT_GNU_IFUNC
 		  || h->u.weakdef != NULL
 		  || (h->def_dynamic
 		      && h->ref_regular
@@ -10226,6 +10515,119 @@ mips_elf_irix6_finish_dynamic_symbol (bfd *abfd ATTRIBUTE_UNUSED,
 	}
 }
 
+/* Create the contents of a iplt entry for the ifunc symbol.  */
+
+static bfd_boolean
+mips_elf_create_iplt (bfd *output_bfd,
+		      bfd *dynobj,
+		      struct mips_elf_link_hash_table *htab,
+		      struct mips_elf_link_hash_entry *hmips,
+		      Elf_Internal_Sym *sym,
+		      struct bfd_link_info *info)
+{
+  /* We've decided to create an IPLT entry for this symbol.  */
+  bfd_byte *loc;
+//  bfd_vma iplt_index;
+  bfd_vma igot_index = 0;
+  bfd_vma igotplt_address = 0;
+  bfd_vma igotplt_address_high, igotplt_address_low, load;
+  const bfd_vma *iplt_entry;
+  asection *gotsect, *relsect;
+
+  gotsect = htab->sigotplt;
+  igot_index = hmips->igot_offset/MIPS_ELF_GOT_SIZE (dynobj);
+
+  relsect = (MIPS_STATIC_LINK(info)) ? htab->sreliplt
+				     : mips_elf_rel_dyn_section (info, FALSE);
+
+  /* Calculate the address of the .igot.plt entry.  */
+  igotplt_address = (gotsect->output_section->vma +
+		     gotsect->output_offset +
+		     hmips->igot_offset);
+
+  /* Initially point the .got.iplt entry at the user ifunc routine.  */
+  if (!gotsect->contents)
+    gotsect->contents = bfd_zalloc(output_bfd,gotsect->size);
+
+  loc = (bfd_byte *)gotsect->contents +
+	 igot_index*MIPS_ELF_GOT_SIZE (dynobj);
+
+  if (ABI_64_P (output_bfd))
+    bfd_put_64 (output_bfd, sym->st_value, loc);
+  else
+    bfd_put_32 (output_bfd, sym->st_value, loc);
+
+  /* Find out where the .iplt entry should go.  */
+  if (!htab->siplt->contents)
+    htab->siplt->contents = bfd_zalloc(output_bfd,htab->siplt->size);
+  loc = htab->siplt->contents + hmips->iplt_offset;
+
+  /* Pick the load opcode.  */
+  load = MIPS_ELF_LOAD_WORD (output_bfd);
+
+  /* Fill in the IPLT entry itself.  */
+  if (!info->shared)
+    {
+      /* Static and call shared.  */
+      igotplt_address_high = ((igotplt_address + 0x8000) >> 16) & 0xffff;
+      igotplt_address_low = igotplt_address & 0xffff;
+      iplt_entry = mips_exec_iplt_entry;
+      bfd_put_32 (output_bfd, iplt_entry[0] | igotplt_address_high, loc);
+      bfd_put_32 (output_bfd, iplt_entry[1] |
+		  igotplt_address_low | load, loc + 4);
+
+      if (LOAD_INTERLOCKS_P (output_bfd))
+	{
+	  bfd_put_32 (output_bfd, iplt_entry[2] , loc + 8);
+	  bfd_put_32 (output_bfd, iplt_entry[3], loc + 12);
+	}
+      else
+	{
+	  bfd_put_32 (output_bfd, iplt_entry[3], loc + 8);
+	  bfd_put_32 (output_bfd, iplt_entry[2] | igotplt_address_low, loc + 12);
+	}
+    }
+  else
+    {
+      /* Dsos. */
+      bfd_vma iplt_address = (htab->siplt->output_section->vma
+			      + htab->siplt->output_offset
+			      + hmips->iplt_offset
+			      + 8 /* bal target */);
+      bfd_vma igotplt_offset = igotplt_address - (iplt_address + MIPS_ELF_GOT_SIZE (dynobj));
+      igotplt_address_high = ((igotplt_offset + 0x8000) >> 16) & 0xffff;
+      igotplt_address_low = igotplt_offset & 0xffff;
+
+      iplt_entry = mips_dso_iplt_entry;
+      bfd_put_32 (output_bfd, iplt_entry[0], loc);
+      bfd_put_32 (output_bfd, iplt_entry[1] , loc + 4);
+      bfd_put_32 (output_bfd, iplt_entry[2] | igotplt_address_high, loc + 8);
+      bfd_put_32 (output_bfd, iplt_entry[3], loc + 12);
+      bfd_put_32 (output_bfd, iplt_entry[4] | igotplt_address_low, loc + 16);
+      bfd_put_32 (output_bfd, iplt_entry[5], loc + 20);
+      bfd_put_32 (output_bfd, iplt_entry[6], loc + 24);
+      bfd_put_32 (output_bfd, iplt_entry[7], loc + 28);
+    }
+
+  /* Emit an R_MIPS_IRELATIVE relocation against the igot entry.  */
+    if (relsect->contents == NULL)
+      {
+	/* Allocate memory for the relocation section contents.  */
+	relsect->contents = bfd_zalloc (dynobj, relsect->size);
+	if (relsect->contents == NULL)
+	  {
+	    bfd_set_error (bfd_error_no_memory);
+	    return FALSE;
+	  }
+      }
+    mips_elf_output_dynamic_relocation (output_bfd, relsect,
+					relsect->reloc_count++,
+					0 /* sym_indx */,
+                                        R_MIPS_IRELATIVE, igotplt_address);
+
+  return TRUE;
+}
+
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
@@ -10550,6 +10952,17 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
       sym->st_other = other;
     }
 
+  /* IFUNC symbols get an iplt stub  */
+  if (hmips->needs_iplt)
+    {
+      if (!(mips_elf_create_iplt(output_bfd, dynobj, htab, hmips, sym, info)))
+	return FALSE;
+      if (MIPS_STATIC_LINK(info))
+	return TRUE;
+      if (h->dynindx == -1  && !h->forced_local)
+        return TRUE;
+    }
+
   /* If we have a MIPS16 function with a stub, the dynamic symbol must
      refer to the stub, since only the stub uses the standard calling
      conventions.  */
@@ -10616,7 +11029,10 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
 		  Elf_Internal_Rela rel[3];
 
 		  memset (rel, 0, sizeof (rel));
-		  if (ABI_64_P (output_bfd))
+		  if (hmips->needs_iplt)
+		    rel[0].r_info = ELF_R_INFO (output_bfd, 0,
+						R_MIPS_IRELATIVE);
+		  else if (ABI_64_P (output_bfd))
 		    rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_64);
 		  else
 		    rel[0].r_info = ELF_R_INFO (output_bfd, 0, R_MIPS_32);
@@ -10713,6 +11129,14 @@ _bfd_mips_elf_finish_dynamic_symbol (bfd *output_bfd,
       sym->st_other -= STO_MICROMIPS;
     }
 
+  if (hmips->needs_iplt)
+    {
+      /* Point at the iplt stub for this ifunc symbol.  */
+      sym->st_value = htab->siplt->output_section->vma +
+		      htab->siplt->output_offset +
+		      hmips->iplt_offset;
+      sym->st_info = ELF_ST_INFO (STB_GLOBAL, STT_FUNC);
+    }
   return TRUE;
 }
 
@@ -11154,7 +11578,6 @@ _bfd_mips_elf_finish_dynamic_sections (bfd *output_bfd,
 	      s = htab->sgotplt;
 	      dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
 	      break;
-
 	    case DT_MIPS_RLD_VERSION:
 	      dyn.d_un.d_val = 1; /* XXX */
 	      break;
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 1381803..c6607bf 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -1194,6 +1194,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
 
   "BFD_RELOC_MOXIE_10_PCREL",
 
+  "BFD_RELOC_MIPS_IRELATIVE",
   "BFD_RELOC_FRV_LABEL16",
   "BFD_RELOC_FRV_LABEL24",
   "BFD_RELOC_FRV_LO16",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 25c089c..493fe54 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2434,6 +2434,11 @@ ENUMDOC
 COMMENT
 
 ENUM
+  BFD_RELOC_MIPS_IRELATIVE
+ENUMDOC
+  MIPS support for STT_GNU_IFUNC.
+
+ENUM
   BFD_RELOC_FRV_LABEL16
 ENUMX
   BFD_RELOC_FRV_LABEL24
diff --git a/include/elf/mips.h b/include/elf/mips.h
index d25e773..fbcbe62 100644
--- a/include/elf/mips.h
+++ b/include/elf/mips.h
@@ -110,6 +110,9 @@ START_RELOC_NUMBERS (elf_mips_reloc_type)
   RELOC_NUMBER (R_MIPS_COPY, 126)
   RELOC_NUMBER (R_MIPS_JUMP_SLOT, 127)
 
+  /* STT_GNU_IFUNC support */
+  RELOC_NUMBER (R_MIPS_IRELATIVE, 128)
+
   /* These relocations are specific to microMIPS.  */
   FAKE_RELOC (R_MICROMIPS_min, 130)
   RELOC_NUMBER (R_MICROMIPS_26_S1, 133)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]