* 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:
Jakub Jelinek 2006-07-10 21:40:25 +00:00
parent 8a112c90fe
commit fdc90cb46b
25 changed files with 757 additions and 76 deletions

View file

@ -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
@ -4811,6 +4829,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,
@ -4832,42 +4975,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
@ -4876,6 +5003,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. */
@ -4883,10 +5017,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
@ -4898,6 +5029,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. */
@ -4913,9 +5047,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
@ -4925,7 +5059,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
@ -4936,7 +5070,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
@ -4959,14 +5093,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;
}
@ -5324,7 +5457,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)
@ -5726,8 +5862,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;
@ -5778,23 +5912,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;
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;
/* 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;
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);
/* Put all hash values in HASHCODES. */
elf_link_hash_traverse (elf_hash_table (info),
elf_collect_hash_codes, &hashcodesp);
elf_hash_table (info)->bucketcount = bucketcount;
nsyms = hashcodesp - hashcodes;
bucketcount
= compute_bucket_count (info, hashcodes, nsyms, 0);
free (hashcodes);
if (bucketcount == 0)
return FALSE;
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.shift1;
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);
@ -6663,9 +6989,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;
@ -6679,15 +7002,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)
{
@ -7861,7 +8192,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. */
}
@ -8621,6 +8952,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;