diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index add80b3..5418e1d 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -1216,6 +1216,13 @@ struct elf_backend_data /* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */ bfd_boolean (*elf_hash_symbol) (struct elf_link_hash_entry *); + /* If non-NULL, called to register the location XLAT_LOC within + .gnu.xhash at which real final dynindx for H should be written. + If XLAT_LOC is zero, the symbol is not included in + .gnu.xhash and no dynindx should be written. */ + void (*record_hash_symbol) + (struct elf_link_hash_entry *h, bfd_vma xlat_loc); + /* Return TRUE if type is a function symbol type. */ bfd_boolean (*is_function_type) (unsigned int type); diff --git a/bfd/elf.c b/bfd/elf.c index 8df38ee..0526183 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -1309,6 +1309,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg) case DT_USED: name = "USED"; break; case DT_FILTER: name = "FILTER"; stringp = TRUE; break; case DT_GNU_HASH: name = "GNU_HASH"; break; + case DT_GNU_XHASH: name = "GNU_XHASH"; break; } fprintf (f, " %-20s ", name); @@ -1575,6 +1576,7 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex) case SHT_PREINIT_ARRAY: /* .preinit_array section. */ case SHT_GNU_LIBLIST: /* .gnu.liblist section. */ case SHT_GNU_HASH: /* .gnu.hash section. */ + case SHT_GNU_XHASH: /* .gnu.xhash section. */ return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); case SHT_DYNAMIC: /* Dynamic linking information. */ @@ -2102,6 +2104,7 @@ static const struct bfd_elf_special_section special_sections_g[] = { STRING_COMMA_LEN (".gnu.liblist"), 0, SHT_GNU_LIBLIST, SHF_ALLOC }, { STRING_COMMA_LEN (".gnu.conflict"), 0, SHT_RELA, SHF_ALLOC }, { STRING_COMMA_LEN (".gnu.hash"), 0, SHT_GNU_HASH, SHF_ALLOC }, + { STRING_COMMA_LEN (".gnu.xhash"), 0, SHT_GNU_XHASH, SHF_ALLOC }, { NULL, 0, 0, 0, 0 } }; @@ -2714,6 +2717,7 @@ elf_fake_sections (bfd *abfd, asection *asect, void *fsarg) break; case SHT_GNU_HASH: + case SHT_GNU_XHASH: this_hdr->sh_entsize = bed->s->arch_size == 64 ? 0 : 4; break; } @@ -3220,6 +3224,7 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info) case SHT_HASH: case SHT_GNU_HASH: + case SHT_GNU_XHASH: case SHT_GNU_versym: /* sh_link is the section header index of the symbol table this hash table or version table is for. */ diff --git a/bfd/elflink.c b/bfd/elflink.c index 99b7ca1..1e3e2db 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -271,8 +271,12 @@ _bfd_elf_link_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) if (info->emit_gnu_hash) { - s = bfd_make_section_anyway_with_flags (abfd, ".gnu.hash", - flags | SEC_READONLY); + if (bed->record_hash_symbol == NULL) + s = bfd_make_section_anyway_with_flags (abfd, ".gnu.hash", + flags | SEC_READONLY); + else + s = bfd_make_section_anyway_with_flags (abfd, ".gnu.xhash", + flags | SEC_READONLY); if (s == NULL || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) return FALSE; @@ -5199,6 +5203,7 @@ struct collect_gnu_hash_codes unsigned long int *counts; bfd_vma *bitmask; bfd_byte *contents; + bfd_vma xlat; long int min_dynindx; unsigned long int bucketcount; unsigned long int symindx; @@ -5278,7 +5283,15 @@ elf_renumber_gnu_hash_syms (struct elf_link_hash_entry *h, void *data) if (! (*s->bed->elf_hash_symbol) (h)) { if (h->dynindx >= s->min_dynindx) - h->dynindx = s->local_indx++; + { + if (s->bed->record_hash_symbol != NULL) + { + (*s->bed->record_hash_symbol) (h, 0); + ++s->local_indx; + } + else + h->dynindx = s->local_indx++; + } return TRUE; } @@ -5295,7 +5308,14 @@ elf_renumber_gnu_hash_syms (struct elf_link_hash_entry *h, void *data) bfd_put_32 (s->output_bfd, val, s->contents + (s->indx[bucket] - s->symindx) * 4); --s->counts[bucket]; - h->dynindx = s->indx[bucket]++; + if (s->bed->record_hash_symbol != NULL) + { + bfd_vma xlat_loc = s->xlat + (s->indx[bucket]++ - s->symindx) * 4; + BFD_ASSERT (xlat_loc != 0); + (*s->bed->record_hash_symbol) (h, xlat_loc); + } + else + h->dynindx = s->indx[bucket]++; return TRUE; } @@ -5917,7 +5937,9 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd, if ((info->emit_hash && !_bfd_elf_add_dynamic_entry (info, DT_HASH, 0)) || (info->emit_gnu_hash - && !_bfd_elf_add_dynamic_entry (info, DT_GNU_HASH, 0)) + && (bed->record_hash_symbol == NULL + ? !_bfd_elf_add_dynamic_entry (info, DT_GNU_HASH, 0) + : !_bfd_elf_add_dynamic_entry (info, DT_GNU_XHASH, 0))) || !_bfd_elf_add_dynamic_entry (info, DT_STRTAB, 0) || !_bfd_elf_add_dynamic_entry (info, DT_SYMTAB, 0) || !_bfd_elf_add_dynamic_entry (info, DT_STRSZ, strsize) @@ -6535,19 +6557,30 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info) return FALSE; } - s = bfd_get_linker_section (dynobj, ".gnu.hash"); + if (bed->record_hash_symbol == NULL) + s = bfd_get_linker_section (dynobj, ".gnu.hash"); + else + s = bfd_get_linker_section (dynobj, ".gnu.xhash"); BFD_ASSERT (s != NULL); if (cinfo.nsyms == 0) { - /* Empty .gnu.hash section is special. */ + /* Empty .gnu.hash or .gnu.xhash section is special. */ BFD_ASSERT (cinfo.min_dynindx == -1); free (cinfo.hashcodes); s->size = 5 * 4 + bed->s->arch_size / 8; + if (bed->record_hash_symbol != NULL) + s->size += 4; contents = (unsigned char *) bfd_zalloc (output_bfd, s->size); if (contents == NULL) return FALSE; s->contents = contents; + if (bed->record_hash_symbol != NULL) + { + /* Chain and xlat symbol count is zero. */ + bfd_put_32 (output_bfd, 0, contents); + contents += 4; + } /* 1 empty bucket. */ bfd_put_32 (output_bfd, 1, contents); /* SYMIDX above the special symbol 0. */ @@ -6620,6 +6653,8 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info) s->size = (4 + bucketcount + cinfo.nsyms) * 4; s->size += cinfo.maskbits / 8; + if (bed->record_hash_symbol != NULL) + s->size += (1 + cinfo.nsyms) * 4; contents = (unsigned char *) bfd_zalloc (output_bfd, s->size); if (contents == NULL) { @@ -6629,6 +6664,11 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info) } s->contents = contents; + if (bed->record_hash_symbol != NULL) + { + bfd_put_32 (output_bfd, cinfo.nsyms, contents); + contents += 4; + } bfd_put_32 (output_bfd, bucketcount, contents); bfd_put_32 (output_bfd, cinfo.symindx, contents + 4); bfd_put_32 (output_bfd, maskwords, contents + 8); @@ -6645,12 +6685,15 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info) } cinfo.contents = contents; - + if (bed->record_hash_symbol != NULL) + cinfo.xlat = contents + cinfo.nsyms * 4 - s->contents; /* Renumber dynamic symbols, populate .gnu.hash section. */ elf_link_hash_traverse (elf_hash_table (info), elf_renumber_gnu_hash_syms, &cinfo); contents = s->contents + 16; + if (bed->record_hash_symbol != NULL) + contents += 4; for (i = 0; i < maskwords; ++i) { bfd_put (bed->s->arch_size, output_bfd, cinfo.bitmask[i], @@ -11301,6 +11344,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info) case DT_GNU_HASH: name = ".gnu.hash"; goto get_vma; + case DT_GNU_XHASH: + name = ".gnu.xhash"; + goto get_vma; case DT_STRTAB: name = ".dynstr"; goto get_vma; diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index d7498e1..d286a62 100644 --- a/bfd/elfxx-mips.c +++ b/bfd/elfxx-mips.c @@ -315,6 +315,11 @@ struct mips_elf_hash_sort_data /* The greatest dynamic symbol table index not corresponding to a symbol without a GOT entry. */ long max_non_got_dynindx; + /* If non-NULL, output BFD for .gnu.xhash finalization. */ + bfd *output_bfd; + /* If non-NULL, pointer to contents of .gnu.xhash for filling in + real final dynindx. */ + bfd_byte *gnuxhash; }; /* We make up to two PLT entries if needed, one for standard MIPS code @@ -372,6 +377,9 @@ struct mips_elf_link_hash_entry being called returns a floating point value. */ asection *call_fp_stub; + /* If non-zero, location in .gnu.xhash to write real final dynindx. */ + bfd_vma gnuxhash_loc; + /* The highest GGA_* value that satisfies all references to this symbol. */ unsigned int global_got_area : 2; @@ -1246,6 +1254,7 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry, ret->fn_stub = NULL; ret->call_stub = NULL; ret->call_fp_stub = NULL; + ret->gnuxhash_loc = 0; ret->global_got_area = GGA_NONE; ret->got_only_for_calls = TRUE; ret->readonly_reloc = FALSE; @@ -3725,6 +3734,18 @@ mips_elf_sort_hash_table (bfd *abfd, struct bfd_link_info *info) = hsd.min_got_dynindx = (elf_hash_table (info)->dynsymcount - g->reloc_only_gotno); hsd.max_non_got_dynindx = count_section_dynsyms (abfd, info) + 1; + hsd.output_bfd = abfd; + if (htab->root.dynobj != NULL + && htab->root.dynamic_sections_created + && info->emit_gnu_hash) + { + asection *s = bfd_get_linker_section (htab->root.dynobj, ".gnu.xhash"); + BFD_ASSERT (s != NULL); + hsd.gnuxhash = s->contents; + BFD_ASSERT (hsd.gnuxhash != NULL); + } + else + hsd.gnuxhash = NULL; mips_elf_link_hash_traverse (((struct mips_elf_link_hash_table *) elf_hash_table (info)), mips_elf_sort_hash_table_f, @@ -3777,6 +3798,12 @@ mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data) break; } + if (h->gnuxhash_loc != 0 && hsd->gnuxhash != NULL) + { + bfd_put_32 (hsd->output_bfd, h->root.dynindx, + hsd->gnuxhash + h->gnuxhash_loc); + } + return TRUE; } @@ -15285,3 +15312,10 @@ _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info) i_ehdrp->e_ident[EI_ABIVERSION] = 1; } } + +void +_bfd_mips_elf_record_hash_symbol (struct elf_link_hash_entry *h, bfd_vma xlat_loc) +{ + struct mips_elf_link_hash_entry *hmips = (struct mips_elf_link_hash_entry *) h; + hmips->gnuxhash_loc = xlat_loc; +} diff --git a/bfd/elfxx-mips.h b/bfd/elfxx-mips.h index f27dc15..f474f81 100644 --- a/bfd/elfxx-mips.h +++ b/bfd/elfxx-mips.h @@ -158,6 +158,8 @@ extern long _bfd_mips_elf_get_synthetic_symtab (bfd *, long, asymbol **, long, asymbol **, asymbol **); extern void _bfd_mips_post_process_headers (bfd *abfd, struct bfd_link_info *link_info); +extern void _bfd_mips_elf_record_hash_symbol + (struct elf_link_hash_entry *h, bfd_vma xlat_loc); extern const struct bfd_elf_special_section _bfd_mips_elf_special_sections []; @@ -186,3 +188,4 @@ literal_reloc_p (int r_type) #define elf_backend_merge_symbol_attribute _bfd_mips_elf_merge_symbol_attribute #define elf_backend_ignore_undef_symbol _bfd_mips_elf_ignore_undef_symbol #define elf_backend_post_process_headers _bfd_mips_post_process_headers +#define elf_backend_record_hash_symbol _bfd_mips_elf_record_hash_symbol diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h index d42ce26..fd20943 100644 --- a/bfd/elfxx-target.h +++ b/bfd/elfxx-target.h @@ -656,6 +656,10 @@ #define elf_backend_hash_symbol _bfd_elf_hash_symbol #endif +#ifndef elf_backend_record_hash_symbol +#define elf_backend_record_hash_symbol NULL +#endif + #ifndef elf_backend_is_function_type #define elf_backend_is_function_type _bfd_elf_is_function_type #endif @@ -758,6 +762,7 @@ static struct elf_backend_data elfNN_bed = elf_backend_common_section, elf_backend_merge_symbol, elf_backend_hash_symbol, + elf_backend_record_hash_symbol, elf_backend_is_function_type, elf_backend_maybe_function_sym, elf_backend_link_order_error_handler, diff --git a/binutils/readelf.c b/binutils/readelf.c index 61ea0ad..e87b111 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -181,6 +181,7 @@ static unsigned int dynamic_syminfo_nent; static char program_interpreter[PATH_MAX]; static bfd_vma dynamic_info[DT_ENCODING]; static bfd_vma dynamic_info_DT_GNU_HASH; +static bfd_vma dynamic_info_DT_GNU_XHASH; static bfd_vma version_info[16]; static Elf_Internal_Ehdr elf_header; static Elf_Internal_Shdr * section_headers; @@ -1805,6 +1806,7 @@ get_dynamic_type (unsigned long type) case DT_GNU_LIBLIST: return "GNU_LIBLIST"; case DT_GNU_LIBLISTSZ: return "GNU_LIBLISTSZ"; case DT_GNU_HASH: return "GNU_HASH"; + case DT_GNU_XHASH: return "GNU_XHASH"; default: if ((type >= DT_LOPROC) && (type <= DT_HIPROC)) @@ -3302,6 +3304,7 @@ get_section_type_name (unsigned int sh_type) case SHT_FINI_ARRAY: return "FINI_ARRAY"; case SHT_PREINIT_ARRAY: return "PREINIT_ARRAY"; case SHT_GNU_HASH: return "GNU_HASH"; + case SHT_GNU_XHASH: return "GNU_XHASH"; case SHT_GROUP: return "GROUP"; case SHT_SYMTAB_SHNDX: return "SYMTAB SECTION INDICIES"; case SHT_GNU_verdef: return "VERDEF"; @@ -8454,6 +8457,16 @@ process_dynamic_section (FILE * file) } break; + case DT_GNU_XHASH: + dynamic_info_DT_GNU_XHASH = entry->d_un.d_val; + dynamic_info_DT_GNU_HASH = dynamic_info_DT_GNU_XHASH + 4; + if (do_dynamic) + { + print_vma (entry->d_un.d_val, PREFIX_HEX); + putchar ('\n'); + } + break; + default: if ((entry->d_tag >= DT_VERSYM) && (entry->d_tag <= DT_VERNEEDNUM)) version_info[DT_VERSIONTAGIDX (entry->d_tag)] = @@ -9380,6 +9393,7 @@ process_symbol_table (FILE * file) bfd_vma ngnubuckets = 0; bfd_vma * gnubuckets = NULL; bfd_vma * gnuchains = NULL; + bfd_vma * gnuxlat = NULL; bfd_vma gnusymidx = 0; if (!do_syms && !do_dyn_syms && !do_histogram) @@ -9510,7 +9524,7 @@ process_symbol_table (FILE * file) if (fseek (file, (archive_file_offset + offset_from_vma (file, buckets_vma - + 4 * (ngnubuckets + maxchain), 4)), + + 4 * (ngnubuckets + maxchain), 4)), SEEK_SET)) { error (_("Unable to seek to start of dynamic information\n")); @@ -9532,6 +9546,30 @@ process_symbol_table (FILE * file) } while ((byte_get (nb, 4) & 1) == 0); + if (dynamic_info_DT_GNU_XHASH) + { + if (fseek (file, + (archive_file_offset + + offset_from_vma (file, dynamic_info_DT_GNU_XHASH, 4)), + SEEK_SET)) + { + error (_("Unable to seek to start of dynamic information\n")); + goto no_gnu_hash; + } + + if (fread (nb, 4, 1, file) != 1) + { + error (_("Failed to determine last chain length\n")); + goto no_gnu_hash; + } + + if (maxchain != byte_get (nb, 4)) + { + error (_("Failed to determine last chain length\n")); + goto no_gnu_hash; + } + } + if (fseek (file, (archive_file_offset + offset_from_vma (file, buckets_vma + 4 * ngnubuckets, 4)), @@ -9543,7 +9581,45 @@ process_symbol_table (FILE * file) gnuchains = get_dynamic_data (file, maxchain, 4); + if (gnuchains == NULL) + goto no_gnu_hash; + + if (dynamic_info_DT_GNU_XHASH) + { + if (fseek (file, + (archive_file_offset + + offset_from_vma (file, dynamic_info_DT_GNU_XHASH, 4)), + SEEK_SET)) + { + error (_("Unable to seek to start of dynamic information\n")); + goto no_gnu_hash; + } + + if (fread (nb, 4, 1, file) != 1) + { + error (_("Failed to read in number of buckets\n")); + goto no_gnu_hash; + } + if (fseek (file, + (archive_file_offset + + offset_from_vma (file, buckets_vma + + 4 * (ngnubuckets + + maxchain), 4)), + SEEK_SET)) + { + error (_("Unable to seek to start of dynamic information\n")); + goto no_gnu_hash; + } + + gnuxlat = get_dynamic_data (file, maxchain, 4); + } + no_gnu_hash: + if (dynamic_info_DT_GNU_XHASH && gnuxlat == NULL) + { + free (gnuchains); + gnuchains = NULL; + } if (gnuchains == NULL) { free (gnubuckets); @@ -9583,7 +9659,8 @@ process_symbol_table (FILE * file) if (dynamic_info_DT_GNU_HASH) { - printf (_("\nSymbol table of `.gnu.hash' for image:\n")); + printf (_("\nSymbol table of `%s' for image:\n"), + !dynamic_info_DT_GNU_XHASH ? ".gnu.hash" : ".gnu.xhash"); if (is_32bit_elf) printf (_(" Num Buc: Value Size Type Bind Vis Ndx Name\n")); else @@ -9597,7 +9674,10 @@ process_symbol_table (FILE * file) do { - print_dynamic_symbol (si, hn); + if (!dynamic_info_DT_GNU_XHASH) + print_dynamic_symbol (si, hn); + else + print_dynamic_symbol (gnuxlat[off], hn); si++; } while ((gnuchains[off++] & 1) == 0); @@ -9931,7 +10011,8 @@ process_symbol_table (FILE * file) return 0; } - printf (_("\nHistogram for `.gnu.hash' bucket list length (total of %lu buckets):\n"), + printf (_("\nHistogram for `%s' bucket list length (total of %lu buckets):\n"), + !dynamic_info_DT_GNU_XHASH ? ".gnu.hash" : ".gnu.xhash", (unsigned long) ngnubuckets); printf (_(" Length Number %% of total Coverage\n")); @@ -9977,6 +10058,8 @@ process_symbol_table (FILE * file) free (lengths); free (gnubuckets); free (gnuchains); + free (gnuxlat); + } return 1; @@ -13913,6 +13996,7 @@ process_object (char * file_name, FILE * file) for (i = ARRAY_SIZE (dynamic_info); i--;) dynamic_info[i] = 0; dynamic_info_DT_GNU_HASH = 0; + dynamic_info_DT_GNU_XHASH = 0; /* Process the file. */ if (show_name) diff --git a/include/elf/common.h b/include/elf/common.h index cd3bcdd..7cdbdeb 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -472,6 +472,7 @@ #define SHT_HIOS 0x6fffffff /* Last of OS specific semantics */ #define SHT_GNU_INCREMENTAL_INPUTS 0x6fff4700 /* incremental build data */ +#define SHT_GNU_XHASH 0x6ffffff4 /* GNU style symbol hash table with xlat */ #define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes */ #define SHT_GNU_HASH 0x6ffffff6 /* GNU style symbol hash table */ #define SHT_GNU_LIBLIST 0x6ffffff7 /* List of prelink dependencies */ @@ -775,6 +776,7 @@ #define DT_VALRNGHI 0x6ffffdff #define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_XHASH 0x6ffffef4 #define DT_GNU_HASH 0x6ffffef5 #define DT_TLSDESC_PLT 0x6ffffef6 #define DT_TLSDESC_GOT 0x6ffffef7 diff --git a/ld/emultempl/mipself.em b/ld/emultempl/mipself.em index 3c6ec9f..e7d4a16 100644 --- a/ld/emultempl/mipself.em +++ b/ld/emultempl/mipself.em @@ -35,21 +35,6 @@ static bfd *stub_bfd; static bfd_boolean insn32; -static void -mips_after_parse (void) -{ - /* .gnu.hash and the MIPS ABI require .dynsym to be sorted in different - ways. .gnu.hash needs symbols to be grouped by hash code whereas the - MIPS ABI requires a mapping between the GOT and the symbol table. */ - if (link_info.emit_gnu_hash) - { - einfo ("%X%P: .gnu.hash is incompatible with the MIPS ABI\n"); - link_info.emit_hash = TRUE; - link_info.emit_gnu_hash = FALSE; - } - after_parse_default (); -} - struct hook_stub_info { lang_statement_list_type add; @@ -281,6 +266,5 @@ PARSE_AND_LIST_ARGS_CASES=' break; ' -LDEMUL_AFTER_PARSE=mips_after_parse LDEMUL_BEFORE_ALLOCATION=mips_before_allocation LDEMUL_CREATE_OUTPUT_SECTION_STATEMENTS=mips_create_output_section_statements diff --git a/ld/scripttempl/elf.sc b/ld/scripttempl/elf.sc index e8126cb..2d89ab1 100644 --- a/ld/scripttempl/elf.sc +++ b/ld/scripttempl/elf.sc @@ -353,6 +353,7 @@ cat > ldscripts/dyntmp.$$ <