diff options
author | Philip Guenther <guenther@cvs.openbsd.org> | 2018-12-03 02:59:53 +0000 |
---|---|---|
committer | Philip Guenther <guenther@cvs.openbsd.org> | 2018-12-03 02:59:53 +0000 |
commit | 9da2f749f1f75e8caf6a463893e8911e905ea711 (patch) | |
tree | 8c42710c93ecdcc24791515a886bcc456b771480 /gnu | |
parent | 472098dae2ff30da0a6e87f6af192d6887cba776 (diff) |
Merge post-2.17 but pre-GPLv3 diffs that add support for DT_GNU_HASH
to ld and improve the readelf support:
https://sourceware.org/ml/binutils/2006-07/msg00129.html
https://sourceware.org/ml/binutils/2006-07/msg00181.html
Disable the ld support on mips64, as its ABI requires a symbol
ordering that conflicts with the requirements of DT_GNU_HASH.
Tested on macppc, alpha, and sparc64 to verify operation with all
three ELF word size combos
testing help miod@
ok kettenis@
Diffstat (limited to 'gnu')
-rw-r--r-- | gnu/usr.bin/binutils-2.17/bfd/elf-bfd.h | 7 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/bfd/elf.c | 16 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/bfd/elf32-i386.c | 13 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/bfd/elf64-x86-64.c | 15 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/bfd/elflink.c | 466 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/bfd/elfxx-target.h | 5 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/binutils/readelf.c | 177 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/include/bfdlink.h | 6 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/ld/emultempl/elf32.em | 21 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/ld/ld.texinfo | 10 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/ld/ldmain.c | 6 | ||||
-rw-r--r-- | gnu/usr.bin/binutils-2.17/ld/scripttempl/elf.sc | 1 |
12 files changed, 675 insertions, 68 deletions
diff --git a/gnu/usr.bin/binutils-2.17/bfd/elf-bfd.h b/gnu/usr.bin/binutils-2.17/bfd/elf-bfd.h index 190886b1982..383a0b4b015 100644 --- a/gnu/usr.bin/binutils-2.17/bfd/elf-bfd.h +++ b/gnu/usr.bin/binutils-2.17/bfd/elf-bfd.h @@ -1022,6 +1022,9 @@ struct elf_backend_data bfd_boolean *, bfd_boolean *, bfd *, asection **); + /* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */ + bfd_boolean (*elf_hash_symbol) (struct elf_link_hash_entry *); + /* Used to handle bad SHF_LINK_ORDER input. */ bfd_error_handler_type link_order_error_handler; @@ -1471,6 +1474,8 @@ extern bfd_vma _bfd_elf_section_offset extern unsigned long bfd_elf_hash (const char *); +extern unsigned long bfd_elf_gnu_hash + (const char *); extern bfd_reloc_status_type bfd_elf_generic_reloc (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); @@ -1641,6 +1646,8 @@ extern bfd_boolean _bfd_elf_merge_symbol struct elf_link_hash_entry **, bfd_boolean *, bfd_boolean *, bfd_boolean *, bfd_boolean *); +extern bfd_boolean _bfd_elf_hash_symbol (struct elf_link_hash_entry *); + extern bfd_boolean _bfd_elf_add_default_symbol (bfd *, struct bfd_link_info *, struct elf_link_hash_entry *, const char *, Elf_Internal_Sym *, asection **, bfd_vma *, diff --git a/gnu/usr.bin/binutils-2.17/bfd/elf.c b/gnu/usr.bin/binutils-2.17/bfd/elf.c index d354eeef54c..ce4e468f689 100644 --- a/gnu/usr.bin/binutils-2.17/bfd/elf.c +++ b/gnu/usr.bin/binutils-2.17/bfd/elf.c @@ -206,6 +206,21 @@ bfd_elf_hash (const char *namearg) return h & 0xffffffff; } +/* DT_GNU_HASH hash function. Do not change this function; you will + cause invalid hash tables to be generated. */ + +unsigned long +bfd_elf_gnu_hash (const char *namearg) +{ + const unsigned char *name = (const unsigned char *) namearg; + unsigned long h = 5381; + unsigned char ch; + + while ((ch = *name++) != '\0') + h = (h << 5) + h + ch; + return h & 0xffffffff; +} + bfd_boolean bfd_elf_mkobject (bfd *abfd) { @@ -1242,6 +1257,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg) case DT_AUXILIARY: name = "AUXILIARY"; stringp = TRUE; break; case DT_USED: name = "USED"; break; case DT_FILTER: name = "FILTER"; stringp = TRUE; break; + case DT_GNU_HASH: name = "GNU_HASH"; break; } fprintf (f, " %-11s ", name); diff --git a/gnu/usr.bin/binutils-2.17/bfd/elf32-i386.c b/gnu/usr.bin/binutils-2.17/bfd/elf32-i386.c index 1c709f75552..cecc7e8b7b2 100644 --- a/gnu/usr.bin/binutils-2.17/bfd/elf32-i386.c +++ b/gnu/usr.bin/binutils-2.17/bfd/elf32-i386.c @@ -3873,6 +3873,18 @@ elf_i386_plt_sym_val (bfd_vma i, const asection *plt, return plt->vma + (i + 1) * PLT_ENTRY_SIZE; } +/* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */ + +static bfd_boolean +elf_i386_hash_symbol (struct elf_link_hash_entry *h) +{ + if (h->plt.offset != (bfd_vma) -1 + && !h->def_regular + && !h->pointer_equality_needed) + return FALSE; + + return _bfd_elf_hash_symbol (h); +} #define TARGET_LITTLE_SYM bfd_elf32_i386_vec #define TARGET_LITTLE_NAME "elf32-i386" @@ -3913,6 +3925,7 @@ elf_i386_plt_sym_val (bfd_vma i, const asection *plt, #define elf_backend_size_dynamic_sections elf_i386_size_dynamic_sections #define elf_backend_always_size_sections elf_i386_always_size_sections #define elf_backend_plt_sym_val elf_i386_plt_sym_val +#define elf_backend_hash_symbol elf_i386_hash_symbol #include "elf32-target.h" diff --git a/gnu/usr.bin/binutils-2.17/bfd/elf64-x86-64.c b/gnu/usr.bin/binutils-2.17/bfd/elf64-x86-64.c index da4398159c8..9efe78f624e 100644 --- a/gnu/usr.bin/binutils-2.17/bfd/elf64-x86-64.c +++ b/gnu/usr.bin/binutils-2.17/bfd/elf64-x86-64.c @@ -3615,6 +3615,19 @@ elf64_x86_64_additional_program_headers (bfd *abfd) return count; } +/* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */ + +static bfd_boolean +elf64_x86_64_hash_symbol (struct elf_link_hash_entry *h) +{ + if (h->plt.offset != (bfd_vma) -1 + && !h->def_regular + && !h->pointer_equality_needed) + return FALSE; + + return _bfd_elf_hash_symbol (h); +} + static const struct bfd_elf_special_section elf64_x86_64_special_sections[]= { @@ -3686,5 +3699,7 @@ static const struct bfd_elf_special_section elf64_x86_64_special_sections #define elf_backend_additional_program_headers \ elf64_x86_64_additional_program_headers +#define elf_backend_hash_symbol \ + elf64_x86_64_hash_symbol #include "elf64-target.h" diff --git a/gnu/usr.bin/binutils-2.17/bfd/elflink.c b/gnu/usr.bin/binutils-2.17/bfd/elflink.c index 24545287fea..f1a67caeeaa 100644 --- a/gnu/usr.bin/binutils-2.17/bfd/elflink.c +++ b/gnu/usr.bin/binutils-2.17/bfd/elflink.c @@ -240,12 +240,30 @@ _bfd_elf_link_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) if (!_bfd_elf_define_linkage_sym (abfd, info, s, "_DYNAMIC")) return FALSE; - s = bfd_make_section_with_flags (abfd, ".hash", - flags | SEC_READONLY); - if (s == NULL - || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) - return FALSE; - elf_section_data (s)->this_hdr.sh_entsize = bed->s->sizeof_hash_entry; + if (info->emit_hash) + { + s = bfd_make_section_with_flags (abfd, ".hash", flags | SEC_READONLY); + if (s == NULL + || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) + return FALSE; + elf_section_data (s)->this_hdr.sh_entsize = bed->s->sizeof_hash_entry; + } + + if (info->emit_gnu_hash) + { + s = bfd_make_section_with_flags (abfd, ".gnu.hash", + flags | SEC_READONLY); + if (s == NULL + || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) + return FALSE; + /* For 64-bit ELF, .gnu.hash is a non-uniform entity size section: + 4 32-bit words followed by variable count of 64-bit words, then + variable count of 32-bit words. */ + if (bed->s->arch_size == 64) + elf_section_data (s)->this_hdr.sh_entsize = 0; + else + elf_section_data (s)->this_hdr.sh_entsize = 4; + } /* Let the backend create the rest of the sections. This lets the backend set the right flags. The backend will normally create @@ -4790,6 +4808,131 @@ elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data) return TRUE; } +struct collect_gnu_hash_codes +{ + bfd *output_bfd; + const struct elf_backend_data *bed; + unsigned long int nsyms; + unsigned long int maskbits; + unsigned long int *hashcodes; + unsigned long int *hashval; + unsigned long int *indx; + unsigned long int *counts; + bfd_vma *bitmask; + bfd_byte *contents; + long int min_dynindx; + unsigned long int bucketcount; + unsigned long int symindx; + long int local_indx; + long int shift1, shift2; + unsigned long int mask; +}; + +/* This function will be called though elf_link_hash_traverse to store + all hash value of the exported symbols in an array. */ + +static bfd_boolean +elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data) +{ + struct collect_gnu_hash_codes *s = data; + const char *name; + char *p; + unsigned long ha; + char *alc = NULL; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + /* Ignore indirect symbols. These are added by the versioning code. */ + if (h->dynindx == -1) + return TRUE; + + /* Ignore also local symbols and undefined symbols. */ + if (! (*s->bed->elf_hash_symbol) (h)) + return TRUE; + + name = h->root.root.string; + p = strchr (name, ELF_VER_CHR); + if (p != NULL) + { + alc = bfd_malloc (p - name + 1); + memcpy (alc, name, p - name); + alc[p - name] = '\0'; + name = alc; + } + + /* Compute the hash value. */ + ha = bfd_elf_gnu_hash (name); + + /* Store the found hash value in the array for compute_bucket_count, + and also for .dynsym reordering purposes. */ + s->hashcodes[s->nsyms] = ha; + s->hashval[h->dynindx] = ha; + ++s->nsyms; + if (s->min_dynindx < 0 || s->min_dynindx > h->dynindx) + s->min_dynindx = h->dynindx; + + if (alc != NULL) + free (alc); + + return TRUE; +} + +/* This function will be called though elf_link_hash_traverse to do + final dynaminc symbol renumbering. */ + +static bfd_boolean +elf_renumber_gnu_hash_syms (struct elf_link_hash_entry *h, void *data) +{ + struct collect_gnu_hash_codes *s = data; + unsigned long int bucket; + unsigned long int val; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + /* Ignore indirect symbols. */ + if (h->dynindx == -1) + return TRUE; + + /* Ignore also local symbols and undefined symbols. */ + if (! (*s->bed->elf_hash_symbol) (h)) + { + if (h->dynindx >= s->min_dynindx) + h->dynindx = s->local_indx++; + return TRUE; + } + + bucket = s->hashval[h->dynindx] % s->bucketcount; + val = (s->hashval[h->dynindx] >> s->shift1) + & ((s->maskbits >> s->shift1) - 1); + s->bitmask[val] |= ((bfd_vma) 1) << (s->hashval[h->dynindx] & s->mask); + s->bitmask[val] + |= ((bfd_vma) 1) << ((s->hashval[h->dynindx] >> s->shift2) & s->mask); + val = s->hashval[h->dynindx] & ~(unsigned long int) 1; + if (s->counts[bucket] == 1) + /* Last element terminates the chain. */ + val |= 1; + bfd_put_32 (s->output_bfd, val, + s->contents + (s->indx[bucket] - s->symindx) * 4); + --s->counts[bucket]; + h->dynindx = s->indx[bucket]++; + return TRUE; +} + +/* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */ + +bfd_boolean +_bfd_elf_hash_symbol (struct elf_link_hash_entry *h) +{ + return !(h->forced_local + || h->root.type == bfd_link_hash_undefined + || h->root.type == bfd_link_hash_undefweak + || ((h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + && h->root.u.def.section->output_section == NULL)); +} + /* Array used to determine the number of hash table buckets to use based on the number of symbols there are. If there are fewer than 3 symbols we use 1 bucket, fewer than 17 symbols we use 3 buckets, @@ -4811,42 +4954,26 @@ static const size_t elf_buckets[] = Therefore the result is always a good payoff between few collisions (= short chain lengths) and table size. */ static size_t -compute_bucket_count (struct bfd_link_info *info) +compute_bucket_count (struct bfd_link_info *info, unsigned long int *hashcodes, + unsigned long int nsyms, int gnu_hash) { size_t dynsymcount = elf_hash_table (info)->dynsymcount; size_t best_size = 0; - unsigned long int *hashcodes; - unsigned long int *hashcodesp; unsigned long int i; bfd_size_type amt; - /* Compute the hash values for all exported symbols. At the same - time store the values in an array so that we could use them for - optimizations. */ - amt = dynsymcount; - amt *= sizeof (unsigned long int); - hashcodes = bfd_malloc (amt); - if (hashcodes == NULL) - return 0; - hashcodesp = hashcodes; - - /* Put all hash values in HASHCODES. */ - elf_link_hash_traverse (elf_hash_table (info), - elf_collect_hash_codes, &hashcodesp); - /* We have a problem here. The following code to optimize the table size requires an integer type with more the 32 bits. If BFD_HOST_U_64_BIT is set we know about such a type. */ #ifdef BFD_HOST_U_64_BIT if (info->optimize) { - unsigned long int nsyms = hashcodesp - hashcodes; size_t minsize; size_t maxsize; BFD_HOST_U_64_BIT best_chlen = ~((BFD_HOST_U_64_BIT) 0); - unsigned long int *counts ; bfd *dynobj = elf_hash_table (info)->dynobj; const struct elf_backend_data *bed = get_elf_backend_data (dynobj); + unsigned long int *counts; /* Possible optimization parameters: if we have NSYMS symbols we say that the hashing table must at least have NSYMS/4 and at most @@ -4855,6 +4982,13 @@ compute_bucket_count (struct bfd_link_info *info) if (minsize == 0) minsize = 1; best_size = maxsize = nsyms * 2; + if (gnu_hash) + { + if (minsize < 2) + minsize = 2; + if ((best_size & 31) == 0) + ++best_size; + } /* Create array where we count the collisions in. We must use bfd_malloc since the size could be large. */ @@ -4862,10 +4996,7 @@ compute_bucket_count (struct bfd_link_info *info) amt *= sizeof (unsigned long int); counts = bfd_malloc (amt); if (counts == NULL) - { - free (hashcodes); - return 0; - } + return 0; /* Compute the "optimal" size for the hash table. The criteria is a minimal chain length. The minor criteria is (of course) the size @@ -4877,6 +5008,9 @@ compute_bucket_count (struct bfd_link_info *info) unsigned long int j; unsigned long int fact; + if (gnu_hash && (i & 31) == 0) + continue; + memset (counts, '\0', i * sizeof (unsigned long int)); /* Determine how often each hash bucket is used. */ @@ -4892,9 +5026,9 @@ compute_bucket_count (struct bfd_link_info *info) # define BFD_TARGET_PAGESIZE (4096) # endif - /* We in any case need 2 + NSYMS entries for the size values and - the chains. */ - max = (2 + nsyms) * (bed->s->arch_size / 8); + /* We in any case need 2 + DYNSYMCOUNT entries for the size values + and the chains. */ + max = (2 + dynsymcount) * bed->s->sizeof_hash_entry; # if 1 /* Variant 1: optimize for short chains. We add the squares @@ -4904,7 +5038,7 @@ compute_bucket_count (struct bfd_link_info *info) max += counts[j] * counts[j]; /* This adds penalties for the overall size of the table. */ - fact = i / (BFD_TARGET_PAGESIZE / (bed->s->arch_size / 8)) + 1; + fact = i / (BFD_TARGET_PAGESIZE / bed->s->sizeof_hash_entry) + 1; max *= fact * fact; # else /* Variant 2: Optimize a lot more for small table. Here we @@ -4915,7 +5049,7 @@ compute_bucket_count (struct bfd_link_info *info) /* The overall size of the table is considered, but not as strong as in variant 1, where it is squared. */ - fact = i / (BFD_TARGET_PAGESIZE / (bed->s->arch_size / 8)) + 1; + fact = i / (BFD_TARGET_PAGESIZE / bed->s->sizeof_hash_entry) + 1; max *= fact; # endif @@ -4938,14 +5072,13 @@ compute_bucket_count (struct bfd_link_info *info) for (i = 0; elf_buckets[i] != 0; i++) { best_size = elf_buckets[i]; - if (dynsymcount < elf_buckets[i + 1]) + if (nsyms < elf_buckets[i + 1]) break; } + if (gnu_hash && best_size < 2) + best_size = 2; } - /* Free the arrays we needed. */ - free (hashcodes); - return best_size; } @@ -5304,7 +5437,10 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd, bfd_size_type strsize; strsize = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr); - if (!_bfd_elf_add_dynamic_entry (info, DT_HASH, 0) + 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)) || !_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) @@ -5706,8 +5842,6 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info) asection *s; bfd_size_type dynsymcount; unsigned long section_sym_count; - size_t bucketcount = 0; - size_t hash_entry_size; unsigned int dtagcount; dynobj = elf_hash_table (info)->dynobj; @@ -5758,23 +5892,215 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info) memset (s->contents, 0, section_sym_count * bed->s->sizeof_sym); } + elf_hash_table (info)->bucketcount = 0; + /* Compute the size of the hashing table. As a side effect this computes the hash values for all the names we export. */ - bucketcount = compute_bucket_count (info); + if (info->emit_hash) + { + unsigned long int *hashcodes; + unsigned long int *hashcodesp; + bfd_size_type amt; + unsigned long int nsyms; + size_t bucketcount; + size_t hash_entry_size; + + /* Compute the hash values for all exported symbols. At the same + time store the values in an array so that we could use them for + optimizations. */ + amt = dynsymcount * sizeof (unsigned long int); + hashcodes = bfd_malloc (amt); + if (hashcodes == NULL) + return FALSE; + hashcodesp = hashcodes; - s = bfd_get_section_by_name (dynobj, ".hash"); - BFD_ASSERT (s != NULL); - hash_entry_size = elf_section_data (s)->this_hdr.sh_entsize; - s->size = ((2 + bucketcount + dynsymcount) * hash_entry_size); - s->contents = bfd_zalloc (output_bfd, s->size); - if (s->contents == NULL) - return FALSE; + /* Put all hash values in HASHCODES. */ + elf_link_hash_traverse (elf_hash_table (info), + elf_collect_hash_codes, &hashcodesp); + + nsyms = hashcodesp - hashcodes; + bucketcount + = compute_bucket_count (info, hashcodes, nsyms, 0); + free (hashcodes); + + if (bucketcount == 0) + return FALSE; - bfd_put (8 * hash_entry_size, output_bfd, bucketcount, s->contents); - bfd_put (8 * hash_entry_size, output_bfd, dynsymcount, - s->contents + hash_entry_size); + elf_hash_table (info)->bucketcount = bucketcount; - elf_hash_table (info)->bucketcount = bucketcount; + s = bfd_get_section_by_name (dynobj, ".hash"); + BFD_ASSERT (s != NULL); + hash_entry_size = elf_section_data (s)->this_hdr.sh_entsize; + s->size = ((2 + bucketcount + dynsymcount) * hash_entry_size); + s->contents = bfd_zalloc (output_bfd, s->size); + if (s->contents == NULL) + return FALSE; + + bfd_put (8 * hash_entry_size, output_bfd, bucketcount, s->contents); + bfd_put (8 * hash_entry_size, output_bfd, dynsymcount, + s->contents + hash_entry_size); + } + + if (info->emit_gnu_hash) + { + size_t i, cnt; + unsigned char *contents; + struct collect_gnu_hash_codes cinfo; + bfd_size_type amt; + size_t bucketcount; + + memset (&cinfo, 0, sizeof (cinfo)); + + /* Compute the hash values for all exported symbols. At the same + time store the values in an array so that we could use them for + optimizations. */ + amt = dynsymcount * 2 * sizeof (unsigned long int); + cinfo.hashcodes = bfd_malloc (amt); + if (cinfo.hashcodes == NULL) + return FALSE; + + cinfo.hashval = cinfo.hashcodes + dynsymcount; + cinfo.min_dynindx = -1; + cinfo.output_bfd = output_bfd; + cinfo.bed = bed; + + /* Put all hash values in HASHCODES. */ + elf_link_hash_traverse (elf_hash_table (info), + elf_collect_gnu_hash_codes, &cinfo); + + bucketcount + = compute_bucket_count (info, cinfo.hashcodes, cinfo.nsyms, 1); + + if (bucketcount == 0) + { + free (cinfo.hashcodes); + return FALSE; + } + + s = bfd_get_section_by_name (dynobj, ".gnu.hash"); + BFD_ASSERT (s != NULL); + + if (cinfo.nsyms == 0) + { + /* Empty .gnu.hash section is special. */ + BFD_ASSERT (cinfo.min_dynindx == -1); + free (cinfo.hashcodes); + s->size = 5 * 4 + bed->s->arch_size / 8; + contents = bfd_zalloc (output_bfd, s->size); + if (contents == NULL) + return FALSE; + s->contents = contents; + /* 1 empty bucket. */ + bfd_put_32 (output_bfd, 1, contents); + /* SYMIDX above the special symbol 0. */ + bfd_put_32 (output_bfd, 1, contents + 4); + /* Just one word for bitmask. */ + bfd_put_32 (output_bfd, 1, contents + 8); + /* Only hash fn bloom filter. */ + bfd_put_32 (output_bfd, 0, contents + 12); + /* No hashes are valid - empty bitmask. */ + bfd_put (bed->s->arch_size, output_bfd, 0, contents + 16); + /* No hashes in the only bucket. */ + bfd_put_32 (output_bfd, 0, + contents + 16 + bed->s->arch_size / 8); + } + else + { + BFD_ASSERT (cinfo.min_dynindx != -1); + unsigned long int maskwords, maskbitslog2; + + maskbitslog2 = bfd_log2 (cinfo.nsyms) + 1; + if (maskbitslog2 < 3) + maskbitslog2 = 5; + else if ((1 << (maskbitslog2 - 2)) & cinfo.nsyms) + maskbitslog2 = maskbitslog2 + 3; + else + maskbitslog2 = maskbitslog2 + 2; + if (bed->s->arch_size == 64) + { + if (maskbitslog2 == 5) + maskbitslog2 = 6; + cinfo.shift1 = 6; + } + else + cinfo.shift1 = 5; + cinfo.mask = (1 << cinfo.shift1) - 1; + cinfo.shift2 = maskbitslog2; + cinfo.maskbits = 1 << maskbitslog2; + maskwords = 1 << (maskbitslog2 - cinfo.shift1); + amt = bucketcount * sizeof (unsigned long int) * 2; + amt += maskwords * sizeof (bfd_vma); + cinfo.bitmask = bfd_malloc (amt); + if (cinfo.bitmask == NULL) + { + free (cinfo.hashcodes); + return FALSE; + } + + cinfo.counts = (void *) (cinfo.bitmask + maskwords); + cinfo.indx = cinfo.counts + bucketcount; + cinfo.symindx = dynsymcount - cinfo.nsyms; + memset (cinfo.bitmask, 0, maskwords * sizeof (bfd_vma)); + + /* Determine how often each hash bucket is used. */ + memset (cinfo.counts, 0, bucketcount * sizeof (cinfo.counts[0])); + for (i = 0; i < cinfo.nsyms; ++i) + ++cinfo.counts[cinfo.hashcodes[i] % bucketcount]; + + for (i = 0, cnt = cinfo.symindx; i < bucketcount; ++i) + if (cinfo.counts[i] != 0) + { + cinfo.indx[i] = cnt; + cnt += cinfo.counts[i]; + } + BFD_ASSERT (cnt == dynsymcount); + cinfo.bucketcount = bucketcount; + cinfo.local_indx = cinfo.min_dynindx; + + s->size = (4 + bucketcount + cinfo.nsyms) * 4; + s->size += cinfo.maskbits / 8; + contents = bfd_zalloc (output_bfd, s->size); + if (contents == NULL) + { + free (cinfo.bitmask); + free (cinfo.hashcodes); + return FALSE; + } + + s->contents = contents; + bfd_put_32 (output_bfd, bucketcount, contents); + bfd_put_32 (output_bfd, cinfo.symindx, contents + 4); + bfd_put_32 (output_bfd, maskwords, contents + 8); + bfd_put_32 (output_bfd, cinfo.shift2, contents + 12); + contents += 16 + cinfo.maskbits / 8; + + for (i = 0; i < bucketcount; ++i) + { + if (cinfo.counts[i] == 0) + bfd_put_32 (output_bfd, 0, contents); + else + bfd_put_32 (output_bfd, cinfo.indx[i], contents); + contents += 4; + } + + cinfo.contents = 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; + for (i = 0; i < maskwords; ++i) + { + bfd_put (bed->s->arch_size, output_bfd, cinfo.bitmask[i], + contents); + contents += bed->s->arch_size / 8; + } + + free (cinfo.bitmask); + free (cinfo.hashcodes); + } + } s = bfd_get_section_by_name (dynobj, ".dynstr"); BFD_ASSERT (s != NULL); @@ -6643,9 +6969,6 @@ elf_link_output_extsym (struct elf_link_hash_entry *h, void *data) { size_t bucketcount; size_t bucket; - size_t hash_entry_size; - bfd_byte *bucketpos; - bfd_vma chain; bfd_byte *esym; sym.st_name = h->dynstr_index; @@ -6659,15 +6982,23 @@ elf_link_output_extsym (struct elf_link_hash_entry *h, void *data) bucketcount = elf_hash_table (finfo->info)->bucketcount; bucket = h->u.elf_hash_value % bucketcount; - hash_entry_size - = elf_section_data (finfo->hash_sec)->this_hdr.sh_entsize; - bucketpos = ((bfd_byte *) finfo->hash_sec->contents - + (bucket + 2) * hash_entry_size); - chain = bfd_get (8 * hash_entry_size, finfo->output_bfd, bucketpos); - bfd_put (8 * hash_entry_size, finfo->output_bfd, h->dynindx, bucketpos); - bfd_put (8 * hash_entry_size, finfo->output_bfd, chain, - ((bfd_byte *) finfo->hash_sec->contents - + (bucketcount + 2 + h->dynindx) * hash_entry_size)); + + if (finfo->hash_sec != NULL) + { + size_t hash_entry_size; + bfd_byte *bucketpos; + bfd_vma chain; + + hash_entry_size + = elf_section_data (finfo->hash_sec)->this_hdr.sh_entsize; + bucketpos = ((bfd_byte *) finfo->hash_sec->contents + + (bucket + 2) * hash_entry_size); + chain = bfd_get (8 * hash_entry_size, finfo->output_bfd, bucketpos); + bfd_put (8 * hash_entry_size, finfo->output_bfd, h->dynindx, bucketpos); + bfd_put (8 * hash_entry_size, finfo->output_bfd, chain, + ((bfd_byte *) finfo->hash_sec->contents + + (bucketcount + 2 + h->dynindx) * hash_entry_size)); + } if (finfo->symver_sec != NULL && finfo->symver_sec->contents != NULL) { @@ -7843,7 +8174,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info) { finfo.dynsym_sec = bfd_get_section_by_name (dynobj, ".dynsym"); finfo.hash_sec = bfd_get_section_by_name (dynobj, ".hash"); - BFD_ASSERT (finfo.dynsym_sec != NULL && finfo.hash_sec != NULL); + BFD_ASSERT (finfo.dynsym_sec != NULL); finfo.symver_sec = bfd_get_section_by_name (dynobj, ".gnu.version"); /* Note that it is OK if symver_sec is NULL. */ } @@ -8597,6 +8928,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info) case DT_HASH: name = ".hash"; goto get_vma; + case DT_GNU_HASH: + name = ".gnu.hash"; + goto get_vma; case DT_STRTAB: name = ".dynstr"; goto get_vma; diff --git a/gnu/usr.bin/binutils-2.17/bfd/elfxx-target.h b/gnu/usr.bin/binutils-2.17/bfd/elfxx-target.h index 503726d2b86..c8825d1b346 100644 --- a/gnu/usr.bin/binutils-2.17/bfd/elfxx-target.h +++ b/gnu/usr.bin/binutils-2.17/bfd/elfxx-target.h @@ -551,6 +551,10 @@ #define elf_backend_merge_symbol NULL #endif +#ifndef elf_backend_hash_symbol +#define elf_backend_hash_symbol _bfd_elf_hash_symbol +#endif + extern const struct elf_size_info _bfd_elfNN_size_info; #ifndef INCLUDED_TARGET_FILE @@ -628,6 +632,7 @@ static const struct elf_backend_data elfNN_bed = elf_backend_common_section_index, elf_backend_common_section, elf_backend_merge_symbol, + elf_backend_hash_symbol, elf_backend_link_order_error_handler, elf_backend_relplt_name, ELF_MACHINE_ALT1, diff --git a/gnu/usr.bin/binutils-2.17/binutils/readelf.c b/gnu/usr.bin/binutils-2.17/binutils/readelf.c index 5243db50fa2..aeb41c74be2 100644 --- a/gnu/usr.bin/binutils-2.17/binutils/readelf.c +++ b/gnu/usr.bin/binutils-2.17/binutils/readelf.c @@ -137,6 +137,7 @@ static unsigned long dynamic_syminfo_offset; static unsigned int dynamic_syminfo_nent; static char program_interpreter[64]; static bfd_vma dynamic_info[DT_RUNPATH + 1]; +static bfd_vma dynamic_info_DT_GNU_HASH; static bfd_vma version_info[16]; static Elf_Internal_Ehdr elf_header; static Elf_Internal_Shdr *section_headers; @@ -1505,7 +1506,6 @@ get_dynamic_type (unsigned long type) case DT_VERSYM: return "VERSYM"; - case DT_GNU_HASH: return "GNU_HASH"; case DT_TLSDESC_GOT: return "TLSDESC_GOT"; case DT_TLSDESC_PLT: return "TLSDESC_PLT"; case DT_RELACOUNT: return "RELACOUNT"; @@ -1525,6 +1525,7 @@ get_dynamic_type (unsigned long type) case DT_GNU_CONFLICTSZ: return "GNU_CONFLICTSZ"; case DT_GNU_LIBLIST: return "GNU_LIBLIST"; case DT_GNU_LIBLISTSZ: return "GNU_LIBLISTSZ"; + case DT_GNU_HASH: return "GNU_HASH"; default: if ((type >= DT_LOPROC) && (type <= DT_HIPROC)) @@ -2613,6 +2614,7 @@ get_section_type_name (unsigned int sh_type) case SHT_INIT_ARRAY: return "INIT_ARRAY"; case SHT_FINI_ARRAY: return "FINI_ARRAY"; case SHT_PREINIT_ARRAY: return "PREINIT_ARRAY"; + case SHT_GNU_HASH: return "GNU_HASH"; case SHT_GROUP: return "GROUP"; case SHT_SYMTAB_SHNDX: return "SYMTAB SECTION INDICIES"; case SHT_GNU_verdef: return "VERDEF"; @@ -2622,7 +2624,6 @@ get_section_type_name (unsigned int sh_type) case 0x6ffffffc: return "VERDEF"; case 0x7ffffffd: return "AUXILIARY"; case 0x7fffffff: return "FILTER"; - case SHT_GNU_HASH: return "GNU_HASH"; case SHT_GNU_LIBLIST: return "GNU_LIBLIST"; default: @@ -6265,6 +6266,15 @@ process_dynamic_section (FILE *file) } break; + case DT_GNU_HASH: + dynamic_info_DT_GNU_HASH = entry->d_un.d_val; + 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)] = @@ -6940,6 +6950,9 @@ process_symbol_table (FILE *file) bfd_vma nchains = 0; bfd_vma *buckets = NULL; bfd_vma *chains = NULL; + bfd_vma ngnubuckets = 0; + bfd_vma *gnubuckets = NULL; + bfd_vma *gnuchains = NULL; if (! do_syms && !do_histogram) return 1; @@ -7319,6 +7332,166 @@ process_symbol_table (FILE *file) free (chains); } + if (do_histogram && dynamic_info_DT_GNU_HASH) + { + unsigned char nb[16]; + bfd_vma i, maxchain = 0xffffffff, symidx, bitmaskwords; + unsigned long *lengths; + unsigned long *counts; + unsigned long hn; + unsigned long maxlength = 0; + unsigned long nzero_counts = 0; + unsigned long nsyms = 0; + bfd_vma buckets_vma; + + if (fseek (file, + (archive_file_offset + + offset_from_vma (file, dynamic_info_DT_GNU_HASH, + sizeof nb)), + SEEK_SET)) + { + error (_("Unable to seek to start of dynamic information")); + return 0; + } + + if (fread (nb, 16, 1, file) != 1) + { + error (_("Failed to read in number of buckets\n")); + return 0; + } + + ngnubuckets = byte_get (nb, 4); + symidx = byte_get (nb + 4, 4); + bitmaskwords = byte_get (nb + 8, 4); + buckets_vma = dynamic_info_DT_GNU_HASH + 16; + if (is_32bit_elf) + buckets_vma += bitmaskwords * 4; + else + buckets_vma += bitmaskwords * 8; + + if (fseek (file, + (archive_file_offset + + offset_from_vma (file, buckets_vma, 4)), + SEEK_SET)) + { + error (_("Unable to seek to start of dynamic information")); + return 0; + } + + gnubuckets = get_dynamic_data (file, ngnubuckets, 4); + + if (gnubuckets == NULL) + return 0; + + for (i = 0; i < ngnubuckets; i++) + if (gnubuckets[i] != 0) + { + if (gnubuckets[i] < symidx) + return 0; + + if (maxchain == 0xffffffff || gnubuckets[i] > maxchain) + maxchain = gnubuckets[i]; + } + + if (maxchain == 0xffffffff) + return 0; + + maxchain -= symidx; + + 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")); + return 0; + } + + do + { + if (fread (nb, 4, 1, file) != 1) + { + error (_("Failed to determine last chain length\n")); + return 0; + } + + if (maxchain + 1 == 0) + return 0; + + ++maxchain; + } + while ((byte_get (nb, 4) & 1) == 0); + + if (fseek (file, + (archive_file_offset + + offset_from_vma (file, buckets_vma + 4 * ngnubuckets, 4)), + SEEK_SET)) + { + error (_("Unable to seek to start of dynamic information")); + return 0; + } + + gnuchains = get_dynamic_data (file, maxchain, 4); + + if (gnuchains == NULL) + return 0; + + lengths = calloc (ngnubuckets, sizeof (*lengths)); + if (lengths == NULL) + { + error (_("Out of memory")); + return 0; + } + + printf (_("\nHistogram for `.gnu.hash' bucket list length (total of %lu buckets):\n"), + (unsigned long) ngnubuckets); + printf (_(" Length Number %% of total Coverage\n")); + + for (hn = 0; hn < ngnubuckets; ++hn) + if (gnubuckets[hn] != 0) + { + bfd_vma off, length = 1; + + for (off = gnubuckets[hn] - symidx; + (gnuchains[off] & 1) == 0; ++off) + ++length; + lengths[hn] = length; + if (length > maxlength) + maxlength = length; + nsyms += length; + } + + counts = calloc (maxlength + 1, sizeof (*counts)); + if (counts == NULL) + { + error (_("Out of memory")); + return 0; + } + + for (hn = 0; hn < ngnubuckets; ++hn) + ++counts[lengths[hn]]; + + if (ngnubuckets > 0) + { + unsigned long j; + printf (" 0 %-10lu (%5.1f%%)\n", + counts[0], (counts[0] * 100.0) / ngnubuckets); + for (j = 1; j <= maxlength; ++j) + { + nzero_counts += counts[j] * j; + printf ("%7lu %-10lu (%5.1f%%) %5.1f%%\n", + j, counts[j], (counts[j] * 100.0) / ngnubuckets, + (nzero_counts * 100.0) / nsyms); + } + } + + free (counts); + free (lengths); + free (gnubuckets); + free (gnuchains); + } + return 1; } diff --git a/gnu/usr.bin/binutils-2.17/include/bfdlink.h b/gnu/usr.bin/binutils-2.17/include/bfdlink.h index 2f1d96dfd39..e419f77f625 100644 --- a/gnu/usr.bin/binutils-2.17/include/bfdlink.h +++ b/gnu/usr.bin/binutils-2.17/include/bfdlink.h @@ -335,6 +335,12 @@ struct bfd_link_info caching ELF symbol buffer. */ unsigned int reduce_memory_overheads: 1; + /* TRUE if .hash section should be created. */ + unsigned int emit_hash: 1; + + /* TRUE if .gnu.hash section should be created. */ + unsigned int emit_gnu_hash: 1; + /* What to do with unresolved symbols in an object file. When producing executables the default is GENERATE_ERROR. When producing shared libraries the default is IGNORE. The diff --git a/gnu/usr.bin/binutils-2.17/ld/emultempl/elf32.em b/gnu/usr.bin/binutils-2.17/ld/emultempl/elf32.em index 9fc5229bdcf..6786e6049e5 100644 --- a/gnu/usr.bin/binutils-2.17/ld/emultempl/elf32.em +++ b/gnu/usr.bin/binutils-2.17/ld/emultempl/elf32.em @@ -2063,6 +2063,7 @@ cat >>e${EMULATION_NAME}.c <<EOF #define OPTION_GROUP (OPTION_ENABLE_NEW_DTAGS + 1) #define OPTION_EH_FRAME_HDR (OPTION_GROUP + 1) #define OPTION_EXCLUDE_LIBS (OPTION_EH_FRAME_HDR + 1) +#define OPTION_HASH_STYLE (OPTION_EXCLUDE_LIBS + 1) static void gld${EMULATION_NAME}_add_options @@ -2079,6 +2080,7 @@ cat >>e${EMULATION_NAME}.c <<EOF {"enable-new-dtags", no_argument, NULL, OPTION_ENABLE_NEW_DTAGS}, {"eh-frame-hdr", no_argument, NULL, OPTION_EH_FRAME_HDR}, {"exclude-libs", required_argument, NULL, OPTION_EXCLUDE_LIBS}, + {"hash-style", required_argument, NULL, OPTION_HASH_STYLE}, {"Bgroup", no_argument, NULL, OPTION_GROUP}, EOF fi @@ -2135,6 +2137,24 @@ cat >>e${EMULATION_NAME}.c <<EOF add_excluded_libs (optarg); break; + case OPTION_HASH_STYLE: +#ifndef __mips64__ + link_info.emit_hash = FALSE; + link_info.emit_gnu_hash = FALSE; + if (strcmp (optarg, "sysv") == 0) + link_info.emit_hash = TRUE; + else if (strcmp (optarg, "gnu") == 0) + link_info.emit_gnu_hash = TRUE; + else if (strcmp (optarg, "both") == 0) + { + link_info.emit_hash = TRUE; + link_info.emit_gnu_hash = TRUE; + } + else + einfo (_("%P%F: invalid hash style \`%s'\n"), optarg); +#endif + break; + case 'z': if (strcmp (optarg, "initfirst") == 0) link_info.flags_1 |= (bfd_vma) DF_1_INITFIRST; @@ -2223,6 +2243,7 @@ cat >>e${EMULATION_NAME}.c <<EOF fprintf (file, _(" --disable-new-dtags\tDisable new dynamic tags\n")); fprintf (file, _(" --enable-new-dtags\tEnable new dynamic tags\n")); fprintf (file, _(" --eh-frame-hdr\tCreate .eh_frame_hdr section\n")); + fprintf (file, _(" --hash-style=STYLE\tSet hash style to sysv, gnu or both\n")); fprintf (file, _(" -z combreloc\t\tMerge dynamic relocs into one section and sort\n")); fprintf (file, _(" -z defs\t\tReport unresolved symbols in object files.\n")); fprintf (file, _(" -z execstack\t\tMark executable as requiring executable stack\n")); diff --git a/gnu/usr.bin/binutils-2.17/ld/ld.texinfo b/gnu/usr.bin/binutils-2.17/ld/ld.texinfo index 2fc9f35c441..b771b016510 100644 --- a/gnu/usr.bin/binutils-2.17/ld/ld.texinfo +++ b/gnu/usr.bin/binutils-2.17/ld/ld.texinfo @@ -1888,6 +1888,16 @@ time it takes the linker to perform its tasks, at the expense of increasing the linker's memory requirements. Similarly reducing this value can reduce the memory requirements at the expense of speed. +@kindex --hash-style=@var{style} +@item --hash-style=@var{style} +Set the type of linker's hash table(s). @var{style} can be either +@code{sysv} for classic ELF @code{.hash} section, @code{gnu} for +new style GNU @code{.gnu.hash} section or @code{both} for both +the classic ELF @code{.hash} and new style GNU @code{.gnu.hash} +hash tables. The default is @code{both}, except on mips64 where +this option has no effect and only the classic ELF @code{.hash} is +supported. + @kindex --reduce-memory-overheads @item --reduce-memory-overheads This option reduces memory requirements at ld runtime, at the expense of diff --git a/gnu/usr.bin/binutils-2.17/ld/ldmain.c b/gnu/usr.bin/binutils-2.17/ld/ldmain.c index 8019881e26b..e76dd8219b0 100644 --- a/gnu/usr.bin/binutils-2.17/ld/ldmain.c +++ b/gnu/usr.bin/binutils-2.17/ld/ldmain.c @@ -313,6 +313,12 @@ main (int argc, char **argv) link_info.create_object_symbols_section = NULL; link_info.gc_sym_list = NULL; link_info.base_file = NULL; + link_info.emit_hash = TRUE; +#ifndef __mips64__ + link_info.emit_gnu_hash = TRUE; +#else + link_info.emit_gnu_hash = FALSE; +#endif /* SVR4 linkers seem to set DT_INIT and DT_FINI based on magic _init and _fini symbols. We are compatible. */ link_info.init_function = "_init"; diff --git a/gnu/usr.bin/binutils-2.17/ld/scripttempl/elf.sc b/gnu/usr.bin/binutils-2.17/ld/scripttempl/elf.sc index bb4e52405a0..7ee1edf06d4 100644 --- a/gnu/usr.bin/binutils-2.17/ld/scripttempl/elf.sc +++ b/gnu/usr.bin/binutils-2.17/ld/scripttempl/elf.sc @@ -302,6 +302,7 @@ SECTIONS ${INITIAL_READONLY_SECTIONS} ${TEXT_DYNAMIC+${DYNAMIC}} .hash ${RELOCATING-0} : { *(.hash) } + .gnu.hash ${RELOCATING-0} : { *(.gnu.hash) } .dynsym ${RELOCATING-0} : { *(.dynsym) } .dynstr ${RELOCATING-0} : { *(.dynstr) } .gnu.version ${RELOCATING-0} : { *(.gnu.version) } |