include/
* bfdlink.h (struct bfd_link_info): Add emit_hash and emit_gnu_hash bitfields. include/elf/ * common.h (SHT_GNU_HASH, DT_GNU_HASH): Define. ld/ * scripttempl/elf.sc: Add .gnu.hash section. * emultempl/elf32.em (OPTION_HASH_STYLE): Define. (gld${EMULATION_NAME}_add_options): Register --hash-style option. (gld${EMULATION_NAME}_handle_option): Handle it. (gld${EMULATION_NAME}_list_options): Document it. * ldmain.c (main): Initialize emit_hash and emit_gnu_hash. * ld.texinfo: Document --hash-style option. bfd/ * elf.c (_bfd_elf_print_private_bfd_data): Handle DT_GNU_HASH. (bfd_section_from_shdr, elf_fake_sections, assign_section_numbers): Handle SHT_GNU_HASH. (special_sections_g): Include .gnu.hash section. (bfd_elf_gnu_hash): New function. * elf-bfd.h (bfd_elf_gnu_hash, _bfd_elf_hash_symbol): New prototypes. (struct elf_backend_data): Add elf_hash_symbol method. * elflink.c (_bfd_elf_link_create_dynamic_sections): Create .hash only if info->emit_hash, create .gnu.hash section if info->emit_gnu_hash. (struct collect_gnu_hash_codes): New type. (elf_collect_gnu_hash_codes, elf_renumber_gnu_hash_syms, _bfd_elf_hash_symbol): New functions. (compute_bucket_count): Don't compute HASHCODES array, instead add that and NSYMS as arguments. Use bed->s->sizeof_hash_entry instead of bed->s->arch_size / 8. Fix .hash size estimation. When not optimizing, use the number of hashed symbols rather than dynsymcount. (bfd_elf_size_dynamic_sections): Only add DT_HASH if info->emit_hash, and ADD DT_GNU_HASH if info->emit_gnu_hash. (bfd_elf_size_dynsym_hash_dynstr): Size .hash only if info->emit_hash, adjust compute_bucket_count caller. Create and populate .gnu.hash section if info->emit_gnu_hash. (elf_link_output_extsym): Only populate .hash section if finfo->hash_sec != NULL. (bfd_elf_final_link): Adjust assertion. Handle DT_GNU_HASH. * elfxx-target.h (elf_backend_hash_symbol): Define if not yet defined. (elfNN_bed): Add elf_backend_hash_symbol. * elf64-x86-64.c (elf64_x86_64_hash_symbol): New function. (elf_backend_hash_symbol): Define. * elf32-i386.c (elf_i386_hash_symbol): New function. (elf_backend_hash_symbol): Define. binutils/ * readelf.c (get_dynamic_type): Handle DT_GNU_HASH. (get_section_type_name): Handle SHT_GNU_HASH. (dynamic_info_DT_GNU_HASH): New variable. (process_dynamic_section): Handle DT_GNU_HASH. (process_symbol_table): Print also DT_GNU_HASH histogram. ld/testsuite/ * ld-powerpc/tlsso32.r: Adjust. * ld-powerpc/tlsso32.d: Adjust. * ld-powerpc/tlsso32.g: Adjust. * ld-powerpc/tlsso.r: Adjust. * ld-powerpc/tlsso.g: Adjust. * ld-powerpc/tlstocso.g: Adjust.
This commit is contained in:
parent
8a112c90fe
commit
fdc90cb46b
25 changed files with 757 additions and 76 deletions
|
@ -135,6 +135,7 @@ static unsigned long dynamic_syminfo_offset;
|
|||
static unsigned int dynamic_syminfo_nent;
|
||||
static char program_interpreter[64];
|
||||
static bfd_vma dynamic_info[DT_JMPREL + 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;
|
||||
|
@ -1501,6 +1502,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))
|
||||
|
@ -2571,6 +2573,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";
|
||||
|
@ -6245,6 +6248,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)] =
|
||||
|
@ -6920,6 +6932,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;
|
||||
|
@ -7299,6 +7314,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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue