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]

Re: Add support for STT_GNU_IFUNC?


Hi H.J.

If you check in the initial STT_GNU_IFUNC support and open a bug report,
more people can take a look at it. I never expect any new feature will
be bug free in any software.

OK - I have checked in the attached patch. I am still not happy with it, but at least it is a starting point.


Cheers
  Nick
kc

include/elf
2009-04-30  Nick Clifton  <nickc@redhat.com>

	* common.h (STT_GNU_IFUNC): Define.

elfcpp
2009-04-30  Nick Clifton  <nickc@redhat.com>

	* (enum STT): Add STT_GNU_IFUNC.

gas
2009-04-30  Nick Clifton  <nickc@redhat.com>

	* config/obj-elf.c (obj_elf_type): Add support for a
	gnu_indirect_function type.
	* config/tc-i386.c (tc_i386_fix_adjustable): Do not adjust fixups
	against indirect function symbols.
	* doc/as.texinfo (.type): Document the support for the
	gnu_indirect_function symbol type.
	* NEWS: Mention the new feature.

gas/testsuite
2009-04-30  Nick Clifton  <nickc@redhat.com>

	* gas/elf/elf.exp: Extend type test to include an ifunc symbol.
	Provide an alternative test for targets which do not support ifunc
	symbols.
	(type.s): Add entry for an ifunc symbol.
	(type.e): Add ifunc entry to expected symbol dump.
	(section2.e-armelf): Add  entry for ifunc symbol.
	(type-noifunc.s): New file.
	(type-noifunc.e): New file.

bfd/
2009-04-30  Nick Clifton  <nickc@redhat.com>

	* elf-bfd.h (struct bfd_elf_section_data): Add indirect_relocs
	section pointer.
	(struct elf_obj_data): Add has_ifunc_symbols boolean.
	* elf.c (swap_out_syms): Convert BSF_GNU_INDIRECT_FUNCTION flags
	into a STT_GNU_IFUNC symbol type.
	(_bfd_elf_is_function_type): Accept STT_GNU_IFUNC as a function
	type.
	(_bfd_elf_set_osabi): Set the osasbi field to ELFOSABI_LINUX if
	the binary contains ifunc symbols.
	* elfcode.h (elf_slurp_symbol_table): Translate the STT_GNU_IFUNC
	symbol type into a BSF_GNU_INDIRECT_FUNCTION flag.
	* elf32-i386.c (is_indirect_function): New function.
	(elf_i386_check_relocs): Create an ifunc output section.
	(allocate_dynrelocs): Create dynamic relocs in the ifunc output
	section if necessary.
	(elf_i386_relocate_section): Emit a reloc against an ifunc symbol
	if necessary.
	(elf_i386_add_symbol_hook): New function. Set the
	has_ifunc_symbols field of the elf_obj_data structure if an ifunc
	symbol is encountered.
	(elf_backend_post_process_headers): Define.
	(elf_backend_add_symbol_hook): Define.
	(elf_i386_post_process_headers): Rename to
	elf_i388_fbsd_post_process_headers.
	* elf64-x86_64.c (IS_X86_64_PCREL_TYPE): New macro.
	(is_indirect_function): New function.
	(elf64_x86_64_check_relocs): Create an ifunc output section.
	(allocate_dynrelocs): Create dynamic relocs in the ifunc output
	section if necessary.
	(elf64_x86_64_relocate_section): Emit a reloc against an ifunc
	symbol if necessary.
	(elf_i386_add_symbol_hook): Set the has_ifunc_symbols field of the
	elf_obj_data structure if an ifunc symbol is encountered.
	(elf_backend_post_process_headers): Define.
	* elflink.c (_bfd_elf_adjust_dynamic_symbol): Always create a PLT
	if we have ifunc symbols to handle.
	(get_ifunc_reloc_section_name): New function.  Computes the name
	for an ifunc section.
	(_bfd_elf_make_ifunc_reloc_section): New function.  Creates a
	section to hold ifunc relocs.
	* syms.c (BSF_GNU_INDIRECT_FUNCTION): Define.
	(bfd_print_symbol_vandf): Handle ifunc symbols.
	(bfd_decode_symclass): Likewise.
	* bfd-in2.h: Regenerate.

binutils
2009-04-30  Nick Clifton  <nickc@redhat.com>

	* readelf.c (dump_relocations): Display a relocation against an
	ifunc symbol as if it were a function invocation.
	(get_symbol_type): Handle STT_GNU_IFUNC.

ld
2009-04-30  Nick Clifton  <nickc@redhat.com>

	* NEWS: Mention support for IFUNC symbols.

ld/testsuite
2009-04-30  Nick Clifton  <nickc@redhat.com>

	* ld-ifunc: New directory.
	* ld-ifunc/ifunc.exp: New file: Run the IFUNC tests.
	* ld-ifunc/prog.c: New file.
	* ld-ifunc/lib.c: New file.

Index: bfd/bfd-in2.h
===================================================================
RCS file: /cvs/src/src/bfd/bfd-in2.h,v
retrieving revision 1.476
diff -c -3 -p -r1.476 bfd-in2.h
*** bfd/bfd-in2.h	27 Apr 2009 14:27:34 -0000	1.476
--- bfd/bfd-in2.h	30 Apr 2009 14:00:16 -0000
*************** typedef struct bfd_symbol
*** 4608,4613 ****
--- 4608,4619 ----
    /* This symbol was created by bfd_get_synthetic_symtab.  */
  #define BSF_SYNTHETIC          (1 << 21)
  
+   /* This symbol is an indirect code object.  Unrelated to BSF_INDIRECT.
+      The dynamic linker will compute the value of this symbol by
+      calling the function that it points to.  BSF_FUNCTION must
+      also be also set.  */
+ #define BSF_GNU_INDIRECT_FUNCTION (1 << 22)
+ 
    flagword flags;
  
    /* A pointer to the section to which this symbol is
Index: bfd/elf-bfd.h
===================================================================
RCS file: /cvs/src/src/bfd/elf-bfd.h,v
retrieving revision 1.281
diff -c -3 -p -r1.281 elf-bfd.h
*** bfd/elf-bfd.h	13 Mar 2009 11:34:42 -0000	1.281
--- bfd/elf-bfd.h	30 Apr 2009 14:00:17 -0000
*************** struct bfd_elf_section_data
*** 1297,1302 ****
--- 1297,1305 ----
    /* A pointer to the bfd section used for dynamic relocs.  */
    asection *sreloc;
  
+   /* A pointer to the bfd section used for dynamic relocs against ifunc symbols.  */
+   asection *indirect_relocs;
+ 
    union {
      /* Group name, if this section is a member of a group.  */
      const char *name;
*************** struct elf_obj_tdata
*** 1559,1564 ****
--- 1562,1572 ----
    bfd_size_type build_id_size;
    bfd_byte *build_id;
  
+   /* True if the bfd contains symbols that have the STT_GNU_IFUNC
+      symbol type.  Used to set the osabi field in the ELF header
+      structure.  */
+   bfd_boolean has_ifunc_symbols;
+ 
    /* An identifier used to distinguish different target
       specific extensions to this structure.  */
    enum elf_object_id object_id;
*************** extern int _bfd_elf_obj_attrs_arg_type (
*** 2139,2144 ****
--- 2147,2155 ----
  extern void _bfd_elf_parse_attributes (bfd *, Elf_Internal_Shdr *);
  extern bfd_boolean _bfd_elf_merge_object_attributes (bfd *, bfd *);
  
+ extern asection * _bfd_elf_make_ifunc_reloc_section
+   (bfd *, asection *, bfd *, unsigned int);
+ 
  /* Large common section.  */
  extern asection _bfd_elf_large_com_section;
  
Index: bfd/elf.c
===================================================================
RCS file: /cvs/src/src/bfd/elf.c,v
retrieving revision 1.480
diff -c -3 -p -r1.480 elf.c
*** bfd/elf.c	26 Mar 2009 12:23:52 -0000	1.480
--- bfd/elf.c	30 Apr 2009 14:00:20 -0000
*************** Unable to find equivalent output section
*** 6399,6404 ****
--- 6399,6406 ----
  
        if ((flags & BSF_THREAD_LOCAL) != 0)
  	type = STT_TLS;
+       else if ((flags & BSF_GNU_INDIRECT_FUNCTION) != 0)
+ 	type = STT_GNU_IFUNC;
        else if ((flags & BSF_FUNCTION) != 0)
  	type = STT_FUNC;
        else if ((flags & BSF_OBJECT) != 0)
*************** _bfd_elf_set_osabi (bfd * abfd,
*** 8977,8991 ****
    i_ehdrp = elf_elfheader (abfd);
  
    i_ehdrp->e_ident[EI_OSABI] = get_elf_backend_data (abfd)->elf_osabi;
  }
  
  
  /* Return TRUE for ELF symbol types that represent functions.
     This is the default version of this function, which is sufficient for
!    most targets.  It returns true if TYPE is STT_FUNC.  */
  
  bfd_boolean
  _bfd_elf_is_function_type (unsigned int type)
  {
!   return (type == STT_FUNC);
  }
--- 8979,9001 ----
    i_ehdrp = elf_elfheader (abfd);
  
    i_ehdrp->e_ident[EI_OSABI] = get_elf_backend_data (abfd)->elf_osabi;
+ 
+   /* To make things simpler for the loader on Linux systems we set the
+      osabi field to ELFOSABI_LINUX if the binary contains symbols of
+      the STT_GNU_IFUNC type.  */
+   if (i_ehdrp->e_ident[EI_OSABI] == ELFOSABI_NONE
+       && elf_tdata (abfd)->has_ifunc_symbols)
+     i_ehdrp->e_ident[EI_OSABI] = ELFOSABI_LINUX;
  }
  
  
  /* Return TRUE for ELF symbol types that represent functions.
     This is the default version of this function, which is sufficient for
!    most targets.  It returns true if TYPE is STT_FUNC or STT_GNU_IFUNC.  */
  
  bfd_boolean
  _bfd_elf_is_function_type (unsigned int type)
  {
!   return (type == STT_FUNC
! 	  || type == STT_GNU_IFUNC);
  }
Index: bfd/elf32-i386.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-i386.c,v
retrieving revision 1.193
diff -c -3 -p -r1.193 elf32-i386.c
*** bfd/elf32-i386.c	12 Mar 2009 17:13:41 -0000	1.193
--- bfd/elf32-i386.c	30 Apr 2009 14:00:21 -0000
*************** elf_i386_tls_transition (struct bfd_link
*** 1196,1201 ****
--- 1196,1220 ----
    return TRUE;
  }
  
+ /* Returns true if the hash entry refers to a symbol
+    marked for indirect handling during reloc processing.  */
+ 
+ static bfd_boolean
+ is_indirect_symbol (bfd * abfd, struct elf_link_hash_entry * h)
+ {
+   const struct elf_backend_data * bed;
+ 
+   if (abfd == NULL || h == NULL)
+     return FALSE;
+ 
+   bed = get_elf_backend_data (abfd);
+ 
+   return h->type == STT_GNU_IFUNC
+     && (bed->elf_osabi == ELFOSABI_LINUX
+ 	/* GNU/Linux is still using the default value 0.  */
+ 	|| bed->elf_osabi == ELFOSABI_NONE);
+ }
+ 
  /* Look through the relocs for a section during the first phase, and
     calculate needed space in the global offset table, procedure linkage
     table, and dynamic reloc sections.  */
*************** elf_i386_check_relocs (bfd *abfd,
*** 1473,1478 ****
--- 1492,1503 ----
  
  		  if (sreloc == NULL)
  		    return FALSE;
+ 
+ 		  /* Create the ifunc section as well, even if we have not encountered a
+ 		     indirect function symbol yet.  We may not even see one in the input
+ 		     object file, but we can still encounter them in libraries.  */
+ 		  (void) _bfd_elf_make_ifunc_reloc_section
+ 		    (abfd, sec, htab->elf.dynobj, 2);
  		}
  
  	      /* If this is a global symbol, we count the number of
*************** allocate_dynrelocs (struct elf_link_hash
*** 1815,1820 ****
--- 1840,1846 ----
    struct elf_i386_link_hash_table *htab;
    struct elf_i386_link_hash_entry *eh;
    struct elf_i386_dyn_relocs *p;
+   bfd_boolean use_indirect_section = FALSE;
  
    if (h->root.type == bfd_link_hash_indirect)
      return TRUE;
*************** allocate_dynrelocs (struct elf_link_hash
*** 2036,2041 ****
--- 2062,2077 ----
  	    }
  	}
      }
+   else if (is_indirect_symbol (info->output_bfd, h)
+ 	   && h->dynindx == -1
+ 	   && ! h->forced_local)
+     {
+       if (bfd_elf_link_record_dynamic_symbol (info, h)
+ 	  && h->dynindx != -1)
+ 	use_indirect_section = TRUE;
+       else
+ 	return FALSE;
+     }
    else if (ELIMINATE_COPY_RELOCS)
      {
        /* For the non-shared case, discard space for relocs against
*************** allocate_dynrelocs (struct elf_link_hash
*** 2074,2080 ****
      {
        asection *sreloc;
  
!       sreloc = elf_section_data (p->sec)->sreloc;
  
        BFD_ASSERT (sreloc != NULL);
        sreloc->size += p->count * sizeof (Elf32_External_Rel);
--- 2110,2119 ----
      {
        asection *sreloc;
  
!       if (use_indirect_section)
! 	sreloc = elf_section_data (p->sec)->indirect_relocs;
!       else
!  	sreloc = elf_section_data (p->sec)->sreloc;
  
        BFD_ASSERT (sreloc != NULL);
        sreloc->size += p->count * sizeof (Elf32_External_Rel);
*************** elf_i386_relocate_section (bfd *output_b
*** 2877,2882 ****
--- 2916,2927 ----
  		   || h->root.type != bfd_link_hash_undefweak)
  	       && (r_type != R_386_PC32
  		   || !SYMBOL_CALLS_LOCAL (info, h)))
+ 	      || (! info->shared
+ 		  && h != NULL
+ 		  && h->dynindx != -1
+ 		  && ! h->forced_local
+ 		  && ((struct elf_i386_link_hash_entry *) h)->dyn_relocs != NULL
+ 		  && is_indirect_symbol (output_bfd, h))
  	      || (ELIMINATE_COPY_RELOCS
  		  && !info->shared
  		  && h != NULL
*************** elf_i386_relocate_section (bfd *output_b
*** 2925,2931 ****
  		  outrel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
  		}
  
! 	      sreloc = elf_section_data (input_section)->sreloc;
  
  	      BFD_ASSERT (sreloc != NULL && sreloc->contents != NULL);
  
--- 2970,2985 ----
  		  outrel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
  		}
  
! 	      if (! info->shared
! 		  && h != NULL
! 		  && h->dynindx != -1
! 		  && ! h->forced_local
! 		  && is_indirect_symbol (output_bfd, h)
! 		  && elf_section_data (input_section)->indirect_relocs != NULL
! 		  && elf_section_data (input_section)->indirect_relocs->contents != NULL)
! 		sreloc = elf_section_data (input_section)->indirect_relocs;
! 	      else
!  		sreloc = elf_section_data (input_section)->sreloc;
  
  	      BFD_ASSERT (sreloc != NULL && sreloc->contents != NULL);
  
*************** elf_i386_hash_symbol (struct elf_link_ha
*** 4045,4050 ****
--- 4099,4122 ----
    return _bfd_elf_hash_symbol (h);
  }
  
+ /* Hook called by the linker routine which adds symbols from an object
+    file.  */
+ 
+ static bfd_boolean
+ elf_i386_add_symbol_hook (bfd * abfd ATTRIBUTE_UNUSED,
+ 			  struct bfd_link_info * info ATTRIBUTE_UNUSED,
+ 			  Elf_Internal_Sym * sym,
+ 			  const char ** namep ATTRIBUTE_UNUSED,
+ 			  flagword * flagsp ATTRIBUTE_UNUSED,
+ 			  asection ** secp ATTRIBUTE_UNUSED,
+ 			  bfd_vma * valp ATTRIBUTE_UNUSED)
+ {
+   if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+     elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+ 
+   return TRUE;
+ }
+ 
  #define TARGET_LITTLE_SYM		bfd_elf32_i386_vec
  #define TARGET_LITTLE_NAME		"elf32-i386"
  #define ELF_ARCH			bfd_arch_i386
*************** elf_i386_hash_symbol (struct elf_link_ha
*** 4089,4094 ****
--- 4161,4169 ----
    ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
  #define elf_backend_plt_sym_val		      elf_i386_plt_sym_val
  #define elf_backend_hash_symbol		      elf_i386_hash_symbol
+ #define elf_backend_add_symbol_hook           elf_i386_add_symbol_hook
+ #undef	elf_backend_post_process_headers
+ #define	elf_backend_post_process_headers	_bfd_elf_set_osabi
  
  #include "elf32-target.h"
  
*************** elf_i386_hash_symbol (struct elf_link_ha
*** 4106,4120 ****
     executables and (for simplicity) also all other object files.  */
  
  static void
! elf_i386_post_process_headers (bfd *abfd,
! 			       struct bfd_link_info *info ATTRIBUTE_UNUSED)
  {
!   Elf_Internal_Ehdr *i_ehdrp;
  
-   i_ehdrp = elf_elfheader (abfd);
- 
-   /* Put an ABI label supported by FreeBSD >= 4.1.  */
-   i_ehdrp->e_ident[EI_OSABI] = get_elf_backend_data (abfd)->elf_osabi;
  #ifdef OLD_FREEBSD_ABI_LABEL
    /* The ABI label supported by FreeBSD <= 4.0 is quite nonstandard.  */
    memcpy (&i_ehdrp->e_ident[EI_ABIVERSION], "FreeBSD", 8);
--- 4181,4190 ----
     executables and (for simplicity) also all other object files.  */
  
  static void
! elf_i386_fbsd_post_process_headers (bfd *abfd, struct bfd_link_info *info)
  {
!   _bfd_elf_set_osabi (abfd, info);
  
  #ifdef OLD_FREEBSD_ABI_LABEL
    /* The ABI label supported by FreeBSD <= 4.0 is quite nonstandard.  */
    memcpy (&i_ehdrp->e_ident[EI_ABIVERSION], "FreeBSD", 8);
*************** elf_i386_post_process_headers (bfd *abfd
*** 4122,4131 ****
  }
  
  #undef	elf_backend_post_process_headers
! #define	elf_backend_post_process_headers	elf_i386_post_process_headers
  #undef	elf32_bed
  #define	elf32_bed				elf32_i386_fbsd_bed
  
  #include "elf32-target.h"
  
  /* VxWorks support.  */
--- 4192,4203 ----
  }
  
  #undef	elf_backend_post_process_headers
! #define	elf_backend_post_process_headers	elf_i386_fbsd_post_process_headers
  #undef	elf32_bed
  #define	elf32_bed				elf32_i386_fbsd_bed
  
+ #undef elf_backend_add_symbol_hook
+ 
  #include "elf32-target.h"
  
  /* VxWorks support.  */
Index: bfd/elf64-x86-64.c
===================================================================
RCS file: /cvs/src/src/bfd/elf64-x86-64.c,v
retrieving revision 1.152
diff -c -3 -p -r1.152 elf64-x86-64.c
*** bfd/elf64-x86-64.c	12 Mar 2009 17:13:41 -0000	1.152
--- bfd/elf64-x86-64.c	30 Apr 2009 14:00:22 -0000
*************** static reloc_howto_type x86_64_elf_howto
*** 161,166 ****
--- 161,172 ----
  	 FALSE)
  };
  
+ #define IS_X86_64_PCREL_TYPE(TYPE)	\
+   (   ((TYPE) == R_X86_64_PC8)		\
+    || ((TYPE) == R_X86_64_PC16)		\
+    || ((TYPE) == R_X86_64_PC32)		\
+    || ((TYPE) == R_X86_64_PC64))
+ 
  /* Map BFD relocs to the x86_64 elf relocs.  */
  struct elf_reloc_map
  {
*************** elf64_x86_64_tls_transition (struct bfd_
*** 977,982 ****
--- 983,1007 ----
    return TRUE;
  }
  
+ /* Returns true if the hash entry refers to a symbol
+    marked for indirect handling during reloc processing.  */
+ 
+ static bfd_boolean
+ is_indirect_symbol (bfd * abfd, struct elf_link_hash_entry * h)
+ {
+   const struct elf_backend_data * bed;
+ 
+   if (abfd == NULL || h == NULL)
+     return FALSE;
+ 
+   bed = get_elf_backend_data (abfd);
+ 
+   return h->type == STT_GNU_IFUNC
+     && (bed->elf_osabi == ELFOSABI_LINUX
+ 	/* GNU/Linux is still using the default value 0.  */
+ 	|| bed->elf_osabi == ELFOSABI_NONE);
+ }
+ 
  /* Look through the relocs for a section during the first phase, and
     calculate needed space in the global offset table, procedure
     linkage table, and dynamic reloc sections.  */
*************** elf64_x86_64_check_relocs (bfd *abfd, st
*** 1003,1009 ****
    sym_hashes = elf_sym_hashes (abfd);
  
    sreloc = NULL;
! 
    rel_end = relocs + sec->reloc_count;
    for (rel = relocs; rel < rel_end; rel++)
      {
--- 1028,1034 ----
    sym_hashes = elf_sym_hashes (abfd);
  
    sreloc = NULL;
!   
    rel_end = relocs + sec->reloc_count;
    for (rel = relocs; rel < rel_end; rel++)
      {
*************** elf64_x86_64_check_relocs (bfd *abfd, st
*** 1259,1271 ****
  	     may need to keep relocations for symbols satisfied by a
  	     dynamic library if we manage to avoid copy relocs for the
  	     symbol.  */
- 
  	  if ((info->shared
  	       && (sec->flags & SEC_ALLOC) != 0
! 	       && (((r_type != R_X86_64_PC8)
! 		    && (r_type != R_X86_64_PC16)
! 		    && (r_type != R_X86_64_PC32)
! 		    && (r_type != R_X86_64_PC64))
  		   || (h != NULL
  		       && (! SYMBOLIC_BIND (info, h)
  			   || h->root.type == bfd_link_hash_defweak
--- 1284,1292 ----
  	     may need to keep relocations for symbols satisfied by a
  	     dynamic library if we manage to avoid copy relocs for the
  	     symbol.  */
  	  if ((info->shared
  	       && (sec->flags & SEC_ALLOC) != 0
! 	       && (! IS_X86_64_PCREL_TYPE (r_type)
  		   || (h != NULL
  		       && (! SYMBOLIC_BIND (info, h)
  			   || h->root.type == bfd_link_hash_defweak
*************** elf64_x86_64_check_relocs (bfd *abfd, st
*** 1293,1298 ****
--- 1314,1325 ----
  
  		  if (sreloc == NULL)
  		    return FALSE;
+ 
+ 		  /* Create the ifunc section, even if we will not encounter an
+ 		     indirect function symbol.  We may not even see one in the input
+ 		     object file, but we can still encounter them in libraries.  */
+ 		  (void) _bfd_elf_make_ifunc_reloc_section
+ 		    (abfd, sec, htab->elf.dynobj, 2);
  		}
  
  	      /* If this is a global symbol, we count the number of
*************** elf64_x86_64_check_relocs (bfd *abfd, st
*** 1324,1329 ****
--- 1351,1357 ----
  	      if (p == NULL || p->sec != sec)
  		{
  		  bfd_size_type amt = sizeof *p;
+ 
  		  p = ((struct elf64_x86_64_dyn_relocs *)
  		       bfd_alloc (htab->elf.dynobj, amt));
  		  if (p == NULL)
*************** elf64_x86_64_check_relocs (bfd *abfd, st
*** 1336,1345 ****
  		}
  
  	      p->count += 1;
! 	      if (r_type == R_X86_64_PC8
! 		  || r_type == R_X86_64_PC16
! 		  || r_type == R_X86_64_PC32
! 		  || r_type == R_X86_64_PC64)
  		p->pc_count += 1;
  	    }
  	  break;
--- 1364,1370 ----
  		}
  
  	      p->count += 1;
! 	      if (IS_X86_64_PCREL_TYPE (r_type))
  		p->pc_count += 1;
  	    }
  	  break;
*************** allocate_dynrelocs (struct elf_link_hash
*** 1650,1655 ****
--- 1675,1681 ----
    struct elf64_x86_64_link_hash_table *htab;
    struct elf64_x86_64_link_hash_entry *eh;
    struct elf64_x86_64_dyn_relocs *p;
+   bfd_boolean use_indirect_section = FALSE;
  
    if (h->root.type == bfd_link_hash_indirect)
      return TRUE;
*************** allocate_dynrelocs (struct elf_link_hash
*** 1728,1734 ****
        && !info->shared
        && h->dynindx == -1
        && elf64_x86_64_hash_entry (h)->tls_type == GOT_TLS_IE)
!     h->got.offset = (bfd_vma) -1;
    else if (h->got.refcount > 0)
      {
        asection *s;
--- 1754,1762 ----
        && !info->shared
        && h->dynindx == -1
        && elf64_x86_64_hash_entry (h)->tls_type == GOT_TLS_IE)
!     {
!       h->got.offset = (bfd_vma) -1;
!     }
    else if (h->got.refcount > 0)
      {
        asection *s;
*************** allocate_dynrelocs (struct elf_link_hash
*** 1827,1839 ****
  	  /* Make sure undefined weak symbols are output as a dynamic
  	     symbol in PIEs.  */
  	  else if (h->dynindx == -1
! 		   && !h->forced_local)
! 	    {
! 	      if (! bfd_elf_link_record_dynamic_symbol (info, h))
! 		return FALSE;
! 	    }
  	}
      }
    else if (ELIMINATE_COPY_RELOCS)
      {
        /* For the non-shared case, discard space for relocs against
--- 1855,1875 ----
  	  /* Make sure undefined weak symbols are output as a dynamic
  	     symbol in PIEs.  */
  	  else if (h->dynindx == -1
! 		   && ! h->forced_local
! 		   && ! bfd_elf_link_record_dynamic_symbol (info, h))
! 	    return FALSE;
  	}
      }
+   else if (is_indirect_symbol (info->output_bfd, h)
+ 	   && h->dynindx == -1
+ 	   && ! h->forced_local)
+     {
+       if (bfd_elf_link_record_dynamic_symbol (info, h)
+ 	  && h->dynindx != -1)
+ 	use_indirect_section = TRUE;
+       else
+ 	return FALSE;
+     }
    else if (ELIMINATE_COPY_RELOCS)
      {
        /* For the non-shared case, discard space for relocs against
*************** allocate_dynrelocs (struct elf_link_hash
*** 1850,1860 ****
  	  /* Make sure this symbol is output as a dynamic symbol.
  	     Undefined weak syms won't yet be marked as dynamic.  */
  	  if (h->dynindx == -1
! 	      && !h->forced_local)
! 	    {
! 	      if (! bfd_elf_link_record_dynamic_symbol (info, h))
! 		return FALSE;
! 	    }
  
  	  /* If that succeeded, we know we'll be keeping all the
  	     relocs.  */
--- 1889,1897 ----
  	  /* Make sure this symbol is output as a dynamic symbol.
  	     Undefined weak syms won't yet be marked as dynamic.  */
  	  if (h->dynindx == -1
! 	      && ! h->forced_local
! 	      && ! bfd_elf_link_record_dynamic_symbol (info, h))
! 	    return FALSE;
  
  	  /* If that succeeded, we know we'll be keeping all the
  	     relocs.  */
*************** allocate_dynrelocs (struct elf_link_hash
*** 1872,1878 ****
      {
        asection * sreloc;
  
!       sreloc = elf_section_data (p->sec)->sreloc;
  
        BFD_ASSERT (sreloc != NULL);
  
--- 1909,1918 ----
      {
        asection * sreloc;
  
!       if (use_indirect_section)
! 	sreloc = elf_section_data (p->sec)->indirect_relocs;
!       else
! 	sreloc = elf_section_data (p->sec)->sreloc;
  
        BFD_ASSERT (sreloc != NULL);
  
*************** elf64_x86_64_relocate_section (bfd *outp
*** 2674,2684 ****
  	       && (h == NULL
  		   || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
  		   || h->root.type != bfd_link_hash_undefweak)
! 	       && ((r_type != R_X86_64_PC8
! 		    && r_type != R_X86_64_PC16
! 		    && r_type != R_X86_64_PC32
! 		    && r_type != R_X86_64_PC64)
! 		   || !SYMBOL_CALLS_LOCAL (info, h)))
  	      || (ELIMINATE_COPY_RELOCS
  		  && !info->shared
  		  && h != NULL
--- 2714,2727 ----
  	       && (h == NULL
  		   || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
  		   || h->root.type != bfd_link_hash_undefweak)
! 	       && (! IS_X86_64_PCREL_TYPE (r_type)
! 		   || ! SYMBOL_CALLS_LOCAL (info, h)))
! 	      || (! info->shared
! 		  && h != NULL
! 		  && h->dynindx != -1
! 		  && ! h->forced_local
! 		  && ((struct elf64_x86_64_link_hash_entry *) h)->dyn_relocs != NULL
! 		  && is_indirect_symbol (output_bfd, h))
  	      || (ELIMINATE_COPY_RELOCS
  		  && !info->shared
  		  && h != NULL
*************** elf64_x86_64_relocate_section (bfd *outp
*** 2718,2730 ****
  		 become local.  */
  	      else if (h != NULL
  		       && h->dynindx != -1
! 		       && (r_type == R_X86_64_PC8
! 			   || r_type == R_X86_64_PC16
! 			   || r_type == R_X86_64_PC32
! 			   || r_type == R_X86_64_PC64
! 			   || !info->shared
! 			   || !SYMBOLIC_BIND (info, h)
! 			   || !h->def_regular))
  		{
  		  outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
  		  outrel.r_addend = rel->r_addend;
--- 2761,2770 ----
  		 become local.  */
  	      else if (h != NULL
  		       && h->dynindx != -1
! 		       && (IS_X86_64_PCREL_TYPE (r_type)
! 			   || ! info->shared
! 			   || ! SYMBOLIC_BIND (info, h)
! 			   || ! h->def_regular))
  		{
  		  outrel.r_info = ELF64_R_INFO (h->dynindx, r_type);
  		  outrel.r_addend = rel->r_addend;
*************** elf64_x86_64_relocate_section (bfd *outp
*** 2773,2780 ****
  		    }
  		}
  
! 	      sreloc = elf_section_data (input_section)->sreloc;
! 		
  	      BFD_ASSERT (sreloc != NULL && sreloc->contents != NULL);
  
  	      loc = sreloc->contents;
--- 2813,2830 ----
  		    }
  		}
  
! 	      if (! info->shared
! 		  && h != NULL
! 		  && h->dynindx != -1
! 		  && ! h->forced_local
! 		  && is_indirect_symbol (output_bfd, h)
! 		  && elf_section_data (input_section)->indirect_relocs != NULL
! 		  && elf_section_data (input_section)->indirect_relocs->contents != NULL
! 		  )
! 		sreloc = elf_section_data (input_section)->indirect_relocs;
! 	      else
! 		sreloc = elf_section_data (input_section)->sreloc;
! 
  	      BFD_ASSERT (sreloc != NULL && sreloc->contents != NULL);
  
  	      loc = sreloc->contents;
*************** elf64_x86_64_section_from_shdr (bfd *abf
*** 3660,3670 ****
  
  static bfd_boolean
  elf64_x86_64_add_symbol_hook (bfd *abfd,
! 			      struct bfd_link_info *info ATTRIBUTE_UNUSED,
  			      Elf_Internal_Sym *sym,
  			      const char **namep ATTRIBUTE_UNUSED,
  			      flagword *flagsp ATTRIBUTE_UNUSED,
! 			      asection **secp, bfd_vma *valp)
  {
    asection *lcomm;
  
--- 3712,3723 ----
  
  static bfd_boolean
  elf64_x86_64_add_symbol_hook (bfd *abfd,
! 			      struct bfd_link_info *info,
  			      Elf_Internal_Sym *sym,
  			      const char **namep ATTRIBUTE_UNUSED,
  			      flagword *flagsp ATTRIBUTE_UNUSED,
! 			      asection **secp,
! 			      bfd_vma *valp)
  {
    asection *lcomm;
  
*************** elf64_x86_64_add_symbol_hook (bfd *abfd,
*** 3687,3692 ****
--- 3740,3749 ----
        *valp = sym->st_size;
        break;
      }
+ 
+   if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+     elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+ 
    return TRUE;
  }
  
*************** static const struct bfd_elf_special_sect
*** 3914,3919 ****
--- 3971,3979 ----
  #define elf_backend_hash_symbol \
    elf64_x86_64_hash_symbol
  
+ #undef  elf_backend_post_process_headers
+ #define elf_backend_post_process_headers  _bfd_elf_set_osabi
+ 
  #include "elf64-target.h"
  
  /* FreeBSD support.  */
*************** static const struct bfd_elf_special_sect
*** 3926,3934 ****
  #undef	ELF_OSABI
  #define	ELF_OSABI			    ELFOSABI_FREEBSD
  
- #undef  elf_backend_post_process_headers
- #define elf_backend_post_process_headers  _bfd_elf_set_osabi
- 
  #undef  elf64_bed
  #define elf64_bed elf64_x86_64_fbsd_bed
  
--- 3986,3991 ----
Index: bfd/elfcode.h
===================================================================
RCS file: /cvs/src/src/bfd/elfcode.h,v
retrieving revision 1.93
diff -c -3 -p -r1.93 elfcode.h
*** bfd/elfcode.h	23 Dec 2008 09:01:45 -0000	1.93
--- bfd/elfcode.h	30 Apr 2009 14:00:26 -0000
*************** elf_slurp_symbol_table (bfd *abfd, asymb
*** 1311,1316 ****
--- 1311,1319 ----
  	    case STT_SRELC:
  	      sym->symbol.flags |= BSF_SRELC;
  	      break;
+ 	    case STT_GNU_IFUNC:
+ 	      sym->symbol.flags |= BSF_GNU_INDIRECT_FUNCTION;
+ 	      break;
  	    }
  
  	  if (dynamic)
Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/src/bfd/elflink.c,v
retrieving revision 1.334
diff -c -3 -p -r1.334 elflink.c
*** bfd/elflink.c	8 Apr 2009 16:04:48 -0000	1.334
--- bfd/elflink.c	30 Apr 2009 14:00:26 -0000
*************** _bfd_elf_adjust_dynamic_symbol (struct e
*** 2749,2754 ****
--- 2749,2761 ----
    dynobj = elf_hash_table (eif->info)->dynobj;
    bed = get_elf_backend_data (dynobj);
  
+ 
+   if (h->type == STT_GNU_IFUNC
+       && (bed->elf_osabi == ELFOSABI_LINUX
+ 	  /* GNU/Linux is still using the default value 0.  */
+ 	  || bed->elf_osabi == ELFOSABI_NONE))
+     h->needs_plt = 1;
+ 
    if (! (*bed->elf_backend_adjust_dynamic_symbol) (eif->info, h))
      {
        eif->failed = TRUE;
*************** _bfd_elf_make_dynamic_reloc_section (ase
*** 12533,12535 ****
--- 12540,12609 ----
  
    return reloc_sec;
  }
+ 
+ /* Returns the name of the ifunc using dynamic reloc section associated with SEC.  */
+ #define IFUNC_INFIX ".ifunc"
+ 
+ static const char *
+ get_ifunc_reloc_section_name (bfd *       abfd,
+ 			      asection *  sec)
+ {
+   const char *  dot;
+   char *  name;
+   const char *  base_name;
+   unsigned int  strndx = elf_elfheader (abfd)->e_shstrndx;
+   unsigned int  shnam = elf_section_data (sec)->rel_hdr.sh_name;
+ 
+   base_name = bfd_elf_string_from_elf_section (abfd, strndx, shnam);
+   if (base_name == NULL)
+     return NULL;
+ 
+   dot = strchr (base_name + 1, '.');
+   name = bfd_alloc (abfd, strlen (base_name) + strlen (IFUNC_INFIX) + 1);
+   sprintf (name, "%.*s%s%s", (int)(dot - base_name), base_name, IFUNC_INFIX, dot);
+ 
+   return name;
+ }
+ 
+ /* Like _bfd_elf_make_dynamic_reloc_section but it creates a
+    section for holding relocs against symbols with the STT_GNU_IFUNC
+    type.  The section is attached to the OWNER bfd but it is created
+    with a name based on SEC from ABFD.  */
+ 
+ asection *
+ _bfd_elf_make_ifunc_reloc_section (bfd *         abfd,
+ 				   asection *    sec,
+ 				   bfd *         owner,
+ 				   unsigned int  align)
+ {
+   asection * reloc_sec = elf_section_data (sec)->indirect_relocs;
+ 
+   if (reloc_sec == NULL)
+     {
+       const char * name = get_ifunc_reloc_section_name (abfd, sec);
+ 
+       if (name == NULL)
+ 	return NULL;
+ 
+       reloc_sec = bfd_get_section_by_name (owner, name);
+ 
+       if (reloc_sec == NULL)
+ 	{
+ 	  flagword flags;
+ 
+ 	  flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ 	  if ((sec->flags & SEC_ALLOC) != 0)
+ 	    flags |= SEC_ALLOC | SEC_LOAD;
+ 
+ 	  reloc_sec = bfd_make_section_with_flags (owner, name, flags);
+ 	  
+ 	  if (reloc_sec != NULL
+ 	      && ! bfd_set_section_alignment (owner, reloc_sec, align))
+ 	    reloc_sec = NULL;
+ 	}
+ 
+       elf_section_data (sec)->indirect_relocs = reloc_sec;
+     }
+ 
+   return reloc_sec;
+ }
Index: bfd/syms.c
===================================================================
RCS file: /cvs/src/src/bfd/syms.c,v
retrieving revision 1.50
diff -c -3 -p -r1.50 syms.c
*** bfd/syms.c	13 Jan 2009 23:44:45 -0000	1.50
--- bfd/syms.c	30 Apr 2009 14:00:26 -0000
*************** CODE_FRAGMENT
*** 297,302 ****
--- 297,308 ----
  .  {* This symbol was created by bfd_get_synthetic_symtab.  *}
  .#define BSF_SYNTHETIC		(1 << 21)
  .
+ .  {* This symbol is an indirect code object.  Unrelated to BSF_INDIRECT.
+ .     The dynamic linker will compute the value of this symbol by
+ .     calling the function that it points to.  BSF_FUNCTION must
+ .     also be also set.  *}
+ .#define BSF_GNU_INDIRECT_FUNCTION (1 << 22)
+ .
  .  flagword flags;
  .
  .  {* A pointer to the section to which this symbol is
*************** bfd_print_symbol_vandf (bfd *abfd, void 
*** 483,489 ****
  	   (type & BSF_WEAK) ? 'w' : ' ',
  	   (type & BSF_CONSTRUCTOR) ? 'C' : ' ',
  	   (type & BSF_WARNING) ? 'W' : ' ',
! 	   (type & BSF_INDIRECT) ? 'I' : ' ',
  	   (type & BSF_DEBUGGING) ? 'd' : (type & BSF_DYNAMIC) ? 'D' : ' ',
  	   ((type & BSF_FUNCTION)
  	    ? 'F'
--- 489,495 ----
  	   (type & BSF_WEAK) ? 'w' : ' ',
  	   (type & BSF_CONSTRUCTOR) ? 'C' : ' ',
  	   (type & BSF_WARNING) ? 'W' : ' ',
! 	   (type & BSF_INDIRECT) ? 'I' : (type & BSF_GNU_INDIRECT_FUNCTION) ? 'i' : ' ',
  	   (type & BSF_DEBUGGING) ? 'd' : (type & BSF_DYNAMIC) ? 'D' : ' ',
  	   ((type & BSF_FUNCTION)
  	    ? 'F'
*************** bfd_decode_symclass (asymbol *symbol)
*** 669,674 ****
--- 675,682 ----
      }
    if (bfd_is_ind_section (symbol->section))
      return 'I';
+   if (symbol->flags & BSF_GNU_INDIRECT_FUNCTION)
+     return 'i';
    if (symbol->flags & BSF_WEAK)
      {
        /* If weak, determine if it's specifically an object
Index: binutils/readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.442
diff -c -3 -p -r1.442 readelf.c
*** binutils/readelf.c	25 Mar 2009 01:37:18 -0000	1.442
--- binutils/readelf.c	30 Apr 2009 14:00:29 -0000
*************** dump_relocations (FILE * file,
*** 1238,1246 ****
  
  	      printf (" ");
  
! 	      print_vma (psym->st_value, LONG_HEX);
  
! 	      printf (is_32bit_elf ? "   " : " ");
  
  	      if (psym->st_name == 0)
  		{
--- 1238,1275 ----
  
  	      printf (" ");
  
! 	      if (ELF_ST_TYPE (psym->st_info) == STT_GNU_IFUNC)
! 		{
! 		  const char * name;
! 		  unsigned int len;
! 		  unsigned int width = is_32bit_elf ? 8 : 14;
! 
! 		  /* Relocations against GNU_IFUNC symbols do not use the value
! 		     of the symbol as the address to relocate against.  Instead
! 		     they invoke the function named by the symbol and use its
! 		     result as the address for relocation.
! 
! 		     To indicate this to the user, do not display the value of
! 		     the symbol in the "Symbols's Value" field.  Instead show
! 		     its name followed by () as a hint that the symbol is
! 		     invoked.  */
! 
! 		  if (strtab == NULL
! 		      || psym->st_name == 0
! 		      || psym->st_name >= strtablen)
! 		    name = "??";
! 		  else
! 		    name = strtab + psym->st_name;
  
! 		  len = print_symbol (width, name);
! 		  printf ("()%-*s", len <= width ? (width + 1) - len : 1, " ");
! 		}
! 	      else
! 		{
! 		  print_vma (psym->st_value, LONG_HEX);
! 
! 		  printf (is_32bit_elf ? "   " : " ");
! 		}
  
  	      if (psym->st_name == 0)
  		{
*************** get_symbol_type (unsigned int type)
*** 6922,6927 ****
--- 6951,6962 ----
  		return "HP_STUB";
  	    }
  
+ 	  if (type == STT_GNU_IFUNC
+ 	      && (elf_header.e_ident[EI_OSABI] == ELFOSABI_LINUX
+ 		  /* GNU/Linux is still using the default value 0.  */
+ 		  || elf_header.e_ident[EI_OSABI] == ELFOSABI_NONE))
+ 	    return "IFUNC";
+ 
  	  snprintf (buff, sizeof (buff), _("<OS specific>: %d"), type);
  	}
        else
Index: elfcpp/elfcpp.h
===================================================================
RCS file: /cvs/src/src/elfcpp/elfcpp.h,v
retrieving revision 1.21
diff -c -3 -p -r1.21 elfcpp.h
*** elfcpp/elfcpp.h	20 Mar 2009 23:37:51 -0000	1.21
--- elfcpp/elfcpp.h	30 Apr 2009 14:00:29 -0000
*************** enum STT
*** 478,483 ****
--- 478,484 ----
    STT_COMMON = 5,
    STT_TLS = 6,
    STT_LOOS = 10,
+   STT_GNU_IFUNC = 10,
    STT_HIOS = 12,
    STT_LOPROC = 13,
    STT_HIPROC = 15,
Index: gas/NEWS
===================================================================
RCS file: /cvs/src/src/gas/NEWS,v
retrieving revision 1.104
diff -c -3 -p -r1.104 NEWS
*** gas/NEWS	24 Apr 2009 18:31:28 -0000	1.104
--- gas/NEWS	30 Apr 2009 14:00:29 -0000
***************
*** 5,10 ****
--- 5,15 ----
  
  * Add support for Sunplus score architecture.
  
+ * The .type pseudo-op now accepts a type of STT_GNU_IFUNC which can be used to
+   indicate that if the symbol is the target of a relocation, its value should
+   not be use.  Instead the function should be invoked and its result used as
+   the value.
+  
  * Add support for Lattice Mico32 (lm32) architecture.
  
  Changes in 2.19:
Index: gas/config/obj-elf.c
===================================================================
RCS file: /cvs/src/src/gas/config/obj-elf.c,v
retrieving revision 1.112
diff -c -3 -p -r1.112 obj-elf.c
*** gas/config/obj-elf.c	15 Jan 2009 12:42:52 -0000	1.112
--- gas/config/obj-elf.c	30 Apr 2009 14:00:30 -0000
*************** obj_elf_type (int ignore ATTRIBUTE_UNUSE
*** 1664,1669 ****
--- 1664,1683 ----
  	    }
  	}
      }
+   else if (strcmp (typename, "gnu_indirect_function") == 0
+       || strcmp (typename, "10") == 0
+       || strcmp (typename, "STT_GNU_IFUNC") == 0)
+     {
+       const struct elf_backend_data *bed;
+ 
+       bed = get_elf_backend_data (stdoutput);
+       if (!(bed->elf_osabi == ELFOSABI_LINUX
+ 	    /* GNU/Linux is still using the default value 0.  */
+ 	    || bed->elf_osabi == ELFOSABI_NONE))
+ 	as_bad (_("symbol type \"%s\" is supported only by GNU targets"),
+ 		typename);
+       type = BSF_FUNCTION | BSF_GNU_INDIRECT_FUNCTION;
+     }
  #ifdef md_elf_symbol_type
    else if ((type = md_elf_symbol_type (typename, sym, elfsym)) != -1)
      ;
Index: gas/config/tc-i386.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-i386.c,v
retrieving revision 1.372
diff -c -3 -p -r1.372 tc-i386.c
*** gas/config/tc-i386.c	20 Apr 2009 06:31:49 -0000	1.372
--- gas/config/tc-i386.c	30 Apr 2009 14:00:32 -0000
*************** tc_i386_fix_adjustable (fixS *fixP ATTRI
*** 2499,2504 ****
--- 2499,2508 ----
        || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
        || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
      return 0;
+ 
+   if (fixP->fx_addsy != NULL
+       && symbol_get_bfdsym (fixP->fx_addsy)->flags & BSF_GNU_INDIRECT_FUNCTION)
+     return 0;
  #endif
    return 1;
  }
Index: gas/doc/as.texinfo
===================================================================
RCS file: /cvs/src/src/gas/doc/as.texinfo,v
retrieving revision 1.200
diff -c -3 -p -r1.200 as.texinfo
*** gas/doc/as.texinfo	24 Apr 2009 18:31:28 -0000	1.200
--- gas/doc/as.texinfo	30 Apr 2009 14:00:35 -0000
*************** The types supported are:
*** 6327,6332 ****
--- 6327,6337 ----
  @itemx function
  Mark the symbol as being a function name.
  
+ @item STT_GNU_IFUNC
+ @itemx gnu_indirect_function
+ Mark the symbol as an indirect function when evaluated during reloc
+ processing.  (This is only supported on Linux targeted assemblers).
+ 
  @item STT_OBJECT
  @itemx object
  Mark the symbol as being a data object.
Index: gas/testsuite/gas/elf/elf.exp
===================================================================
RCS file: /cvs/src/src/gas/testsuite/gas/elf/elf.exp,v
retrieving revision 1.49
diff -c -3 -p -r1.49 elf.exp
*** gas/testsuite/gas/elf/elf.exp	5 Feb 2009 21:58:41 -0000	1.49
--- gas/testsuite/gas/elf/elf.exp	30 Apr 2009 14:00:35 -0000
*************** if { ([istarget "*-*-*elf*"]		
*** 114,121 ****
      run_elf_list_test "section5" "" "-al" "-SW" "| grep \" \\\\.test\\\[0-9\\\]\""
      run_dump_test "struct" 
      run_dump_test "symtab"
!     run_dump_test "symver" 
!     run_elf_list_test "type" "" "" "-s" "| grep \"1 \\\[FIONTC\\\]\""
      run_dump_test "section6" 
      run_dump_test "section7" 
  }
--- 114,133 ----
      run_elf_list_test "section5" "" "-al" "-SW" "| grep \" \\\\.test\\\[0-9\\\]\""
      run_dump_test "struct" 
      run_dump_test "symtab"
!     run_dump_test "symver"
! 
!     # The MSP port sets the ELF header's OSABI field to ELFOSABI_STANDALONE.
!     # The non-eabi ARM ports sets it to ELFOSABI_ARM.
!     # So for these targets we cannot include an IFUNC symbol type
!     # in the symbol type test.
!     if {   [istarget "msp*-*-*"]
! 	|| [istarget "arm*-*-*"]
! 	|| [istarget "xscale*-*-*"]} then {
! 	    run_elf_list_test "type-noifunc" "" "" "-s" "| grep \"1 \\\[FONTC\\\]\""
!     } else {
! 	    run_elf_list_test "type" "" "" "-s" "| grep \"1 \\\[FIONTC\\\]\""
!     }
! 
      run_dump_test "section6" 
      run_dump_test "section7" 
  }
Index: gas/testsuite/gas/elf/section2.e-armelf
===================================================================
RCS file: /cvs/src/src/gas/testsuite/gas/elf/section2.e-armelf,v
retrieving revision 1.1
diff -c -3 -p -r1.1 section2.e-armelf
*** gas/testsuite/gas/elf/section2.e-armelf	22 Nov 2006 17:45:56 -0000	1.1
--- gas/testsuite/gas/elf/section2.e-armelf	30 Apr 2009 14:00:35 -0000
***************
*** 1,5 ****
  
! Symbol table '.symtab' contains 6 entries:
     Num:    Value[ 	]* Size Type    Bind   Vis      Ndx Name
       0: 0+0     0 NOTYPE  LOCAL  DEFAULT  UND 
       1: 0+0     0 SECTION LOCAL  DEFAULT    1 
--- 1,5 ----
  
! Symbol table '.symtab' contains 7 entries:
     Num:    Value[ 	]* Size Type    Bind   Vis      Ndx Name
       0: 0+0     0 NOTYPE  LOCAL  DEFAULT  UND 
       1: 0+0     0 SECTION LOCAL  DEFAULT    1 
*************** Symbol table '.symtab' contains 6 entrie
*** 7,9 ****
--- 7,10 ----
       3: 0+0     0 SECTION LOCAL  DEFAULT    3 
       4: 0+0     0 SECTION LOCAL  DEFAULT    4 
       5: 0+0     0 NOTYPE  LOCAL  DEFAULT    4 \$d
+      6: 0+0     0 SECTION LOCAL  DEFAULT    5 
Index: gas/testsuite/gas/elf/type.e
===================================================================
RCS file: /cvs/src/src/gas/testsuite/gas/elf/type.e,v
retrieving revision 1.5
diff -c -3 -p -r1.5 type.e
*** gas/testsuite/gas/elf/type.e	23 Dec 2008 09:01:49 -0000	1.5
--- gas/testsuite/gas/elf/type.e	30 Apr 2009 14:00:35 -0000
***************
*** 1,5 ****
       .: 0+0     1 FUNC    LOCAL  DEFAULT    . function
       .: 0+0     1 OBJECT  LOCAL  DEFAULT    . object
       .: 0+1     1 TLS     LOCAL  DEFAULT    . tls_object
!      .: 0+2     1 NOTYPE  LOCAL  DEFAULT    . notype
      ..: 0+1     1 (COMMON|OBJECT)  GLOBAL DEFAULT  COM common
--- 1,6 ----
       .: 0+0     1 FUNC    LOCAL  DEFAULT    . function
+      .: 0+1     1 IFUNC   LOCAL  DEFAULT    . indirect_function
       .: 0+0     1 OBJECT  LOCAL  DEFAULT    . object
       .: 0+1     1 TLS     LOCAL  DEFAULT    . tls_object
!     ..: 0+2     1 NOTYPE  LOCAL  DEFAULT    . notype
      ..: 0+1     1 (COMMON|OBJECT)  GLOBAL DEFAULT  COM common
Index: gas/testsuite/gas/elf/type.s
===================================================================
RCS file: /cvs/src/src/gas/testsuite/gas/elf/type.s,v
retrieving revision 1.5
diff -c -3 -p -r1.5 type.s
*** gas/testsuite/gas/elf/type.s	23 Dec 2008 09:01:49 -0000	1.5
--- gas/testsuite/gas/elf/type.s	30 Apr 2009 14:00:35 -0000
***************
*** 3,8 ****
--- 3,12 ----
          .type   function,%function
  function:
  	.byte	0x0
+         .size   indirect_function,1
+         .type   indirect_function,%gnu_indirect_function
+ indirect_function:
+ 	.byte	0x0
          .data
          .type   object,%object
          .size   object,1
Index: include/elf/common.h
===================================================================
RCS file: /cvs/src/src/include/elf/common.h,v
retrieving revision 1.110
diff -c -3 -p -r1.110 common.h
*** include/elf/common.h	16 Apr 2009 15:39:45 -0000	1.110
--- include/elf/common.h	30 Apr 2009 14:00:40 -0000
***************
*** 569,574 ****
--- 569,575 ----
  #define STT_RELC	8		/* Complex relocation expression */
  #define STT_SRELC	9		/* Signed Complex relocation expression */
  #define STT_LOOS	10		/* OS-specific semantics */
+ #define STT_GNU_IFUNC	10		/* Symbol is an indirect code object */
  #define STT_HIOS	12		/* OS-specific semantics */
  #define STT_LOPROC	13		/* Processor-specific semantics */
  #define STT_HIPROC	15		/* Processor-specific semantics */
Index: ld/NEWS
===================================================================
RCS file: /cvs/src/src/ld/NEWS,v
retrieving revision 1.101
diff -c -3 -p -r1.101 NEWS
*** ld/NEWS	8 Apr 2009 16:04:50 -0000	1.101
--- ld/NEWS	30 Apr 2009 14:00:41 -0000
***************
*** 12,17 ****
--- 12,21 ----
    automatically in the presence of un-stripped debug information, as GDB
    needs to be able to find the debug info sections by their full names.
  
+ * For GNU/Linux systems the linker will now avoid processing any relocations
+   made against symbols of the STT_GNU_IFUNC type and instead emit them into
+   the resulting binary for processing by the loader.
+ 
  * --as-needed now links in a dynamic library if it satisfies undefined
    symbols in regular objects, or in other dynamic libraries.  In the
    latter case the library is not linked if it is found in a DT_NEEDED

*** /dev/null	2009-04-30 11:28:48.565250861 +0100
--- gas/testsuite/gas/elf/type-noifunc.s	2009-04-24 13:35:53.000000000 +0100
***************
*** 0 ****
--- 1,20 ----
+ 	.text
+         .size   function,1
+         .type   function,%function
+ function:
+ 	.byte	0x0
+         .data
+         .type   object,%object
+         .size   object,1
+ object:
+ 	.byte	0x0
+         .type   tls_object,%tls_object
+         .size   tls_object,1
+ tls_object:
+ 	.byte	0x0
+         .type   notype,%notype
+         .size   notype,1
+ notype:
+ 	.byte	0x0
+ 	.comm	common, 1
+ 	.type   common,STT_COMMON

*** /dev/null	2009-04-30 11:28:48.565250861 +0100
--- gas/testsuite/gas/elf/type-noifunc.e	2009-04-24 13:36:08.000000000 +0100
***************
*** 0 ****
--- 1,5 ----
+      .: 0+0     1 FUNC    LOCAL  DEFAULT    . function
+      .: 0+0     1 OBJECT  LOCAL  DEFAULT    . object
+      .: 0+1     1 TLS     LOCAL  DEFAULT    . tls_object
+     ..: 0+2     1 NOTYPE  LOCAL  DEFAULT    . notype
+     ..: 0+1     1 (COMMON|OBJECT)  GLOBAL DEFAULT  COM common

*** /dev/null	2009-04-30 11:28:48.565250861 +0100
--- ld/testsuite/ld-ifunc/ifunc.exp	2009-04-30 14:57:22.000000000 +0100
***************
*** 0 ****
--- 1,254 ----
+ # Expect script for linker support of IFUNC symbols and relocations.
+ #
+ #   Copyright 2009  Free Software Foundation, Inc.
+ #   Contributed by Red Hat.
+ #
+ # This file is part of the GNU Binutils.
+ #
+ # This program is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+ # the Free Software Foundation; either version 3 of the License, or
+ # (at your option) any later version.
+ #
+ # This program is distributed in the hope that it will be useful,
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ # GNU General Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License
+ # along with this program; if not, write to the Free Software
+ # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ # MA 02110-1301, USA.
+ #
+ # Written by Nick Clifton <nickc@redhat.com>
+ 
+ 
+ # IFUNC support has only been implemented for the x86_64 and ix86 so far.
+ if {! (  [istarget "x86_64-*-elf*"]
+       || [istarget "x86_64-*-linux*"]
+       || [istarget "i?86-*-elf*"]
+       || [istarget "i?86-*-linux*"]) } {
+     verbose "IFUNC tests not run - target does not support IFUNC"
+     return
+ }
+ 
+ # We need a native system.  FIXME: Strictly speaking this
+ # is not true, we just need to know how to create a fully
+ # linked executable, including the C and Z libraries, using
+ # the linker that is under test.
+ if ![isnative] {
+     verbose "IFUNC tests not run - not a native toolchain"
+     return
+ }
+ 
+ # We need a working compiler.  (Strictly speaking this is
+ # not true, we could use target specific assembler files).
+ if { [which $CC] == 0 } {
+     verbose "IFUNC tests not run - no compiler available"
+     return
+ }
+ 
+ # A procedure to check the OS/ABI field in the ELF header of a binary file.
+ proc check_osabi { binary_file expected_osabi } {
+     global READELF
+     global READELFFLAGS
+ 
+     catch "exec $READELF $READELFFLAGS --file-header $binary_file > readelf.out" got
+ 
+     if ![string match "" $got] then {
+ 	verbose "proc check_osabi: Readelf produced unexpected out processing $binary_file: $got"
+ 	return 0
+     }
+ 
+     if { ![regexp "\n\[ \]*OS/ABI:\[ \]*(.+)\n\[ \]*ABI" \
+ 	   [file_contents readelf.out] nil osabi] } {
+ 	verbose "proc check_osabi: Readelf failed to extract an ELF header from $binary_file"
+ 	return 0
+     }
+ 
+     if { $osabi == $expected_osabi } {
+ 	return 1
+     }
+ 
+     verbose "Expected OSABI: $expected_osabi, Obtained osabi: $osabi"
+     
+     return 0
+ }
+ 
+ # A procedure to confirm that a file contains the IFUNC symbol.
+ # Returns -1 upon error, 0 if the symbol was not found and 1 if it was found.
+ proc contains_ifunc_symbol { binary_file } {
+     global READELF
+     global READELFFLAGS
+ 
+     catch "exec $READELF $READELFFLAGS --symbols $binary_file > readelf.out" got
+ 
+     if ![string match "" $got] then {
+ 	verbose "proc contains_ifunc_symbol: Readelf produced unexpected out processing $binary_file: $got"
+ 	return -1
+     }
+ 
+     # Look for a line like this:
+     #    58: 0000000000400600    30 IFUNC   GLOBAL DEFAULT   12 library_func2
+ 
+     if { ![regexp ".*\[ \]*IFUNC\[ \]+GLOBAL\[ \]+DEFAULT\[ \]+\[UND0-9\]+\[ \]+library_func2\n" [file_contents readelf.out]] } {
+ 	return 0
+     }
+ 
+     return 1
+ }
+ 
+ # A procedure to confirm that a file contains a relocation that references an IFUNC symbol.
+ # Returns -1 upon error, 0 if the reloc was not found and 1 if it was found.
+ proc contains_ifunc_reloc { binary_file } {
+     global READELF
+     global READELFFLAGS
+ 
+     catch "exec $READELF $READELFFLAGS --relocs $binary_file > readelf.out" got
+ 
+     if ![string match "" $got] then {
+ 	verbose "proc contains_ifunc_reloc: Readelf produced unexpected out processing $binary_file: $got"
+ 	return -1
+     }
+ 
+     if [string match "" [file_contents readelf.out]] then {
+ 	verbose "No relocs found in $binary_file"
+ 	return 0
+     }
+ 
+     if { ![regexp "\\(\\)" [file_contents readelf.out]] } {
+ 	return 0
+     }
+ 
+     return 1
+ }
+ 
+ set fails 0
+ 
+ # Create the object files, libraries and executables.
+ if ![ld_compile "$CC -c -shared -fPIC" "$srcdir/$subdir/prog.c" "tmpdir/shared_prog.o"] {
+     fail "Could not create a shared object file"
+     set fails [expr $fails + 1]
+ }
+ if ![ld_compile "$CC -c -static" "$srcdir/$subdir/prog.c" "tmpdir/static_prog.o"] {
+     fail "Could not create a static object file"
+     set fails [expr $fails + 1]
+ }
+ if ![ld_compile "$CC -c -shared -fPIC -DWITH_IFUNC" "$srcdir/$subdir/lib.c" "tmpdir/shared_ifunc.o"] {
+     fail "Could not create an object file containing an IFUNC symbol"
+     set fails [expr $fails + 1]
+ }
+ if ![ld_compile "$CC -c -static -DWITH_IFUNC" "$srcdir/$subdir/lib.c" "tmpdir/static_ifunc.o"] {
+     fail "Could not create a static object file containing an IFUNC symbol"
+     set fails [expr $fails + 1]
+ }
+ if ![ld_compile "$CC -c -static -DWITHOUT_IFUNC" "$srcdir/$subdir/lib.c" "tmpdir/static_noifunc.o"] {
+     fail "Could not create an ordinary static object file"
+     set fails [expr $fails + 1]
+ }
+ 
+ if { $fails != 0 } {
+     return
+ }
+ 
+ if ![ld_simple_link $ld "tmpdir/libshared_ifunc.so" "-shared tmpdir/shared_ifunc.o"] {
+     fail "Could not create a shared library containing an IFUNC symbol"
+     set fails [expr $fails + 1]
+ }
+ if ![ar_simple_create $ar "" "tmpdir/libifunc.a" "tmpdir/static_ifunc.o"] {
+     fail "Could not create a static library containing an IFUNC symbol"
+     set fails [expr $fails + 1]
+ }
+ 
+ if { $fails != 0 } {
+     return
+ }
+ 
+ if ![default_ld_link $ld "tmpdir/dynamic_prog" "-Ltmpdir tmpdir/shared_prog.o -Bdynamic -lshared_ifunc -rpath ./tmpdir"] {
+     fail "Could not link a dynamic executable"
+     set fails [expr $fails + 1]
+ }
+ if ![default_ld_link $ld "tmpdir/static_prog" "-Ltmpdir tmpdir/static_prog.o -lifunc"] {
+     fail "Could not link a static executable"
+     set fails [expr $fails + 1]
+ }
+ if ![default_ld_link $ld "tmpdir/static_nonifunc_prog" "-Ltmpdir tmpdir/static_prog.o tmpdir/static_noifunc.o"] {
+     fail "Could not link a non-ifunc using static executable"
+     set fails [expr $fails + 1]
+ }
+ 
+ if { $fails == 0 } {
+   pass "Building ifunc binaries"
+   set fails 0
+ } else {
+     return
+ }
+ 
+ # Check the executables.
+ #
+ # The linked ifunc using executables should have an OSABI field of LINUX
+ # The linked non-ifunc using executable should have an OSABI field of NONE (aka System V).
+ 
+ if {! [check_osabi tmpdir/static_prog {UNIX - Linux}]} {
+     fail "Static ifunc-using executable does not have an OS/ABI field of LINUX"
+     set fails [expr $fails + 1]
+ }
+ if {! [check_osabi tmpdir/dynamic_prog {UNIX - Linux}]} {
+     fail "Dynamic ifunc-using executable does not have an OS/ABI field of LINUX"
+     set fails [expr $fails + 1]
+ }
+ if {! [check_osabi tmpdir/static_nonifunc_prog {UNIX - System V}]} {
+     fail "Static non-ifunc-using executable does not have an OS/ABI field of System V"
+     set fails [expr $fails + 1]
+ }
+ 
+ # The linked ifunc using executables should contain an IFUNC symbol,
+ # The non-ifunc using executable should not.
+ 
+ if {[contains_ifunc_symbol tmpdir/static_prog] != 1} {
+     fail "Static ifunc-using executable does not contain an IFUNC symbol"
+     set fails [expr $fails + 1]
+ }
+ if {[contains_ifunc_symbol tmpdir/dynamic_prog] != 1} {
+     fail "Dynamic ifunc-using executable does not contain an IFUNC symbol"
+     set fails [expr $fails + 1]
+ }
+ if {[contains_ifunc_symbol tmpdir/static_nonifunc_prog] != 0} {
+     fail "Static non-ifunc-using executable contains an IFUNC symbol"
+     set fails [expr $fails + 1]
+ }
+ 
+ # The linked ifunc using executablea should contain a dynamic reloc referencing the IFUNC symbol.
+ # (Even the static executable which should have a dynamic section created for it).
+ # The non-ifunc using executable should not.
+ 
+ if {[contains_ifunc_reloc tmpdir/static_prog] != 1} {
+     fail "Static ifunc-using executable does not contain a reloc against an IFUNC symbol"
+     set fails [expr $fails + 1]
+ }
+ if {[contains_ifunc_reloc tmpdir/dynamic_prog] != 1} {
+     fail "Dynamic ifunc-using executable does not contain a reloc against an IFUNC symbol"
+     set fails [expr $fails + 1]
+ }
+ if {[contains_ifunc_reloc tmpdir/static_nonifunc_prog] == 1} {
+     fail "Static non-ifunc-using executable contains a reloc against an IFUNC symbol!"
+     set fails [expr $fails + 1]
+ }
+ 
+ if { $fails == 0 } {
+   pass "Checking ifunc binaries"
+ }
+ 
+ # Clean up, unless we are being verbose, in which case we leave the files available.
+ if { $verbose < 1 } {
+     remote_file host delete "tmpdir/shared_prog.o"
+     remote_file host delete "tmpdir/static_prog.o"
+     remote_file host delete "tmpdir/shared_ifunc.o"
+     remote_file host delete "tmpdir/static_ifunc.o"
+     remote_file host delete "tmpdir/static_noifunc.o"
+     remote_file host delete "tmpdir/libshared_ifunc.so"
+     remote_file host delete "tmpdir/libifunc.a"
+     remote_file host delete "tmpdir/dynamic_prog"
+     remote_file host delete "tmpdir/static_prog"
+     remote_file host delete "tmpdir/static_nonifunc_prog"
+ }

*** /dev/null	2009-04-30 11:28:48.565250861 +0100
--- ld/testsuite/ld-ifunc/prog.c	2009-04-30 14:48:14.000000000 +0100
***************
*** 0 ****
--- 1,46 ----
+ extern int printf (const char *, ...);
+ 
+ extern int library_func1 (void);
+ extern int library_func2 (void);
+ extern int global;
+ 
+ int
+ main (void)
+ {
+   int res = -1;
+ 
+   res += library_func1 ();
+   res += library_func2 ();
+ 
+   switch (res)
+     {
+     case 0:
+       if (global)
+ 	printf ("ifunc working correctly\n");
+       else
+ 	{
+ 	  printf ("wrong value returned by library_func2\n");
+ 	  res = -1;
+ 	}
+       break;
+ 
+     case 1:
+       if (global)
+ 	printf ("wrong value returned by library_func2\n");
+       else
+ 	{
+ 	  printf ("ifunc working correctly\n");
+ 	  res = 0;
+ 	}
+       break;
+ 
+     case 4:
+       printf ("non-ifunc testcase\n");
+       break;
+ 
+     default:
+       printf ("ifunc function not evaluated at run-time, res = %x\n", res);
+       break;
+     }
+   return res;
+ }

*** /dev/null	2009-04-30 11:28:48.565250861 +0100
--- ld/testsuite/ld-ifunc/lib.c	2009-04-30 14:50:23.000000000 +0100
***************
*** 0 ****
--- 1,26 ----
+ int
+ library_func1 (void)
+ {
+   return 2;
+ }
+ 
+ int global = 1;
+ 
+ #ifdef WITH_IFUNC
+ 
+ static int minus_one (void) { return -1; }
+ static int zero (void) { return 0; }
+ 
+ void * library_func2_ifunc (void) __asm__ ("library_func2");
+ void * library_func2_ifunc (void) { return global ? minus_one : zero ; }
+ __asm__(".type library_func2, %gnu_indirect_function");
+ 
+ #else /* WITHOUT_IFUNC */
+ 
+ int
+ library_func2 (void)
+ {
+   return 3;
+ }
+ 
+ #endif

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