OSDN Git Service

include/elf
authornickc <nickc>
Thu, 30 Apr 2009 15:47:02 +0000 (15:47 +0000)
committernickc <nickc>
Thu, 30 Apr 2009 15:47:02 +0000 (15:47 +0000)
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.

33 files changed:
bfd/ChangeLog
bfd/bfd-in2.h
bfd/elf-bfd.h
bfd/elf.c
bfd/elf32-i386.c
bfd/elf64-x86-64.c
bfd/elfcode.h
bfd/elflink.c
bfd/syms.c
binutils/ChangeLog
binutils/readelf.c
elfcpp/ChangeLog
elfcpp/elfcpp.h
gas/ChangeLog
gas/NEWS
gas/config/obj-elf.c
gas/config/tc-i386.c
gas/doc/as.texinfo
gas/testsuite/gas/elf/elf.exp
gas/testsuite/gas/elf/section2.e-armelf
gas/testsuite/gas/elf/type-noifunc.e [new file with mode: 0644]
gas/testsuite/gas/elf/type-noifunc.s [new file with mode: 0644]
gas/testsuite/gas/elf/type.e
gas/testsuite/gas/elf/type.s
include/elf/ChangeLog
include/elf/common.h
ld/ChangeLog
ld/NEWS
ld/testsuite/ChangeLog
ld/testsuite/ld-checks/checks.exp
ld/testsuite/ld-ifunc/ifunc.exp [new file with mode: 0644]
ld/testsuite/ld-ifunc/lib.c [new file with mode: 0644]
ld/testsuite/ld-ifunc/prog.c [new file with mode: 0644]

index 25c21ef..d13bda0 100644 (file)
@@ -1,3 +1,50 @@
+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.
+
 2009-04-30  Joseph Myers  <joseph@codesourcery.com>
 
        * elf32-arm.c (elf32_arm_check_relocs): Give errors for absolute
index fc6a4a1..18eae4e 100644 (file)
@@ -4608,6 +4608,12 @@ typedef struct bfd_symbol
   /* 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 865388c..5761923 100644 (file)
@@ -1297,6 +1297,9 @@ struct bfd_elf_section_data
   /* 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;
@@ -1559,6 +1562,11 @@ struct elf_obj_tdata
   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;
@@ -2139,6 +2147,9 @@ extern int _bfd_elf_obj_attrs_arg_type (bfd *, int, int);
 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 720c8a1..c7151be 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -6399,6 +6399,8 @@ Unable to find equivalent output section for symbol '%s' from section '%s'"),
 
       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)
@@ -8977,15 +8979,23 @@ _bfd_elf_set_osabi (bfd * abfd,
   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.  */
+   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);
+  return (type == STT_FUNC
+         || type == STT_GNU_IFUNC);
 }
index 3c56685..c0276c0 100644 (file)
@@ -1196,6 +1196,25 @@ elf_i386_tls_transition (struct bfd_link_info *info, bfd *abfd,
   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.  */
@@ -1473,6 +1492,12 @@ elf_i386_check_relocs (bfd *abfd,
 
                  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
@@ -1815,6 +1840,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   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;
@@ -2036,6 +2062,16 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
            }
        }
     }
+  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
@@ -2074,7 +2110,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     {
       asection *sreloc;
 
-      sreloc = elf_section_data (p->sec)->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);
@@ -2877,6 +2916,12 @@ elf_i386_relocate_section (bfd *output_bfd,
                   || 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
@@ -2925,7 +2970,16 @@ elf_i386_relocate_section (bfd *output_bfd,
                  outrel.r_info = ELF32_R_INFO (0, R_386_RELATIVE);
                }
 
-             sreloc = elf_section_data (input_section)->sreloc;
+             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);
 
@@ -4045,6 +4099,24 @@ elf_i386_hash_symbol (struct elf_link_hash_entry *h)
   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
@@ -4089,6 +4161,9 @@ elf_i386_hash_symbol (struct elf_link_hash_entry *h)
   ((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"
 
@@ -4106,15 +4181,10 @@ elf_i386_hash_symbol (struct elf_link_hash_entry *h)
    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_i386_fbsd_post_process_headers (bfd *abfd, struct bfd_link_info *info)
 {
-  Elf_Internal_Ehdr *i_ehdrp;
-
-  i_ehdrp = elf_elfheader (abfd);
+  _bfd_elf_set_osabi (abfd, info);
 
-  /* 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);
@@ -4122,10 +4192,12 @@ elf_i386_post_process_headers (bfd *abfd,
 }
 
 #undef elf_backend_post_process_headers
-#define        elf_backend_post_process_headers        elf_i386_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 58be143..b82bcd1 100644 (file)
@@ -161,6 +161,12 @@ static reloc_howto_type x86_64_elf_howto_table[] =
         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
 {
@@ -977,6 +983,25 @@ elf64_x86_64_tls_transition (struct bfd_link_info *info, bfd *abfd,
   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.  */
@@ -1003,7 +1028,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
   sym_hashes = elf_sym_hashes (abfd);
 
   sreloc = NULL;
-
+  
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -1259,13 +1284,9 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
             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))
+              && (! IS_X86_64_PCREL_TYPE (r_type)
                   || (h != NULL
                       && (! SYMBOLIC_BIND (info, h)
                           || h->root.type == bfd_link_hash_defweak
@@ -1293,6 +1314,12 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
                  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
@@ -1324,6 +1351,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
              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)
@@ -1336,10 +1364,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
                }
 
              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)
+             if (IS_X86_64_PCREL_TYPE (r_type))
                p->pc_count += 1;
            }
          break;
@@ -1650,6 +1675,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
   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;
@@ -1728,7 +1754,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
       && !info->shared
       && h->dynindx == -1
       && elf64_x86_64_hash_entry (h)->tls_type == GOT_TLS_IE)
-    h->got.offset = (bfd_vma) -1;
+    {
+      h->got.offset = (bfd_vma) -1;
+    }
   else if (h->got.refcount > 0)
     {
       asection *s;
@@ -1827,13 +1855,21 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
          /* 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;
-           }
+                  && ! 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
@@ -1850,11 +1886,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
          /* 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;
-           }
+             && ! 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.  */
@@ -1872,7 +1906,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
     {
       asection * sreloc;
 
-      sreloc = elf_section_data (p->sec)->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);
 
@@ -2674,11 +2711,14 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
               && (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)))
+              && (! 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
@@ -2718,13 +2758,10 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                 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))
+                      && (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;
@@ -2773,8 +2810,17 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
                    }
                }
 
-             sreloc = elf_section_data (input_section)->sreloc;
-               
+             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;
@@ -3660,11 +3706,12 @@ elf64_x86_64_section_from_shdr (bfd *abfd,
 
 static bfd_boolean
 elf64_x86_64_add_symbol_hook (bfd *abfd,
-                             struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                             struct bfd_link_info *info,
                              Elf_Internal_Sym *sym,
                              const char **namep ATTRIBUTE_UNUSED,
                              flagword *flagsp ATTRIBUTE_UNUSED,
-                             asection **secp, bfd_vma *valp)
+                             asection **secp,
+                             bfd_vma *valp)
 {
   asection *lcomm;
 
@@ -3687,6 +3734,10 @@ elf64_x86_64_add_symbol_hook (bfd *abfd,
       *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;
 }
 
@@ -3914,6 +3965,9 @@ static const struct bfd_elf_special_section
 #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.  */
@@ -3926,9 +3980,6 @@ static const struct bfd_elf_special_section
 #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
 
index b42229f..024ead4 100644 (file)
@@ -1311,6 +1311,9 @@ elf_slurp_symbol_table (bfd *abfd, asymbol **symptrs, bfd_boolean dynamic)
            case STT_SRELC:
              sym->symbol.flags |= BSF_SRELC;
              break;
+           case STT_GNU_IFUNC:
+             sym->symbol.flags |= BSF_GNU_INDIRECT_FUNCTION;
+             break;
            }
 
          if (dynamic)
index 3f70d91..54ad2af 100644 (file)
@@ -2749,6 +2749,13 @@ _bfd_elf_adjust_dynamic_symbol (struct elf_link_hash_entry *h, void *data)
   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;
@@ -12533,3 +12540,70 @@ _bfd_elf_make_dynamic_reloc_section (asection *         sec,
 
   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 cdbf905..6abb929 100644 (file)
@@ -297,6 +297,12 @@ CODE_FRAGMENT
 .  {* 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
@@ -483,7 +489,7 @@ bfd_print_symbol_vandf (bfd *abfd, void *arg, asymbol *symbol)
           (type & BSF_WEAK) ? 'w' : ' ',
           (type & BSF_CONSTRUCTOR) ? 'C' : ' ',
           (type & BSF_WARNING) ? 'W' : ' ',
-          (type & BSF_INDIRECT) ? 'I' : ' ',
+          (type & BSF_INDIRECT) ? 'I' : (type & BSF_GNU_INDIRECT_FUNCTION) ? 'i' : ' ',
           (type & BSF_DEBUGGING) ? 'd' : (type & BSF_DYNAMIC) ? 'D' : ' ',
           ((type & BSF_FUNCTION)
            ? 'F'
@@ -669,6 +675,8 @@ bfd_decode_symclass (asymbol *symbol)
     }
   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 800e76a..268d75e 100644 (file)
@@ -1,3 +1,9 @@
+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.
+
 2009-04-29  Anthony Green  <green@moxielogic.com>
 
        * NEWS: Tweak verilog support description.
index a7e7b05..30bcf66 100644 (file)
@@ -1238,9 +1238,38 @@ dump_relocations (FILE * file,
 
              printf (" ");
 
-             print_vma (psym->st_value, LONG_HEX);
+             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;
 
-             printf (is_32bit_elf ? "   " : " ");
+                 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)
                {
@@ -6913,6 +6942,12 @@ get_symbol_type (unsigned int type)
                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 9f048dd..893e6b8 100644 (file)
@@ -1,3 +1,7 @@
+2009-04-30  Nick Clifton  <nickc@redhat.com>
+
+       * (enum STT): Add STT_GNU_IFUNC.
+
 2009-03-20  Mikolaj Zalewski  <mikolajz@google.com>
 
        * elfcpp.h (SHT_GNU_INCREMENTAL_INPUTS): Define.
index a3786de..7abc9e3 100644 (file)
@@ -478,6 +478,7 @@ enum STT
   STT_COMMON = 5,
   STT_TLS = 6,
   STT_LOOS = 10,
+  STT_GNU_IFUNC = 10,
   STT_HIOS = 12,
   STT_LOPROC = 13,
   STT_HIPROC = 15,
index ca81227..ade42dd 100644 (file)
@@ -1,3 +1,13 @@
+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.
+
 2009-04-24  Cary Coutant  <ccoutant@google.com>
 
        * NEWS: Add item about discriminator support.
index e94438f..0650b30 100644 (file)
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -5,6 +5,11 @@
 
 * 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 4c68779..3d80ecc 100644 (file)
@@ -1664,6 +1664,20 @@ obj_elf_type (int ignore ATTRIBUTE_UNUSED)
            }
        }
     }
+  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 767d1fc..dd2adcc 100644 (file)
@@ -2499,6 +2499,10 @@ tc_i386_fix_adjustable (fixS *fixP ATTRIBUTE_UNUSED)
       || 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 682b368..0968a29 100644 (file)
@@ -6327,6 +6327,11 @@ The types supported are:
 @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 1085b04..abf2030 100644 (file)
@@ -114,8 +114,20 @@ if { ([istarget "*-*-*elf*"]
     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 "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 8d2e4ff..44ecffc 100644 (file)
@@ -1,5 +1,5 @@
 
-Symbol table '.symtab' contains 6 entries:
+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 
@@ -7,3 +7,4 @@ Symbol table '.symtab' contains 6 entries:
      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 
diff --git a/gas/testsuite/gas/elf/type-noifunc.e b/gas/testsuite/gas/elf/type-noifunc.e
new file mode 100644 (file)
index 0000000..81ee39b
--- /dev/null
@@ -0,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
diff --git a/gas/testsuite/gas/elf/type-noifunc.s b/gas/testsuite/gas/elf/type-noifunc.s
new file mode 100644 (file)
index 0000000..11f75bf
--- /dev/null
@@ -0,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
index 50a49ab..95d846a 100644 (file)
@@ -1,5 +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+2     1 NOTYPE  LOCAL  DEFAULT    . notype
     ..: 0+1     1 (COMMON|OBJECT)  GLOBAL DEFAULT  COM common
index 11f75bf..8620cc2 100644 (file)
@@ -3,6 +3,10 @@
         .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 eeaa880..6afa1cf 100644 (file)
@@ -1,3 +1,7 @@
+2009-04-30  Nick Clifton  <nickc@redhat.com>
+
+       * common.h (STT_GNU_IFUNC): Define.
+
 2009-04-24  Cary Coutant  <ccoutant@google.com>
 
         * dwarf2.h (DW_LNE_set_discriminator): New enum value.
index a343daa..a5b451d 100644 (file)
 #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 793d154..26c446f 100644 (file)
@@ -1,3 +1,7 @@
+2009-04-30  Nick Clifton  <nickc@redhat.com>
+
+       * NEWS: Mention support for IFUNC symbols.
+
 2009-04-29  Anthony Green  <green@moxielogic.com>
 
        * emulparams/elf32moxie.sh (STACK_ADDR): Move default stack
diff --git a/ld/NEWS b/ld/NEWS
index 99d5d85..a0d7fb2 100644 (file)
--- a/ld/NEWS
+++ b/ld/NEWS
   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
index 4542a24..91bb3ed 100644 (file)
@@ -1,3 +1,10 @@
+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.
+
 2009-04-30  Joseph Myers  <joseph@codesourcery.com>
 
        * ld-arm/movw-shared-1.d, ld-arm/movw-shared-1.s,
index 39e9ac7..1e4af28 100644 (file)
@@ -1,5 +1,5 @@
 # Expect script for LD section checks tests
-#   Copyright 1999, 2001, 2003, 2007 Free Software Foundation, Inc.
+#   Copyright 1999, 2001, 2003, 2007, 2009 Free Software Foundation, Inc.
 #
 # This file is part of the GNU Binutils.
 #
@@ -26,16 +26,17 @@ proc section_check {} {
     global ld
     global srcdir
     global subdir
-    
+
     # The usage of .lcomm in asm.s is incompatible with ia64 and ppc coff.
     if { [istarget ia64-*-*]
-        || [istarget powerpc*-*-aix*] || [istarget powerpc-*-beos*]
+        || [istarget powerpc*-*-aix*]
+        || [istarget powerpc-*-beos*]
         || [istarget rs6000-*-*] } {
        return
     }
     set test "check sections 1"
-    
-    set ldflags "--check-sections"
+
+    set ldflags "--check-sections -e foo"
 
     if { ![ld_assemble $as $srcdir/$subdir/asm.s tmpdir/asm.o]} {
        unresolved $test
@@ -55,15 +56,15 @@ proc section_check {} {
     set ldflags "--check-sections -T $srcdir/$subdir/script -e foo"
 
     # Perform the equivalent of invoking ld_simple_link
-    # except that we need to massage the output futher.
-    
+    # except that we need to massage the output further.
+
     set exec_output [run_host_cmd "$ld" "-o tmpdir/asm.x $ldflags tmpdir/asm.o"]
     set exec_output [prune_warnings $exec_output]
 
     # Make sure that we got some output from the linker
     if [string match "" $exec_output] then {
        fail $test
-    } 
+    }
 
     # Now remove our expected error message
     regsub -all ".*: section .data .* overlaps section .text .*" $exec_output "" exec_output
@@ -78,5 +79,3 @@ proc section_check {} {
 }
 
 section_check
-
-
diff --git a/ld/testsuite/ld-ifunc/ifunc.exp b/ld/testsuite/ld-ifunc/ifunc.exp
new file mode 100644 (file)
index 0000000..2ba5da1
--- /dev/null
@@ -0,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"
+}
diff --git a/ld/testsuite/ld-ifunc/lib.c b/ld/testsuite/ld-ifunc/lib.c
new file mode 100644 (file)
index 0000000..5bfd2cd
--- /dev/null
@@ -0,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
diff --git a/ld/testsuite/ld-ifunc/prog.c b/ld/testsuite/ld-ifunc/prog.c
new file mode 100644 (file)
index 0000000..da786a5
--- /dev/null
@@ -0,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;
+}