From: Nick Clifton Date: Thu, 30 Apr 2009 15:47:13 +0000 (+0000) Subject: include/elf X-Git-Tag: sid-snapshot-20090501~9 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d8045f234d8865a7a7bfce71e81fcbeaf4098a7e;p=external%2Fbinutils.git include/elf 2009-04-30 Nick Clifton * common.h (STT_GNU_IFUNC): Define. elfcpp 2009-04-30 Nick Clifton * (enum STT): Add STT_GNU_IFUNC. gas 2009-04-30 Nick Clifton * 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 * 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 * 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 * 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 * NEWS: Mention support for IFUNC symbols. ld/testsuite 2009-04-30 Nick Clifton * 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. --- diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 25c21ef..d13bda0 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,50 @@ +2009-04-30 Nick Clifton + + * 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 * elf32-arm.c (elf32_arm_check_relocs): Give errors for absolute diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index fc6a4a1..18eae4e 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -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 diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 865388c..5761923 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -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; diff --git a/bfd/elf.c b/bfd/elf.c index 720c8a1..c7151be 100644 --- 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); } diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index 3c56685..c0276c0 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -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. */ diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index 58be143..b82bcd1 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -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 diff --git a/bfd/elfcode.h b/bfd/elfcode.h index b42229f..024ead4 100644 --- a/bfd/elfcode.h +++ b/bfd/elfcode.h @@ -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) diff --git a/bfd/elflink.c b/bfd/elflink.c index 3f70d91..54ad2af 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -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; +} diff --git a/bfd/syms.c b/bfd/syms.c index cdbf905..6abb929 100644 --- a/bfd/syms.c +++ b/bfd/syms.c @@ -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 diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 800e76a..268d75e 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,9 @@ +2009-04-30 Nick Clifton + + * 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 * NEWS: Tweak verilog support description. diff --git a/binutils/readelf.c b/binutils/readelf.c index a7e7b05..30bcf66 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -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), _(": %d"), type); } else diff --git a/elfcpp/ChangeLog b/elfcpp/ChangeLog index 9f048dd..893e6b8 100644 --- a/elfcpp/ChangeLog +++ b/elfcpp/ChangeLog @@ -1,3 +1,7 @@ +2009-04-30 Nick Clifton + + * (enum STT): Add STT_GNU_IFUNC. + 2009-03-20 Mikolaj Zalewski * elfcpp.h (SHT_GNU_INCREMENTAL_INPUTS): Define. diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h index a3786de..7abc9e3 100644 --- a/elfcpp/elfcpp.h +++ b/elfcpp/elfcpp.h @@ -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, diff --git a/gas/ChangeLog b/gas/ChangeLog index ca81227..ade42dd 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,13 @@ +2009-04-30 Nick Clifton + + * 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 * NEWS: Add item about discriminator support. diff --git a/gas/NEWS b/gas/NEWS index e94438f..0650b30 100644 --- 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: diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c index 4c68779..3d80ecc 100644 --- a/gas/config/obj-elf.c +++ b/gas/config/obj-elf.c @@ -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) ; diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c index 767d1fc..dd2adcc 100644 --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@ -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; } diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index 682b368..0968a29 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -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. diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp index 1085b04..abf2030 100644 --- a/gas/testsuite/gas/elf/elf.exp +++ b/gas/testsuite/gas/elf/elf.exp @@ -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" } diff --git a/gas/testsuite/gas/elf/section2.e-armelf b/gas/testsuite/gas/elf/section2.e-armelf index 8d2e4ff..44ecffc 100644 --- a/gas/testsuite/gas/elf/section2.e-armelf +++ b/gas/testsuite/gas/elf/section2.e-armelf @@ -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 index 0000000..81ee39b --- /dev/null +++ b/gas/testsuite/gas/elf/type-noifunc.e @@ -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 index 0000000..11f75bf --- /dev/null +++ b/gas/testsuite/gas/elf/type-noifunc.s @@ -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 diff --git a/gas/testsuite/gas/elf/type.e b/gas/testsuite/gas/elf/type.e index 50a49ab..95d846a 100644 --- a/gas/testsuite/gas/elf/type.e +++ b/gas/testsuite/gas/elf/type.e @@ -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 diff --git a/gas/testsuite/gas/elf/type.s b/gas/testsuite/gas/elf/type.s index 11f75bf..8620cc2 100644 --- a/gas/testsuite/gas/elf/type.s +++ b/gas/testsuite/gas/elf/type.s @@ -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 diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index eeaa880..6afa1cf 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,7 @@ +2009-04-30 Nick Clifton + + * common.h (STT_GNU_IFUNC): Define. + 2009-04-24 Cary Coutant * dwarf2.h (DW_LNE_set_discriminator): New enum value. diff --git a/include/elf/common.h b/include/elf/common.h index a343daa..a5b451d 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -569,6 +569,7 @@ #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 */ diff --git a/ld/ChangeLog b/ld/ChangeLog index 793d154..26c446f 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,7 @@ +2009-04-30 Nick Clifton + + * NEWS: Mention support for IFUNC symbols. + 2009-04-29 Anthony Green * emulparams/elf32moxie.sh (STACK_ADDR): Move default stack diff --git a/ld/NEWS b/ld/NEWS index 99d5d85..a0d7fb2 100644 --- a/ld/NEWS +++ b/ld/NEWS @@ -15,6 +15,10 @@ 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 diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index 4542a24..91bb3ed 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2009-04-30 Nick Clifton + + * 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 * ld-arm/movw-shared-1.d, ld-arm/movw-shared-1.s, diff --git a/ld/testsuite/ld-checks/checks.exp b/ld/testsuite/ld-checks/checks.exp index 39e9ac7..1e4af28 100644 --- a/ld/testsuite/ld-checks/checks.exp +++ b/ld/testsuite/ld-checks/checks.exp @@ -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 index 0000000..2ba5da1 --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc.exp @@ -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 + + +# 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 index 0000000..5bfd2cd --- /dev/null +++ b/ld/testsuite/ld-ifunc/lib.c @@ -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 index 0000000..da786a5 --- /dev/null +++ b/ld/testsuite/ld-ifunc/prog.c @@ -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; +}