|
|
const struct elf_backend_data *bed; | const struct elf_backend_data *bed; |
bfd_byte *extdyn; | bfd_byte *extdyn; |
| |
_bfd_elf_strtab_finalize (dynstr); |
_bfd_elf_strtab_finalize (dynstr, info->dynsort ? |
|
elf_hash_table (info)->bucketcount : 0); |
size = _bfd_elf_strtab_size (dynstr); | size = _bfd_elf_strtab_size (dynstr); |
| |
bed = get_elf_backend_data (dynobj); | bed = get_elf_backend_data (dynobj); |
|
|
return FALSE; | return FALSE; |
} | } |
} | } |
|
|
/* 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 |
/* Compute the elf hash value of the name ignoring the version. */ |
elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data) |
unsigned long |
|
_bfd_elf_ver_hash (const char *name) |
{ | { |
unsigned long **valuep = data; |
|
const char *name; |
|
char *p; | char *p; |
unsigned long ha; | unsigned long ha; |
char *alc = NULL; | 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; |
|
|
|
name = h->root.root.string; |
|
p = strchr (name, ELF_VER_CHR); | p = strchr (name, ELF_VER_CHR); |
if (p != NULL) | if (p != NULL) |
{ | { |
|
|
/* Compute the hash value. */ | /* Compute the hash value. */ |
ha = bfd_elf_hash (name); | ha = bfd_elf_hash (name); |
| |
|
if (alc != NULL) |
|
free (alc); |
|
|
|
return ha; |
|
} |
|
|
|
|
|
/* 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_hash_codes (struct elf_link_hash_entry *h, void *data) |
|
{ |
|
unsigned long **valuep = data; |
|
unsigned long ha; |
|
|
|
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; |
|
|
|
ha = _bfd_elf_ver_hash (h->root.root.string); |
|
|
/* Store the found hash value in the array given as the argument. */ | /* Store the found hash value in the array given as the argument. */ |
*(*valuep)++ = ha; | *(*valuep)++ = ha; |
| |
|
|
later. */ | later. */ |
h->u.elf_hash_value = ha; | h->u.elf_hash_value = ha; |
| |
if (alc != NULL) |
|
free (alc); |
|
|
|
return TRUE; | return TRUE; |
} | } |
| |
|
|
return best_size; | return best_size; |
} | } |
| |
|
void _bfd_elf_link_hash_traverse |
|
(struct elf_link_hash_table *table, |
|
bfd_boolean (*func) (struct elf_link_hash_entry *, void *), |
|
void *info) |
|
{ |
|
if (!table->sorted) |
|
bfd_link_hash_traverse |
|
(&(table)->root, |
|
(bfd_boolean (*) (struct bfd_link_hash_entry *, void *)) (func), |
|
(info)); |
|
else |
|
{ |
|
unsigned int i; |
|
for (i = 0; i < table->sorted_size; i++) |
|
{ |
|
if (! func (table->sorted[i], info)) |
|
return; |
|
} |
|
} |
|
} |
|
|
|
/* Sort by elf hash value % buckets. */ |
|
static int |
|
elf_sort_dynsym_hash (const void *arg1, const void *arg2, |
|
const void *closure) |
|
{ |
|
size_t h1_bucket, h2_bucket; |
|
const struct elf_link_hash_entry *h1; |
|
const struct elf_link_hash_entry *h2; |
|
const bfd_size_type *bucketcount; |
|
|
|
h1 = *(const struct elf_link_hash_entry **) arg1; |
|
h2 = *(const struct elf_link_hash_entry **) arg2; |
|
bucketcount = closure; |
|
|
|
h1_bucket = h1->u.elf_hash_value % *bucketcount; |
|
h2_bucket = h2->u.elf_hash_value % *bucketcount; |
|
|
|
if (h1_bucket > h2_bucket) |
|
return 1; |
|
if (h1_bucket < h2_bucket) |
|
return -1; |
|
|
|
return 0; |
|
} |
|
|
|
struct elf_dynsym_sort_info |
|
{ |
|
bfd_boolean do_dynsym; |
|
unsigned int alloc_size; |
|
unsigned int sorted_size; |
|
struct elf_link_hash_entry **sorted_syms; |
|
}; |
|
|
|
/* collect sym entries into an array for later sorting. */ |
|
static bfd_boolean |
|
elf_sort_collect_dynsyms (struct elf_link_hash_entry *h, void *data) |
|
{ |
|
struct elf_dynsym_sort_info *sinfo = data; |
|
|
|
if ((sinfo->do_dynsym && h->dynindx < 0) |
|
|| (!sinfo->do_dynsym && h->dynindx >= 0)) |
|
return TRUE; |
|
|
|
if (sinfo->sorted_size >= sinfo->alloc_size) |
|
{ |
|
sinfo->alloc_size *= 2; |
|
/* FIXME: need to free this data too ... */ |
|
sinfo->sorted_syms = bfd_realloc |
|
(sinfo->sorted_syms, |
|
sizeof (struct elf_link_hash_entry *) * |
|
sinfo->alloc_size); |
|
} |
|
sinfo->sorted_syms [sinfo->sorted_size++] = h; |
|
|
|
return TRUE; |
|
} |
|
|
|
/* |
|
* Sort the exported elf symbols by elf_hash % bucketcount to |
|
* improve run-time linker cache behavior. Subsequent |
|
* elf_link_hash_traverse calls will reflect this new order. |
|
*/ |
|
static bfd_boolean |
|
_bfd_elf_sort_dynsyms (struct bfd_link_info *info) |
|
{ |
|
bfd_size_type bucketcount; |
|
struct elf_dynsym_sort_info sinfo; |
|
|
|
sinfo.alloc_size = 8; |
|
sinfo.sorted_syms = bfd_malloc (sizeof (struct elf_link_hash_entry *) * |
|
sinfo.alloc_size); |
|
if (!sinfo.sorted_syms) |
|
return FALSE; |
|
|
|
sinfo.sorted_size = 0; |
|
|
|
/* append dynsyms for sorting. */ |
|
sinfo.do_dynsym = TRUE; |
|
elf_link_hash_traverse (elf_hash_table (info), |
|
elf_sort_collect_dynsyms, &sinfo); |
|
|
|
/* sort. */ |
|
bucketcount = elf_hash_table (info)->bucketcount; |
|
bfd_qsort (sinfo.sorted_syms, sinfo.sorted_size, |
|
sizeof (struct elf_link_hash_entry *), |
|
elf_sort_dynsym_hash, |
|
&bucketcount); |
|
|
|
/* append everything else. */ |
|
sinfo.do_dynsym = FALSE; |
|
elf_link_hash_traverse (elf_hash_table (info), |
|
elf_sort_collect_dynsyms, &sinfo); |
|
|
|
/* freed in _bfd_elf_link_hash_table_free. */ |
|
elf_hash_table (info)->sorted = sinfo.sorted_syms; |
|
elf_hash_table (info)->sorted_size = sinfo.sorted_size; |
|
|
|
return TRUE; |
|
} |
|
|
/* Set up the sizes and contents of the ELF dynamic sections. This is | /* Set up the sizes and contents of the ELF dynamic sections. This is |
called by the ELF linker emulation before_allocation routine. We | called by the ELF linker emulation before_allocation routine. We |
must set the sizes of the sections before the linker sets the | must set the sizes of the sections before the linker sets the |
|
|
section symbol for each output section, which come first. | section symbol for each output section, which come first. |
Next come all of the back-end allocated local dynamic syms, | Next come all of the back-end allocated local dynamic syms, |
followed by the rest of the global symbols. */ | followed by the rest of the global symbols. */ |
|
/* To sort these optimally we need the correct bucketcount. */ |
| |
dynsymcount = _bfd_elf_link_renumber_dynsyms (output_bfd, info, | dynsymcount = _bfd_elf_link_renumber_dynsyms (output_bfd, info, |
§ion_sym_count); | §ion_sym_count); |
|
|
for (dtagcount = 0; dtagcount <= info->spare_dynamic_tags; ++dtagcount) | for (dtagcount = 0; dtagcount <= info->spare_dynamic_tags; ++dtagcount) |
if (!_bfd_elf_add_dynamic_entry (info, DT_NULL, 0)) | if (!_bfd_elf_add_dynamic_entry (info, DT_NULL, 0)) |
return FALSE; | return FALSE; |
|
|
|
/* Sort .dynsym to accelerate runtime linking. */ |
|
if (info->dynsort) |
|
{ |
|
if (!_bfd_elf_sort_dynsyms (info)) |
|
return FALSE; |
|
|
|
/* renumber to reflect the new sorting order. */ |
|
_bfd_elf_link_renumber_dynsyms (output_bfd, info, |
|
§ion_sym_count); |
|
} |
} | } |
| |
return TRUE; | return TRUE; |
|
|
bfd_vma sym_mask; | bfd_vma sym_mask; |
} u; | } u; |
enum elf_reloc_type_class type; | enum elf_reloc_type_class type; |
|
unsigned long elf_bucket; |
/* We use this as an array of size int_rels_per_ext_rel. */ | /* We use this as an array of size int_rels_per_ext_rel. */ |
Elf_Internal_Rela rela[1]; | Elf_Internal_Rela rela[1]; |
}; | }; |
|
|
const struct elf_link_sort_rela *b = B; | const struct elf_link_sort_rela *b = B; |
int copya, copyb; | int copya, copyb; |
| |
|
if (a->elf_bucket < b->elf_bucket) |
|
return -1; |
|
if (a->elf_bucket > b->elf_bucket) |
|
return 1; |
if (a->u.offset < b->u.offset) | if (a->u.offset < b->u.offset) |
return -1; | return -1; |
if (a->u.offset > b->u.offset) | if (a->u.offset > b->u.offset) |
|
|
} | } |
| |
static size_t | static size_t |
elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec) |
elf_link_sort_relocs (bfd *abfd, struct elf_final_link_info *finfo, |
|
asection **psec) |
{ | { |
|
struct bfd_link_info *info = finfo->info; |
asection *reldyn; | asection *reldyn; |
bfd_size_type count, size; | bfd_size_type count, size; |
size_t i, ret, sort_elt, ext_size; | size_t i, ret, sort_elt, ext_size; |
|
|
void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *); | void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *); |
struct bfd_link_order *lo; | struct bfd_link_order *lo; |
bfd_vma r_sym_mask; | bfd_vma r_sym_mask; |
|
int r_sym_shift; |
| |
reldyn = bfd_get_section_by_name (abfd, ".rela.dyn"); | reldyn = bfd_get_section_by_name (abfd, ".rela.dyn"); |
if (reldyn == NULL || reldyn->size == 0) | if (reldyn == NULL || reldyn->size == 0) |
|
|
} | } |
| |
if (bed->s->arch_size == 32) | if (bed->s->arch_size == 32) |
r_sym_mask = ~(bfd_vma) 0xff; |
{ |
|
r_sym_mask = ~(bfd_vma) 0xff; |
|
r_sym_shift = 8; |
|
} |
else | else |
r_sym_mask = ~(bfd_vma) 0xffffffff; |
{ |
|
r_sym_mask = ~(bfd_vma) 0xffffffff; |
|
r_sym_shift = 32; |
|
} |
| |
for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next) | for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next) |
if (lo->type == bfd_indirect_link_order) | if (lo->type == bfd_indirect_link_order) |
{ | { |
bfd_byte *erel, *erelend; | bfd_byte *erel, *erelend; |
asection *o = lo->u.indirect.section; | asection *o = lo->u.indirect.section; |
|
int base_offset = -1; |
|
int base_max = 0; |
|
|
|
if (elf_hash_table (info)->sorted_size > 0) |
|
{ |
|
base_offset = elf_hash_table (info)->sorted[0]->dynindx; |
|
base_max = base_offset + elf_hash_table (info)->sorted_size; |
|
} |
| |
if (o->contents == NULL && o->size != 0) | if (o->contents == NULL && o->size != 0) |
{ | { |
|
|
p = sort + o->output_offset / ext_size * sort_elt; | p = sort + o->output_offset / ext_size * sort_elt; |
while (erel < erelend) | while (erel < erelend) |
{ | { |
|
long dyn_idx; |
|
size_t bucketcount = elf_hash_table (info)->bucketcount; |
struct elf_link_sort_rela *s = (struct elf_link_sort_rela *) p; | struct elf_link_sort_rela *s = (struct elf_link_sort_rela *) p; |
(*swap_in) (abfd, erel, s->rela); | (*swap_in) (abfd, erel, s->rela); |
s->type = (*bed->elf_backend_reloc_type_class) (s->rela); | s->type = (*bed->elf_backend_reloc_type_class) (s->rela); |
s->u.sym_mask = r_sym_mask; | s->u.sym_mask = r_sym_mask; |
|
|
|
if (s->type != reloc_class_relative) |
|
dyn_idx = s->rela->r_info >> r_sym_shift; |
|
else |
|
dyn_idx = -1; |
|
|
|
if (info->dynsort && base_offset >= 0 |
|
&& dyn_idx < base_max && dyn_idx >= base_offset) |
|
{ |
|
struct elf_link_hash_entry *ent; |
|
ent = elf_hash_table (info)->sorted [dyn_idx - base_offset]; |
|
s->elf_bucket = ent->u.elf_hash_value % bucketcount; |
|
} |
|
else |
|
s->elf_bucket = 0; |
|
|
p += sort_elt; | p += sort_elt; |
erel += ext_size; | erel += ext_size; |
} | } |
|
|
} | } |
| |
if (dynamic && info->combreloc && dynobj != NULL) | if (dynamic && info->combreloc && dynobj != NULL) |
relativecount = elf_link_sort_relocs (abfd, info, &reldyn); |
relativecount = elf_link_sort_relocs (abfd, &finfo, &reldyn); |
| |
/* If we are linking against a dynamic object, or generating a | /* If we are linking against a dynamic object, or generating a |
shared library, finish up the dynamic linking information. */ | shared library, finish up the dynamic linking information. */ |