struct elf_info_failed
{
boolean failed;
struct bfd_link_info *info;
struct bfd_elf_version_tree *verdefs;
};
static boolean is_global_data_symbol_definition
PARAMS ((bfd *, Elf_Internal_Sym *));
static boolean elf_link_is_defined_archive_symbol
PARAMS ((bfd *, carsym *));
static boolean elf_link_add_object_symbols
PARAMS ((bfd *, struct bfd_link_info *));
static boolean elf_link_add_archive_symbols
PARAMS ((bfd *, struct bfd_link_info *));
static boolean elf_merge_symbol
PARAMS ((bfd *, struct bfd_link_info *, const char *,
Elf_Internal_Sym *, asection **, bfd_vma *,
struct elf_link_hash_entry **, boolean *, boolean *,
boolean *, boolean));
static boolean elf_add_default_symbol
PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
const char *, Elf_Internal_Sym *, asection **, bfd_vma *,
boolean *, boolean, boolean));
static boolean elf_export_symbol
PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean elf_finalize_dynstr
PARAMS ((bfd *, struct bfd_link_info *));
static boolean elf_fix_symbol_flags
PARAMS ((struct elf_link_hash_entry *, struct elf_info_failed *));
static boolean elf_adjust_dynamic_symbol
PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean elf_link_find_version_dependencies
PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean elf_link_assign_sym_version
PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean elf_collect_hash_codes
PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean elf_link_read_relocs_from_section
PARAMS ((bfd *, Elf_Internal_Shdr *, PTR, Elf_Internal_Rela *));
static size_t compute_bucket_count
PARAMS ((struct bfd_link_info *));
static void elf_link_output_relocs
PARAMS ((bfd *, asection *, Elf_Internal_Shdr *, Elf_Internal_Rela *));
static boolean elf_link_size_reloc_section
PARAMS ((bfd *, Elf_Internal_Shdr *, asection *));
static void elf_link_adjust_relocs
PARAMS ((bfd *, Elf_Internal_Shdr *, unsigned int,
struct elf_link_hash_entry **));
static int elf_link_sort_cmp1
PARAMS ((const void *, const void *));
static int elf_link_sort_cmp2
PARAMS ((const void *, const void *));
static size_t elf_link_sort_relocs
PARAMS ((bfd *, struct bfd_link_info *, asection **));
static boolean elf_section_ignore_discarded_relocs
PARAMS ((asection *));
boolean
elf_bfd_link_add_symbols (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
switch (bfd_get_format (abfd))
{
case bfd_object:
return elf_link_add_object_symbols (abfd, info);
case bfd_archive:
return elf_link_add_archive_symbols (abfd, info);
default:
bfd_set_error (bfd_error_wrong_format);
return false;
}
}
static boolean
is_global_data_symbol_definition (abfd, sym)
bfd * abfd ATTRIBUTE_UNUSED;
Elf_Internal_Sym * sym;
{
if (ELF_ST_BIND (sym->st_info) != STB_GLOBAL
&& ELF_ST_BIND (sym->st_info) < STB_LOOS)
return false;
if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
return false;
if (sym->st_shndx == SHN_UNDEF)
return false;
if (sym->st_shndx == SHN_COMMON)
return false;
if (sym->st_shndx >= SHN_LORESERVE && sym->st_shndx < SHN_ABS)
return false;
return true;
}
static boolean
elf_link_is_defined_archive_symbol (abfd, symdef)
bfd * abfd;
carsym * symdef;
{
Elf_Internal_Shdr * hdr;
Elf_Internal_Shdr * shndx_hdr;
Elf_External_Sym * esym;
Elf_External_Sym * esymend;
Elf_External_Sym * buf = NULL;
Elf_External_Sym_Shndx * shndx_buf = NULL;
Elf_External_Sym_Shndx * shndx;
bfd_size_type symcount;
bfd_size_type extsymcount;
bfd_size_type extsymoff;
boolean result = false;
file_ptr pos;
bfd_size_type amt;
abfd = _bfd_get_elt_at_filepos (abfd, symdef->file_offset);
if (abfd == (bfd *) NULL)
return false;
if (! bfd_check_format (abfd, bfd_object))
return false;
if (abfd->archive_pass)
return false;
if ((abfd->flags & DYNAMIC) == 0 || elf_dynsymtab (abfd) == 0)
{
hdr = &elf_tdata (abfd)->symtab_hdr;
shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
}
else
{
hdr = &elf_tdata (abfd)->dynsymtab_hdr;
shndx_hdr = NULL;
}
symcount = hdr->sh_size / sizeof (Elf_External_Sym);
if (elf_bad_symtab (abfd))
{
extsymcount = symcount;
extsymoff = 0;
}
else
{
extsymcount = symcount - hdr->sh_info;
extsymoff = hdr->sh_info;
}
amt = extsymcount * sizeof (Elf_External_Sym);
buf = (Elf_External_Sym *) bfd_malloc (amt);
if (buf == NULL && extsymcount != 0)
return false;
pos = hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym);
if (bfd_seek (abfd, pos, SEEK_SET) != 0
|| bfd_bread ((PTR) buf, amt, abfd) != amt)
goto error_exit;
if (shndx_hdr != NULL && shndx_hdr->sh_size != 0)
{
amt = extsymcount * sizeof (Elf_External_Sym_Shndx);
shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
if (shndx_buf == NULL && extsymcount != 0)
goto error_exit;
pos = shndx_hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym_Shndx);
if (bfd_seek (abfd, pos, SEEK_SET) != 0
|| bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
goto error_exit;
}
esymend = buf + extsymcount;
for (esym = buf, shndx = shndx_buf;
esym < esymend;
esym++, shndx = (shndx != NULL ? shndx + 1 : NULL))
{
Elf_Internal_Sym sym;
const char * name;
elf_swap_symbol_in (abfd, esym, shndx, &sym);
name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link, sym.st_name);
if (name == (const char *) NULL)
break;
if (strcmp (name, symdef->name) == 0)
{
result = is_global_data_symbol_definition (abfd, & sym);
break;
}
}
error_exit:
if (shndx_buf != NULL)
free (shndx_buf);
if (buf != NULL)
free (buf);
return result;
}
static boolean
elf_link_add_archive_symbols (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
symindex c;
boolean *defined = NULL;
boolean *included = NULL;
carsym *symdefs;
boolean loop;
bfd_size_type amt;
if (! bfd_has_map (abfd))
{
if (bfd_openr_next_archived_file (abfd, (bfd *) NULL) == NULL)
return true;
bfd_set_error (bfd_error_no_armap);
return false;
}
c = bfd_ardata (abfd)->symdef_count;
if (c == 0)
return true;
amt = c;
amt *= sizeof (boolean);
defined = (boolean *) bfd_malloc (amt);
included = (boolean *) bfd_malloc (amt);
if (defined == (boolean *) NULL || included == (boolean *) NULL)
goto error_return;
memset (defined, 0, (size_t) amt);
memset (included, 0, (size_t) amt);
symdefs = bfd_ardata (abfd)->symdefs;
do
{
file_ptr last;
symindex i;
carsym *symdef;
carsym *symdefend;
loop = false;
last = -1;
symdef = symdefs;
symdefend = symdef + c;
for (i = 0; symdef < symdefend; symdef++, i++)
{
struct elf_link_hash_entry *h;
bfd *element;
struct bfd_link_hash_entry *undefs_tail;
symindex mark;
if (defined[i] || included[i])
continue;
if (symdef->file_offset == last)
{
included[i] = true;
continue;
}
h = elf_link_hash_lookup (elf_hash_table (info), symdef->name,
false, false, false);
if (h == NULL)
{
char *p, *copy;
p = strchr (symdef->name, ELF_VER_CHR);
if (p == NULL || p[1] != ELF_VER_CHR)
continue;
copy = bfd_alloc (abfd, (bfd_size_type) (p - symdef->name + 1));
if (copy == NULL)
goto error_return;
memcpy (copy, symdef->name, (size_t) (p - symdef->name));
copy[p - symdef->name] = '\0';
h = elf_link_hash_lookup (elf_hash_table (info), copy,
false, false, false);
bfd_release (abfd, copy);
}
if (h == NULL)
continue;
if (h->root.type == bfd_link_hash_common)
{
if (! elf_link_is_defined_archive_symbol (abfd, symdef))
continue;
}
else if (h->root.type != bfd_link_hash_undefined)
{
if (h->root.type != bfd_link_hash_undefweak)
defined[i] = true;
continue;
}
element = _bfd_get_elt_at_filepos (abfd, symdef->file_offset);
if (element == (bfd *) NULL)
goto error_return;
if (! bfd_check_format (element, bfd_object))
goto error_return;
if (element->archive_pass != 0)
{
bfd_set_error (bfd_error_bad_value);
goto error_return;
}
element->archive_pass = 1;
undefs_tail = info->hash->undefs_tail;
if (! (*info->callbacks->add_archive_element) (info, element,
symdef->name))
goto error_return;
if (! elf_link_add_object_symbols (element, info))
goto error_return;
if (undefs_tail != info->hash->undefs_tail)
loop = true;
mark = i;
do
{
included[mark] = true;
if (mark == 0)
break;
--mark;
}
while (symdefs[mark].file_offset == symdef->file_offset);
last = symdef->file_offset;
}
}
while (loop);
free (defined);
free (included);
return true;
error_return:
if (defined != (boolean *) NULL)
free (defined);
if (included != (boolean *) NULL)
free (included);
return false;
}
static boolean
elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
override, type_change_ok, size_change_ok, dt_needed)
bfd *abfd;
struct bfd_link_info *info;
const char *name;
Elf_Internal_Sym *sym;
asection **psec;
bfd_vma *pvalue;
struct elf_link_hash_entry **sym_hash;
boolean *override;
boolean *type_change_ok;
boolean *size_change_ok;
boolean dt_needed;
{
asection *sec;
struct elf_link_hash_entry *h;
int bind;
bfd *oldbfd;
boolean newdyn, olddyn, olddef, newdef, newdyncommon, olddyncommon;
*override = false;
sec = *psec;
bind = ELF_ST_BIND (sym->st_info);
if (! bfd_is_und_section (sec))
h = elf_link_hash_lookup (elf_hash_table (info), name, true, false, false);
else
h = ((struct elf_link_hash_entry *)
bfd_wrapped_link_hash_lookup (abfd, info, name, true, false, false));
if (h == NULL)
return false;
*sym_hash = h;
if (info->hash->creator != abfd->xvec)
return true;
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->root.type == bfd_link_hash_new)
{
h->elf_link_hash_flags &=~ ELF_LINK_NON_ELF;
return true;
}
switch (h->root.type)
{
default:
oldbfd = NULL;
break;
case bfd_link_hash_undefined:
case bfd_link_hash_undefweak:
oldbfd = h->root.u.undef.abfd;
break;
case bfd_link_hash_defined:
case bfd_link_hash_defweak:
oldbfd = h->root.u.def.section->owner;
break;
case bfd_link_hash_common:
oldbfd = h->root.u.c.p->section->owner;
break;
}
if (abfd == oldbfd
&& ((abfd->flags & DYNAMIC) == 0
|| (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0))
return true;
if ((abfd->flags & DYNAMIC) != 0)
newdyn = true;
else
newdyn = false;
if (oldbfd != NULL)
olddyn = (oldbfd->flags & DYNAMIC) != 0;
else
{
asection *hsec;
switch (h->root.type)
{
default:
hsec = NULL;
break;
case bfd_link_hash_defined:
case bfd_link_hash_defweak:
hsec = h->root.u.def.section;
break;
case bfd_link_hash_common:
hsec = h->root.u.c.p->section;
break;
}
if (hsec == NULL)
olddyn = false;
else
olddyn = (hsec->symbol->flags & BSF_DYNAMIC) != 0;
}
if (bfd_is_und_section (sec) || bfd_is_com_section (sec))
newdef = false;
else
newdef = true;
if (h->root.type == bfd_link_hash_undefined
|| h->root.type == bfd_link_hash_undefweak
|| h->root.type == bfd_link_hash_common)
olddef = false;
else
olddef = true;
if (newdyn
&& newdef
&& (sec->flags & SEC_ALLOC) != 0
&& (sec->flags & SEC_LOAD) == 0
&& sym->st_size > 0
&& bind != STB_WEAK
&& ELF_ST_TYPE (sym->st_info) != STT_FUNC)
newdyncommon = true;
else
newdyncommon = false;
if (olddyn
&& olddef
&& h->root.type == bfd_link_hash_defined
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& (h->root.u.def.section->flags & SEC_ALLOC) != 0
&& (h->root.u.def.section->flags & SEC_LOAD) == 0
&& h->size > 0
&& h->type != STT_FUNC)
olddyncommon = true;
else
olddyncommon = false;
if ((! dt_needed && h->root.type == bfd_link_hash_defweak)
|| h->root.type == bfd_link_hash_undefweak
|| bind == STB_WEAK)
*type_change_ok = true;
if (*type_change_ok
|| h->root.type == bfd_link_hash_undefined)
*size_change_ok = true;
if (olddyncommon
&& newdyncommon
&& sym->st_size != h->size)
{
if (! ((*info->callbacks->multiple_common)
(info, h->root.root.string, oldbfd, bfd_link_hash_common,
h->size, abfd, bfd_link_hash_common, sym->st_size)))
return false;
if (sym->st_size > h->size)
h->size = sym->st_size;
*size_change_ok = true;
}
if (newdyn
&& newdef
&& (olddef
|| (h->root.type == bfd_link_hash_common
&& (bind == STB_WEAK
|| ELF_ST_TYPE (sym->st_info) == STT_FUNC)))
&& (h->root.type != bfd_link_hash_defweak
|| dt_needed
|| bind == STB_WEAK))
{
*override = true;
newdef = false;
newdyncommon = false;
*psec = sec = bfd_und_section_ptr;
*size_change_ok = true;
if (h->root.type == bfd_link_hash_common)
*type_change_ok = true;
}
if (newdyncommon
&& h->root.type == bfd_link_hash_common)
{
*override = true;
newdef = false;
newdyncommon = false;
*pvalue = sym->st_size;
*psec = sec = bfd_com_section_ptr;
*size_change_ok = true;
}
if (! newdyn
&& (newdef
|| (bfd_is_com_section (sec)
&& (h->root.type == bfd_link_hash_defweak
|| h->type == STT_FUNC)))
&& olddyn
&& olddef
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& (bind != STB_WEAK
|| h->root.type == bfd_link_hash_defweak))
{
h->root.type = bfd_link_hash_undefined;
h->root.u.undef.abfd = h->root.u.def.section->owner;
*size_change_ok = true;
olddef = false;
olddyncommon = false;
if (bfd_is_com_section (sec))
*type_change_ok = true;
h->verinfo.vertree = NULL;
*sym_hash = h;
}
if (! newdyn
&& bfd_is_com_section (sec)
&& olddyncommon)
{
if (! ((*info->callbacks->multiple_common)
(info, h->root.root.string, oldbfd, bfd_link_hash_common,
h->size, abfd, bfd_link_hash_common, sym->st_size)))
return false;
if (h->size > *pvalue)
*pvalue = h->size;
olddef = false;
olddyncommon = false;
h->root.type = bfd_link_hash_undefined;
h->root.u.undef.abfd = h->root.u.def.section->owner;
*size_change_ok = true;
*type_change_ok = true;
h->verinfo.vertree = NULL;
}
if (olddef
&& ! dt_needed
&& h->root.type == bfd_link_hash_defweak
&& newdef
&& newdyn
&& bind != STB_WEAK)
{
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0)
h->elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR;
else if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0)
h->elf_link_hash_flags |= ELF_LINK_HASH_REF_DYNAMIC;
h->elf_link_hash_flags &= ~ (ELF_LINK_HASH_DEF_REGULAR
| ELF_LINK_HASH_DEF_DYNAMIC);
*sym_hash = h;
}
if (olddef
&& olddyn
&& h->root.type != bfd_link_hash_defweak
&& newdef
&& ! newdyn
&& bind == STB_WEAK)
*override = true;
return true;
}
static boolean
elf_add_default_symbol (abfd, info, h, name, sym, sec, value,
dynsym, override, dt_needed)
bfd *abfd;
struct bfd_link_info *info;
struct elf_link_hash_entry *h;
const char *name;
Elf_Internal_Sym *sym;
asection **sec;
bfd_vma *value;
boolean *dynsym;
boolean override;
boolean dt_needed;
{
boolean type_change_ok;
boolean size_change_ok;
char *shortname;
struct elf_link_hash_entry *hi;
struct elf_backend_data *bed;
boolean collect;
boolean dynamic;
char *p;
p = strchr (name, ELF_VER_CHR);
if (p == NULL || p[1] != ELF_VER_CHR)
return true;
if (override)
{
hi = elf_link_hash_lookup (elf_hash_table (info), name, true,
false, false);
BFD_ASSERT (hi != NULL);
if (hi == h)
return true;
while (hi->root.type == bfd_link_hash_indirect
|| hi->root.type == bfd_link_hash_warning)
{
hi = (struct elf_link_hash_entry *) hi->root.u.i.link;
if (hi == h)
return true;
}
}
bed = get_elf_backend_data (abfd);
collect = bed->collect;
dynamic = (abfd->flags & DYNAMIC) != 0;
shortname = bfd_hash_allocate (&info->hash->table,
(size_t) (p - name + 1));
if (shortname == NULL)
return false;
strncpy (shortname, name, (size_t) (p - name));
shortname [p - name] = '\0';
type_change_ok = false;
size_change_ok = false;
if (! elf_merge_symbol (abfd, info, shortname, sym, sec, value,
&hi, &override, &type_change_ok,
&size_change_ok, dt_needed))
return false;
if (! override)
{
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, shortname, BSF_INDIRECT, bfd_ind_section_ptr,
(bfd_vma) 0, name, false, collect,
(struct bfd_link_hash_entry **) &hi)))
return false;
}
else
{
while (hi->root.type == bfd_link_hash_indirect
|| hi->root.type == bfd_link_hash_warning)
hi = (struct elf_link_hash_entry *) hi->root.u.i.link;
h->root.type = bfd_link_hash_indirect;
h->root.u.i.link = (struct bfd_link_hash_entry *) hi;
if (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)
{
h->elf_link_hash_flags &=~ ELF_LINK_HASH_DEF_DYNAMIC;
hi->elf_link_hash_flags |= ELF_LINK_HASH_REF_DYNAMIC;
if (hi->elf_link_hash_flags
& (ELF_LINK_HASH_REF_REGULAR
| ELF_LINK_HASH_DEF_REGULAR))
{
if (! _bfd_elf_link_record_dynamic_symbol (info, hi))
return false;
}
}
hi = h;
}
if (hi->root.type == bfd_link_hash_indirect)
{
struct elf_link_hash_entry *ht;
BFD_ASSERT ((hi->elf_link_hash_flags
& (ELF_LINK_HASH_DEF_DYNAMIC
| ELF_LINK_HASH_DEF_REGULAR)) == 0);
ht = (struct elf_link_hash_entry *) hi->root.u.i.link;
(*bed->elf_backend_copy_indirect_symbol) (ht, hi);
if (! *dynsym)
{
if (! dynamic)
{
if (info->shared
|| ((hi->elf_link_hash_flags
& ELF_LINK_HASH_REF_DYNAMIC) != 0))
*dynsym = true;
}
else
{
if ((hi->elf_link_hash_flags
& ELF_LINK_HASH_REF_REGULAR) != 0)
*dynsym = true;
}
}
}
shortname = bfd_hash_allocate (&info->hash->table, strlen (name));
if (shortname == NULL)
return false;
strncpy (shortname, name, (size_t) (p - name));
strcpy (shortname + (p - name), p + 1);
type_change_ok = false;
size_change_ok = false;
if (! elf_merge_symbol (abfd, info, shortname, sym, sec, value,
&hi, &override, &type_change_ok,
&size_change_ok, dt_needed))
return false;
if (override)
{
(*_bfd_error_handler)
(_("%s: warning: unexpected redefinition of `%s'"),
bfd_archive_filename (abfd), shortname);
}
else
{
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, shortname, BSF_INDIRECT,
bfd_ind_section_ptr, (bfd_vma) 0, name, false,
collect, (struct bfd_link_hash_entry **) &hi)))
return false;
if (hi->root.type == bfd_link_hash_indirect)
{
BFD_ASSERT ((hi->elf_link_hash_flags
& (ELF_LINK_HASH_DEF_DYNAMIC
| ELF_LINK_HASH_DEF_REGULAR)) == 0);
(*bed->elf_backend_copy_indirect_symbol) (h, hi);
if (! *dynsym)
{
if (! dynamic)
{
if (info->shared
|| ((hi->elf_link_hash_flags
& ELF_LINK_HASH_REF_DYNAMIC) != 0))
*dynsym = true;
}
else
{
if ((hi->elf_link_hash_flags
& ELF_LINK_HASH_REF_REGULAR) != 0)
*dynsym = true;
}
}
}
}
return true;
}
static boolean
elf_link_add_object_symbols (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
boolean (*add_symbol_hook) PARAMS ((bfd *, struct bfd_link_info *,
const Elf_Internal_Sym *,
const char **, flagword *,
asection **, bfd_vma *));
boolean (*check_relocs) PARAMS ((bfd *, struct bfd_link_info *,
asection *, const Elf_Internal_Rela *));
boolean collect;
Elf_Internal_Shdr *hdr;
Elf_Internal_Shdr *shndx_hdr;
bfd_size_type symcount;
bfd_size_type extsymcount;
bfd_size_type extsymoff;
Elf_External_Sym *buf = NULL;
Elf_External_Sym_Shndx *shndx_buf = NULL;
Elf_External_Sym_Shndx *shndx;
struct elf_link_hash_entry **sym_hash;
boolean dynamic;
Elf_External_Versym *extversym = NULL;
Elf_External_Versym *ever;
Elf_External_Dyn *dynbuf = NULL;
struct elf_link_hash_entry *weaks;
Elf_External_Sym *esym;
Elf_External_Sym *esymend;
struct elf_backend_data *bed;
boolean dt_needed;
struct elf_link_hash_table * hash_table;
file_ptr pos;
bfd_size_type amt;
hash_table = elf_hash_table (info);
bed = get_elf_backend_data (abfd);
add_symbol_hook = bed->elf_add_symbol_hook;
collect = bed->collect;
if ((abfd->flags & DYNAMIC) == 0)
dynamic = false;
else
{
dynamic = true;
if (info->relocateable || info->hash->creator != abfd->xvec)
{
bfd_set_error (bfd_error_invalid_operation);
goto error_return;
}
}
if (! info->shared)
{
asection *s;
for (s = abfd->sections; s != NULL; s = s->next)
{
const char *name;
name = bfd_get_section_name (abfd, s);
if (strncmp (name, ".gnu.warning.", sizeof ".gnu.warning." - 1) == 0)
{
char *msg;
bfd_size_type sz;
name += sizeof ".gnu.warning." - 1;
if (dynamic && abfd->xvec == info->hash->creator)
{
struct elf_link_hash_entry *h;
h = elf_link_hash_lookup (hash_table, name,
false, false, true);
if (h != NULL
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak))
{
s->_raw_size = 0;
continue;
}
}
sz = bfd_section_size (abfd, s);
msg = (char *) bfd_alloc (abfd, sz + 1);
if (msg == NULL)
goto error_return;
if (! bfd_get_section_contents (abfd, s, msg, (file_ptr) 0, sz))
goto error_return;
msg[sz] = '\0';
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, name, BSF_WARNING, s, (bfd_vma) 0, msg,
false, collect, (struct bfd_link_hash_entry **) NULL)))
goto error_return;
if (! info->relocateable)
{
s->_raw_size = 0;
}
}
}
}
if (! dynamic || elf_dynsymtab (abfd) == 0)
{
hdr = &elf_tdata (abfd)->symtab_hdr;
shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
}
else
{
hdr = &elf_tdata (abfd)->dynsymtab_hdr;
shndx_hdr = NULL;
}
if (dynamic)
{
if (! _bfd_elf_slurp_version_tables (abfd))
goto error_return;
if (elf_dynversym (abfd) != 0)
{
Elf_Internal_Shdr *versymhdr;
versymhdr = &elf_tdata (abfd)->dynversym_hdr;
extversym = (Elf_External_Versym *) bfd_malloc (versymhdr->sh_size);
if (extversym == NULL)
goto error_return;
amt = versymhdr->sh_size;
if (bfd_seek (abfd, versymhdr->sh_offset, SEEK_SET) != 0
|| bfd_bread ((PTR) extversym, amt, abfd) != amt)
goto error_return;
}
}
symcount = hdr->sh_size / sizeof (Elf_External_Sym);
if (elf_bad_symtab (abfd))
{
extsymcount = symcount;
extsymoff = 0;
}
else
{
extsymcount = symcount - hdr->sh_info;
extsymoff = hdr->sh_info;
}
amt = extsymcount * sizeof (Elf_External_Sym);
buf = (Elf_External_Sym *) bfd_malloc (amt);
if (buf == NULL && extsymcount != 0)
goto error_return;
if (shndx_hdr != NULL && shndx_hdr->sh_size != 0)
{
amt = extsymcount * sizeof (Elf_External_Sym_Shndx);
shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
if (shndx_buf == NULL && extsymcount != 0)
goto error_return;
}
amt = extsymcount * sizeof (struct elf_link_hash_entry *);
sym_hash = (struct elf_link_hash_entry **) bfd_alloc (abfd, amt);
if (sym_hash == NULL)
goto error_return;
elf_sym_hashes (abfd) = sym_hash;
dt_needed = false;
if (! dynamic)
{
if (info->shared
&& is_elf_hash_table (info)
&& ! hash_table->dynamic_sections_created
&& abfd->xvec == info->hash->creator)
{
if (! elf_link_create_dynamic_sections (abfd, info))
goto error_return;
}
}
else if (! is_elf_hash_table (info))
goto error_return;
else
{
asection *s;
boolean add_needed;
const char *name;
bfd_size_type oldsize;
bfd_size_type strindex;
add_needed = true;
name = bfd_get_filename (abfd);
if (elf_dt_name (abfd) != NULL)
{
name = elf_dt_name (abfd);
if (*name == '\0')
{
if (elf_dt_soname (abfd) != NULL)
dt_needed = true;
add_needed = false;
}
}
s = bfd_get_section_by_name (abfd, ".dynamic");
if (s != NULL)
{
Elf_External_Dyn *extdyn;
Elf_External_Dyn *extdynend;
int elfsec;
unsigned long shlink;
int rpath;
int runpath;
dynbuf = (Elf_External_Dyn *) bfd_malloc (s->_raw_size);
if (dynbuf == NULL)
goto error_return;
if (! bfd_get_section_contents (abfd, s, (PTR) dynbuf,
(file_ptr) 0, s->_raw_size))
goto error_return;
elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
if (elfsec == -1)
goto error_return;
shlink = elf_elfsections (abfd)[elfsec]->sh_link;
{
Elf_Internal_Shdr *shdr = elf_elfsections (abfd)[shlink];
if (shdr->sh_type != SHT_STRTAB)
{
asection *ds = bfd_get_section_by_name (abfd, ".dynsym");
int elfdsec = _bfd_elf_section_from_bfd_section (abfd, ds);
if (elfdsec == -1)
goto error_return;
shlink = elf_elfsections (abfd)[elfdsec]->sh_link;
}
}
extdyn = dynbuf;
extdynend = extdyn + s->_raw_size / sizeof (Elf_External_Dyn);
rpath = 0;
runpath = 0;
for (; extdyn < extdynend; extdyn++)
{
Elf_Internal_Dyn dyn;
elf_swap_dyn_in (abfd, extdyn, &dyn);
if (dyn.d_tag == DT_SONAME)
{
unsigned int tagv = dyn.d_un.d_val;
name = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
if (name == NULL)
goto error_return;
}
if (dyn.d_tag == DT_NEEDED)
{
struct bfd_link_needed_list *n, **pn;
char *fnm, *anm;
unsigned int tagv = dyn.d_un.d_val;
amt = sizeof (struct bfd_link_needed_list);
n = (struct bfd_link_needed_list *) bfd_alloc (abfd, amt);
fnm = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
if (n == NULL || fnm == NULL)
goto error_return;
anm = bfd_alloc (abfd, (bfd_size_type) strlen (fnm) + 1);
if (anm == NULL)
goto error_return;
strcpy (anm, fnm);
n->name = anm;
n->by = abfd;
n->next = NULL;
for (pn = & hash_table->needed;
*pn != NULL;
pn = &(*pn)->next)
;
*pn = n;
}
if (dyn.d_tag == DT_RUNPATH)
{
struct bfd_link_needed_list *n, **pn;
char *fnm, *anm;
unsigned int tagv = dyn.d_un.d_val;
if (rpath && hash_table->runpath)
hash_table->runpath = NULL;
amt = sizeof (struct bfd_link_needed_list);
n = (struct bfd_link_needed_list *) bfd_alloc (abfd, amt);
fnm = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
if (n == NULL || fnm == NULL)
goto error_return;
anm = bfd_alloc (abfd, (bfd_size_type) strlen (fnm) + 1);
if (anm == NULL)
goto error_return;
strcpy (anm, fnm);
n->name = anm;
n->by = abfd;
n->next = NULL;
for (pn = & hash_table->runpath;
*pn != NULL;
pn = &(*pn)->next)
;
*pn = n;
runpath = 1;
rpath = 0;
}
if (!runpath && dyn.d_tag == DT_RPATH)
{
struct bfd_link_needed_list *n, **pn;
char *fnm, *anm;
unsigned int tagv = dyn.d_un.d_val;
amt = sizeof (struct bfd_link_needed_list);
n = (struct bfd_link_needed_list *) bfd_alloc (abfd, amt);
fnm = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
if (n == NULL || fnm == NULL)
goto error_return;
anm = bfd_alloc (abfd, (bfd_size_type) strlen (fnm) + 1);
if (anm == NULL)
goto error_return;
strcpy (anm, fnm);
n->name = anm;
n->by = abfd;
n->next = NULL;
for (pn = & hash_table->runpath;
*pn != NULL;
pn = &(*pn)->next)
;
*pn = n;
rpath = 1;
}
}
free (dynbuf);
dynbuf = NULL;
}
bfd_section_list_clear (abfd);
if (! hash_table->dynamic_sections_created)
if (! elf_link_create_dynamic_sections (abfd, info))
goto error_return;
if (add_needed)
{
oldsize = _bfd_elf_strtab_size (hash_table->dynstr);
strindex = _bfd_elf_strtab_add (hash_table->dynstr, name, false);
if (strindex == (bfd_size_type) -1)
goto error_return;
if (oldsize == _bfd_elf_strtab_size (hash_table->dynstr))
{
asection *sdyn;
Elf_External_Dyn *dyncon, *dynconend;
sdyn = bfd_get_section_by_name (hash_table->dynobj, ".dynamic");
BFD_ASSERT (sdyn != NULL);
dyncon = (Elf_External_Dyn *) sdyn->contents;
dynconend = (Elf_External_Dyn *) (sdyn->contents +
sdyn->_raw_size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
elf_swap_dyn_in (hash_table->dynobj, dyncon, & dyn);
if (dyn.d_tag == DT_NEEDED
&& dyn.d_un.d_val == strindex)
{
if (buf != NULL)
free (buf);
if (extversym != NULL)
free (extversym);
_bfd_elf_strtab_delref (hash_table->dynstr, strindex);
return true;
}
}
}
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_NEEDED, strindex))
goto error_return;
}
if (*name == '\0')
name = basename (bfd_get_filename (abfd));
elf_dt_name (abfd) = name;
}
pos = hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym);
amt = extsymcount * sizeof (Elf_External_Sym);
if (bfd_seek (abfd, pos, SEEK_SET) != 0
|| bfd_bread ((PTR) buf, amt, abfd) != amt)
goto error_return;
if (shndx_hdr != NULL && shndx_hdr->sh_size != 0)
{
amt = extsymcount * sizeof (Elf_External_Sym_Shndx);
pos = shndx_hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym_Shndx);
if (bfd_seek (abfd, pos, SEEK_SET) != 0
|| bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
goto error_return;
}
weaks = NULL;
ever = extversym != NULL ? extversym + extsymoff : NULL;
esymend = buf + extsymcount;
for (esym = buf, shndx = shndx_buf;
esym < esymend;
esym++, sym_hash++, ever = (ever != NULL ? ever + 1 : NULL),
shndx = (shndx != NULL ? shndx + 1 : NULL))
{
Elf_Internal_Sym sym;
int bind;
bfd_vma value;
asection *sec;
flagword flags;
const char *name;
struct elf_link_hash_entry *h;
boolean definition;
boolean size_change_ok, type_change_ok;
boolean new_weakdef;
unsigned int old_alignment;
boolean override;
override = false;
elf_swap_symbol_in (abfd, esym, shndx, &sym);
flags = BSF_NO_FLAGS;
sec = NULL;
value = sym.st_value;
*sym_hash = NULL;
bind = ELF_ST_BIND (sym.st_info);
if (bind == STB_LOCAL)
{
continue;
}
else if (bind == STB_GLOBAL)
{
if (sym.st_shndx != SHN_UNDEF
&& sym.st_shndx != SHN_COMMON)
flags = BSF_GLOBAL;
}
else if (bind == STB_WEAK)
flags = BSF_WEAK;
else
{
}
if (sym.st_shndx == SHN_UNDEF)
sec = bfd_und_section_ptr;
else if (sym.st_shndx < SHN_LORESERVE || sym.st_shndx > SHN_HIRESERVE)
{
sec = section_from_elf_index (abfd, sym.st_shndx);
if (sec == NULL)
sec = bfd_abs_section_ptr;
else if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
value -= sec->vma;
}
else if (sym.st_shndx == SHN_ABS)
sec = bfd_abs_section_ptr;
else if (sym.st_shndx == SHN_COMMON)
{
sec = bfd_com_section_ptr;
value = sym.st_size;
}
else
{
}
name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link, sym.st_name);
if (name == (const char *) NULL)
goto error_return;
if (add_symbol_hook)
{
if (! (*add_symbol_hook) (abfd, info, &sym, &name, &flags, &sec,
&value))
goto error_return;
if (name == (const char *) NULL)
continue;
}
if (sec == (asection *) NULL)
{
bfd_set_error (bfd_error_bad_value);
goto error_return;
}
if (bfd_is_und_section (sec)
|| bfd_is_com_section (sec))
definition = false;
else
definition = true;
size_change_ok = false;
type_change_ok = get_elf_backend_data (abfd)->type_change_ok;
old_alignment = 0;
if (info->hash->creator->flavour == bfd_target_elf_flavour)
{
Elf_Internal_Versym iver;
unsigned int vernum = 0;
if (ever != NULL)
{
_bfd_elf_swap_versym_in (abfd, ever, &iver);
vernum = iver.vs_vers & VERSYM_VERSION;
if ((iver.vs_vers & VERSYM_HIDDEN) != 0
|| (vernum > 1 && ! bfd_is_abs_section (sec)))
{
const char *verstr;
unsigned int namelen;
bfd_size_type newlen;
char *newname, *p;
if (sym.st_shndx != SHN_UNDEF)
{
if (vernum > elf_tdata (abfd)->dynverdef_hdr.sh_info)
{
(*_bfd_error_handler)
(_("%s: %s: invalid version %u (max %d)"),
bfd_archive_filename (abfd), name, vernum,
elf_tdata (abfd)->dynverdef_hdr.sh_info);
bfd_set_error (bfd_error_bad_value);
goto error_return;
}
else if (vernum > 1)
verstr =
elf_tdata (abfd)->verdef[vernum - 1].vd_nodename;
else
verstr = "";
}
else
{
Elf_Internal_Verneed *t;
verstr = NULL;
for (t = elf_tdata (abfd)->verref;
t != NULL;
t = t->vn_nextref)
{
Elf_Internal_Vernaux *a;
for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
{
if (a->vna_other == vernum)
{
verstr = a->vna_nodename;
break;
}
}
if (a != NULL)
break;
}
if (verstr == NULL)
{
(*_bfd_error_handler)
(_("%s: %s: invalid needed version %d"),
bfd_archive_filename (abfd), name, vernum);
bfd_set_error (bfd_error_bad_value);
goto error_return;
}
}
namelen = strlen (name);
newlen = namelen + strlen (verstr) + 2;
if ((iver.vs_vers & VERSYM_HIDDEN) == 0)
++newlen;
newname = (char *) bfd_alloc (abfd, newlen);
if (newname == NULL)
goto error_return;
strcpy (newname, name);
p = newname + namelen;
*p++ = ELF_VER_CHR;
if ((iver.vs_vers & VERSYM_HIDDEN) == 0
&& sym.st_shndx != SHN_UNDEF)
*p++ = ELF_VER_CHR;
strcpy (p, verstr);
name = newname;
}
}
if (! elf_merge_symbol (abfd, info, name, &sym, &sec, &value,
sym_hash, &override, &type_change_ok,
&size_change_ok, dt_needed))
goto error_return;
if (override)
definition = false;
h = *sym_hash;
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->root.type == bfd_link_hash_common)
old_alignment = h->root.u.c.p->alignment_power;
if (elf_tdata (abfd)->verdef != NULL
&& ! override
&& vernum > 1
&& definition)
h->verinfo.verdef = &elf_tdata (abfd)->verdef[vernum - 1];
}
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, name, flags, sec, value, (const char *) NULL,
false, collect, (struct bfd_link_hash_entry **) sym_hash)))
goto error_return;
h = *sym_hash;
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
*sym_hash = h;
new_weakdef = false;
if (dynamic
&& definition
&& (flags & BSF_WEAK) != 0
&& ELF_ST_TYPE (sym.st_info) != STT_FUNC
&& info->hash->creator->flavour == bfd_target_elf_flavour
&& h->weakdef == NULL)
{
h->weakdef = weaks;
weaks = h;
new_weakdef = true;
}
if (sym.st_shndx == SHN_COMMON
&& h->root.type == bfd_link_hash_common)
{
unsigned int align;
align = bfd_log2 (sym.st_value);
if (align > old_alignment
|| (sym.st_value == 1 && old_alignment == 0))
h->root.u.c.p->alignment_power = align;
}
if (info->hash->creator->flavour == bfd_target_elf_flavour)
{
int old_flags;
boolean dynsym;
int new_flag;
if (sym.st_size != 0
&& (definition || h->size == 0))
{
if (h->size != 0 && h->size != sym.st_size && ! size_change_ok)
(*_bfd_error_handler)
(_("Warning: size of symbol `%s' changed from %lu to %lu in %s"),
name, (unsigned long) h->size, (unsigned long) sym.st_size,
bfd_archive_filename (abfd));
h->size = sym.st_size;
}
if (h->root.type == bfd_link_hash_common)
h->size = h->root.u.c.size;
if (ELF_ST_TYPE (sym.st_info) != STT_NOTYPE
&& (definition || h->type == STT_NOTYPE))
{
if (h->type != STT_NOTYPE
&& h->type != ELF_ST_TYPE (sym.st_info)
&& ! type_change_ok)
(*_bfd_error_handler)
(_("Warning: type of symbol `%s' changed from %d to %d in %s"),
name, h->type, ELF_ST_TYPE (sym.st_info),
bfd_archive_filename (abfd));
h->type = ELF_ST_TYPE (sym.st_info);
}
if (sym.st_other != 0)
{
unsigned char hvis = ELF_ST_VISIBILITY (h->other);
unsigned char symvis = ELF_ST_VISIBILITY (sym.st_other);
if (symvis && (hvis > symvis || hvis == 0))
h->other = sym.st_other;
if (!symvis && !hvis
&& (definition || h->other == 0))
h->other = sym.st_other;
}
old_flags = h->elf_link_hash_flags;
dynsym = false;
if (! dynamic)
{
if (! definition)
{
new_flag = ELF_LINK_HASH_REF_REGULAR;
if (bind != STB_WEAK)
new_flag |= ELF_LINK_HASH_REF_REGULAR_NONWEAK;
}
else
new_flag = ELF_LINK_HASH_DEF_REGULAR;
if (info->shared
|| (old_flags & (ELF_LINK_HASH_DEF_DYNAMIC
| ELF_LINK_HASH_REF_DYNAMIC)) != 0)
dynsym = true;
}
else
{
if (! definition)
new_flag = ELF_LINK_HASH_REF_DYNAMIC;
else
new_flag = ELF_LINK_HASH_DEF_DYNAMIC;
if ((old_flags & (ELF_LINK_HASH_DEF_REGULAR
| ELF_LINK_HASH_REF_REGULAR)) != 0
|| (h->weakdef != NULL
&& ! new_weakdef
&& h->weakdef->dynindx != -1))
dynsym = true;
}
h->elf_link_hash_flags |= new_flag;
if (definition || h->root.type == bfd_link_hash_common)
if (! elf_add_default_symbol (abfd, info, h, name, &sym,
&sec, &value, &dynsym,
override, dt_needed))
goto error_return;
if (dynsym && h->dynindx == -1)
{
if (! _bfd_elf_link_record_dynamic_symbol (info, h))
goto error_return;
if (h->weakdef != NULL
&& ! new_weakdef
&& h->weakdef->dynindx == -1)
{
if (! _bfd_elf_link_record_dynamic_symbol (info, h->weakdef))
goto error_return;
}
}
else if (dynsym && h->dynindx != -1)
switch (ELF_ST_VISIBILITY (h->other))
{
case STV_INTERNAL:
case STV_HIDDEN:
(*bed->elf_backend_hide_symbol) (info, h, true);
break;
}
if (dt_needed && definition
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_REF_REGULAR) != 0)
{
bfd_size_type oldsize;
bfd_size_type strindex;
if (! is_elf_hash_table (info))
goto error_return;
dt_needed = false;
oldsize = _bfd_elf_strtab_size (hash_table->dynstr);
strindex = _bfd_elf_strtab_add (hash_table->dynstr,
elf_dt_soname (abfd), false);
if (strindex == (bfd_size_type) -1)
goto error_return;
if (oldsize == _bfd_elf_strtab_size (hash_table->dynstr))
{
asection *sdyn;
Elf_External_Dyn *dyncon, *dynconend;
sdyn = bfd_get_section_by_name (hash_table->dynobj,
".dynamic");
BFD_ASSERT (sdyn != NULL);
dyncon = (Elf_External_Dyn *) sdyn->contents;
dynconend = (Elf_External_Dyn *) (sdyn->contents +
sdyn->_raw_size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
elf_swap_dyn_in (hash_table->dynobj,
dyncon, &dyn);
BFD_ASSERT (dyn.d_tag != DT_NEEDED ||
dyn.d_un.d_val != strindex);
}
}
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_NEEDED, strindex))
goto error_return;
}
}
}
while (weaks != NULL)
{
struct elf_link_hash_entry *hlook;
asection *slook;
bfd_vma vlook;
struct elf_link_hash_entry **hpp;
struct elf_link_hash_entry **hppend;
hlook = weaks;
weaks = hlook->weakdef;
hlook->weakdef = NULL;
BFD_ASSERT (hlook->root.type == bfd_link_hash_defined
|| hlook->root.type == bfd_link_hash_defweak
|| hlook->root.type == bfd_link_hash_common
|| hlook->root.type == bfd_link_hash_indirect);
slook = hlook->root.u.def.section;
vlook = hlook->root.u.def.value;
hpp = elf_sym_hashes (abfd);
hppend = hpp + extsymcount;
for (; hpp < hppend; hpp++)
{
struct elf_link_hash_entry *h;
h = *hpp;
if (h != NULL && h != hlook
&& h->root.type == bfd_link_hash_defined
&& h->root.u.def.section == slook
&& h->root.u.def.value == vlook)
{
hlook->weakdef = h;
if (hlook->dynindx != -1
&& h->dynindx == -1)
{
if (! _bfd_elf_link_record_dynamic_symbol (info, h))
goto error_return;
}
if (h->dynindx != -1
&& hlook->dynindx == -1)
{
if (! _bfd_elf_link_record_dynamic_symbol (info, hlook))
goto error_return;
}
break;
}
}
}
if (buf != NULL)
{
free (buf);
buf = NULL;
}
if (extversym != NULL)
{
free (extversym);
extversym = NULL;
}
check_relocs = get_elf_backend_data (abfd)->check_relocs;
if (! dynamic
&& abfd->xvec == info->hash->creator
&& check_relocs != NULL)
{
asection *o;
for (o = abfd->sections; o != NULL; o = o->next)
{
Elf_Internal_Rela *internal_relocs;
boolean ok;
if ((o->flags & SEC_RELOC) == 0
|| o->reloc_count == 0
|| ((info->strip == strip_all || info->strip == strip_debugger)
&& (o->flags & SEC_DEBUGGING) != 0)
|| bfd_is_abs_section (o->output_section))
continue;
internal_relocs = (NAME(_bfd_elf,link_read_relocs)
(abfd, o, (PTR) NULL,
(Elf_Internal_Rela *) NULL,
info->keep_memory));
if (internal_relocs == NULL)
goto error_return;
ok = (*check_relocs) (abfd, info, o, internal_relocs);
if (! info->keep_memory)
free (internal_relocs);
if (! ok)
goto error_return;
}
}
if (! dynamic
&& ! info->relocateable
&& ! info->traditional_format
&& info->hash->creator->flavour == bfd_target_elf_flavour
&& is_elf_hash_table (info)
&& (info->strip != strip_all && info->strip != strip_debugger))
{
asection *stab, *stabstr;
stab = bfd_get_section_by_name (abfd, ".stab");
if (stab != NULL && !(stab->flags & SEC_MERGE))
{
stabstr = bfd_get_section_by_name (abfd, ".stabstr");
if (stabstr != NULL)
{
struct bfd_elf_section_data *secdata;
secdata = elf_section_data (stab);
if (! _bfd_link_section_stabs (abfd,
& hash_table->stab_info,
stab, stabstr,
&secdata->sec_info))
goto error_return;
if (secdata->sec_info)
secdata->sec_info_type = ELF_INFO_TYPE_STABS;
}
}
}
if (! info->relocateable && ! dynamic
&& is_elf_hash_table (info))
{
asection *s;
for (s = abfd->sections; s != NULL; s = s->next)
if (s->flags & SEC_MERGE)
{
struct bfd_elf_section_data *secdata;
secdata = elf_section_data (s);
if (! _bfd_merge_section (abfd,
& hash_table->merge_info,
s, &secdata->sec_info))
goto error_return;
else if (secdata->sec_info)
secdata->sec_info_type = ELF_INFO_TYPE_MERGE;
}
}
return true;
error_return:
if (buf != NULL)
free (buf);
if (dynbuf != NULL)
free (dynbuf);
if (extversym != NULL)
free (extversym);
return false;
}
boolean
elf_link_create_dynamic_sections (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
flagword flags;
register asection *s;
struct elf_link_hash_entry *h;
struct elf_backend_data *bed;
if (! is_elf_hash_table (info))
return false;
if (elf_hash_table (info)->dynamic_sections_created)
return true;
if (elf_hash_table (info)->dynobj == NULL)
elf_hash_table (info)->dynobj = abfd;
else
abfd = elf_hash_table (info)->dynobj;
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
| SEC_IN_MEMORY | SEC_LINKER_CREATED);
if (! info->shared)
{
s = bfd_make_section (abfd, ".interp");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY))
return false;
}
if (! info->traditional_format
&& info->hash->creator->flavour == bfd_target_elf_flavour)
{
s = bfd_make_section (abfd, ".eh_frame_hdr");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, 2))
return false;
}
s = bfd_make_section (abfd, ".gnu.version_d");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, LOG_FILE_ALIGN))
return false;
s = bfd_make_section (abfd, ".gnu.version");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, 1))
return false;
s = bfd_make_section (abfd, ".gnu.version_r");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, LOG_FILE_ALIGN))
return false;
s = bfd_make_section (abfd, ".dynsym");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, LOG_FILE_ALIGN))
return false;
s = bfd_make_section (abfd, ".dynstr");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY))
return false;
if (elf_hash_table (info)->dynstr == NULL)
{
elf_hash_table (info)->dynstr = _bfd_elf_strtab_init ();
if (elf_hash_table (info)->dynstr == NULL)
return false;
}
s = bfd_make_section (abfd, ".dynamic");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags)
|| ! bfd_set_section_alignment (abfd, s, LOG_FILE_ALIGN))
return false;
h = NULL;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, "_DYNAMIC", BSF_GLOBAL, s, (bfd_vma) 0,
(const char *) NULL, false, get_elf_backend_data (abfd)->collect,
(struct bfd_link_hash_entry **) &h)))
return false;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_OBJECT;
if (info->shared
&& ! _bfd_elf_link_record_dynamic_symbol (info, h))
return false;
bed = get_elf_backend_data (abfd);
s = bfd_make_section (abfd, ".hash");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, LOG_FILE_ALIGN))
return false;
elf_section_data (s)->this_hdr.sh_entsize = bed->s->sizeof_hash_entry;
if (! (*bed->elf_backend_create_dynamic_sections) (abfd, info))
return false;
elf_hash_table (info)->dynamic_sections_created = true;
return true;
}
boolean
elf_add_dynamic_entry (info, tag, val)
struct bfd_link_info *info;
bfd_vma tag;
bfd_vma val;
{
Elf_Internal_Dyn dyn;
bfd *dynobj;
asection *s;
bfd_size_type newsize;
bfd_byte *newcontents;
if (! is_elf_hash_table (info))
return false;
dynobj = elf_hash_table (info)->dynobj;
s = bfd_get_section_by_name (dynobj, ".dynamic");
BFD_ASSERT (s != NULL);
newsize = s->_raw_size + sizeof (Elf_External_Dyn);
newcontents = (bfd_byte *) bfd_realloc (s->contents, newsize);
if (newcontents == NULL)
return false;
dyn.d_tag = tag;
dyn.d_un.d_val = val;
elf_swap_dyn_out (dynobj, &dyn,
(Elf_External_Dyn *) (newcontents + s->_raw_size));
s->_raw_size = newsize;
s->contents = newcontents;
return true;
}
boolean
elf_link_record_local_dynamic_symbol (info, input_bfd, input_indx)
struct bfd_link_info *info;
bfd *input_bfd;
long input_indx;
{
struct elf_link_local_dynamic_entry *entry;
struct elf_link_hash_table *eht;
struct elf_strtab_hash *dynstr;
Elf_External_Sym esym;
Elf_External_Sym_Shndx eshndx;
Elf_External_Sym_Shndx *shndx;
unsigned long dynstr_index;
char *name;
file_ptr pos;
bfd_size_type amt;
if (! is_elf_hash_table (info))
return false;
for (entry = elf_hash_table (info)->dynlocal; entry ; entry = entry->next)
if (entry->input_bfd == input_bfd && entry->input_indx == input_indx)
return true;
entry = (struct elf_link_local_dynamic_entry *)
bfd_alloc (input_bfd, (bfd_size_type) sizeof (*entry));
if (entry == NULL)
return false;
amt = sizeof (Elf_External_Sym);
pos = elf_tdata (input_bfd)->symtab_hdr.sh_offset + input_indx * amt;
if (bfd_seek (input_bfd, pos, SEEK_SET) != 0
|| bfd_bread ((PTR) &esym, amt, input_bfd) != amt)
return false;
shndx = NULL;
if (elf_tdata (input_bfd)->symtab_shndx_hdr.sh_size != 0)
{
amt = sizeof (Elf_External_Sym_Shndx);
pos = elf_tdata (input_bfd)->symtab_shndx_hdr.sh_offset;
pos += input_indx * amt;
shndx = &eshndx;
if (bfd_seek (input_bfd, pos, SEEK_SET) != 0
|| bfd_bread ((PTR) shndx, amt, input_bfd) != amt)
return false;
}
elf_swap_symbol_in (input_bfd, &esym, shndx, &entry->isym);
name = (bfd_elf_string_from_elf_section
(input_bfd, elf_tdata (input_bfd)->symtab_hdr.sh_link,
entry->isym.st_name));
dynstr = elf_hash_table (info)->dynstr;
if (dynstr == NULL)
{
elf_hash_table (info)->dynstr = dynstr = _bfd_elf_strtab_init ();
if (dynstr == NULL)
return false;
}
dynstr_index = _bfd_elf_strtab_add (dynstr, name, false);
if (dynstr_index == (unsigned long) -1)
return false;
entry->isym.st_name = dynstr_index;
eht = elf_hash_table (info);
entry->next = eht->dynlocal;
eht->dynlocal = entry;
entry->input_bfd = input_bfd;
entry->input_indx = input_indx;
eht->dynsymcount++;
entry->isym.st_info
= ELF_ST_INFO (STB_LOCAL, ELF_ST_TYPE (entry->isym.st_info));
return true;
}
static boolean
elf_link_read_relocs_from_section (abfd, shdr, external_relocs,
internal_relocs)
bfd *abfd;
Elf_Internal_Shdr *shdr;
PTR external_relocs;
Elf_Internal_Rela *internal_relocs;
{
struct elf_backend_data *bed;
bfd_size_type amt;
if (!shdr)
return true;
if (bfd_seek (abfd, shdr->sh_offset, SEEK_SET) != 0)
return false;
if (bfd_bread (external_relocs, shdr->sh_size, abfd) != shdr->sh_size)
return false;
bed = get_elf_backend_data (abfd);
if (shdr->sh_entsize == sizeof (Elf_External_Rel))
{
Elf_External_Rel *erel;
Elf_External_Rel *erelend;
Elf_Internal_Rela *irela;
Elf_Internal_Rel *irel;
erel = (Elf_External_Rel *) external_relocs;
erelend = erel + NUM_SHDR_ENTRIES (shdr);
irela = internal_relocs;
amt = bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rel);
irel = bfd_alloc (abfd, amt);
for (; erel < erelend; erel++, irela += bed->s->int_rels_per_ext_rel)
{
unsigned int i;
if (bed->s->swap_reloc_in)
(*bed->s->swap_reloc_in) (abfd, (bfd_byte *) erel, irel);
else
elf_swap_reloc_in (abfd, erel, irel);
for (i = 0; i < bed->s->int_rels_per_ext_rel; ++i)
{
irela[i].r_offset = irel[i].r_offset;
irela[i].r_info = irel[i].r_info;
irela[i].r_addend = 0;
}
}
}
else
{
Elf_External_Rela *erela;
Elf_External_Rela *erelaend;
Elf_Internal_Rela *irela;
BFD_ASSERT (shdr->sh_entsize == sizeof (Elf_External_Rela));
erela = (Elf_External_Rela *) external_relocs;
erelaend = erela + NUM_SHDR_ENTRIES (shdr);
irela = internal_relocs;
for (; erela < erelaend; erela++, irela += bed->s->int_rels_per_ext_rel)
{
if (bed->s->swap_reloca_in)
(*bed->s->swap_reloca_in) (abfd, (bfd_byte *) erela, irela);
else
elf_swap_reloca_in (abfd, erela, irela);
}
}
return true;
}
Elf_Internal_Rela *
NAME(_bfd_elf,link_read_relocs) (abfd, o, external_relocs, internal_relocs,
keep_memory)
bfd *abfd;
asection *o;
PTR external_relocs;
Elf_Internal_Rela *internal_relocs;
boolean keep_memory;
{
Elf_Internal_Shdr *rel_hdr;
PTR alloc1 = NULL;
Elf_Internal_Rela *alloc2 = NULL;
struct elf_backend_data *bed = get_elf_backend_data (abfd);
if (elf_section_data (o)->relocs != NULL)
return elf_section_data (o)->relocs;
if (o->reloc_count == 0)
return NULL;
rel_hdr = &elf_section_data (o)->rel_hdr;
if (internal_relocs == NULL)
{
bfd_size_type size;
size = o->reloc_count;
size *= bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rela);
if (keep_memory)
internal_relocs = (Elf_Internal_Rela *) bfd_alloc (abfd, size);
else
internal_relocs = alloc2 = (Elf_Internal_Rela *) bfd_malloc (size);
if (internal_relocs == NULL)
goto error_return;
}
if (external_relocs == NULL)
{
bfd_size_type size = rel_hdr->sh_size;
if (elf_section_data (o)->rel_hdr2)
size += elf_section_data (o)->rel_hdr2->sh_size;
alloc1 = (PTR) bfd_malloc (size);
if (alloc1 == NULL)
goto error_return;
external_relocs = alloc1;
}
if (!elf_link_read_relocs_from_section (abfd, rel_hdr,
external_relocs,
internal_relocs))
goto error_return;
if (!elf_link_read_relocs_from_section
(abfd,
elf_section_data (o)->rel_hdr2,
((bfd_byte *) external_relocs) + rel_hdr->sh_size,
internal_relocs + (NUM_SHDR_ENTRIES (rel_hdr)
* bed->s->int_rels_per_ext_rel)))
goto error_return;
if (keep_memory)
elf_section_data (o)->relocs = internal_relocs;
if (alloc1 != NULL)
free (alloc1);
return internal_relocs;
error_return:
if (alloc1 != NULL)
free (alloc1);
if (alloc2 != NULL)
free (alloc2);
return NULL;
}
boolean
NAME(bfd_elf,record_link_assignment) (output_bfd, info, name, provide)
bfd *output_bfd ATTRIBUTE_UNUSED;
struct bfd_link_info *info;
const char *name;
boolean provide;
{
struct elf_link_hash_entry *h;
if (info->hash->creator->flavour != bfd_target_elf_flavour)
return true;
h = elf_link_hash_lookup (elf_hash_table (info), name, true, true, false);
if (h == NULL)
return false;
if (h->root.type == bfd_link_hash_new)
h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
if (provide
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
h->root.type = bfd_link_hash_undefined;
if (!provide
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
h->verinfo.verdef = NULL;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
if (((h->elf_link_hash_flags & (ELF_LINK_HASH_DEF_DYNAMIC
| ELF_LINK_HASH_REF_DYNAMIC)) != 0
|| info->shared)
&& h->dynindx == -1)
{
if (! _bfd_elf_link_record_dynamic_symbol (info, h))
return false;
if (h->weakdef != NULL
&& h->weakdef->dynindx == -1)
{
if (! _bfd_elf_link_record_dynamic_symbol (info, h->weakdef))
return false;
}
}
return true;
}
struct elf_assign_sym_version_info
{
bfd *output_bfd;
struct bfd_link_info *info;
struct bfd_elf_version_tree *verdefs;
boolean failed;
};
struct elf_find_verdep_info
{
bfd *output_bfd;
struct bfd_link_info *info;
unsigned int vers;
boolean failed;
};
static const size_t elf_buckets[] =
{
1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209,
16411, 32771, 0
};
static size_t
compute_bucket_count (info)
struct bfd_link_info *info;
{
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;
amt = dynsymcount;
amt *= sizeof (unsigned long int);
hashcodes = (unsigned long int *) bfd_malloc (amt);
if (hashcodes == NULL)
return 0;
hashcodesp = hashcodes;
elf_link_hash_traverse (elf_hash_table (info),
elf_collect_hash_codes, &hashcodesp);
#ifdef BFD_HOST_U_64_BIT
if (info->optimize == true)
{
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 ;
minsize = nsyms / 4;
if (minsize == 0)
minsize = 1;
best_size = maxsize = nsyms * 2;
amt = maxsize;
amt *= sizeof (unsigned long int);
counts = (unsigned long int *) bfd_malloc (amt);
if (counts == NULL)
{
free (hashcodes);
return 0;
}
for (i = minsize; i < maxsize; ++i)
{
BFD_HOST_U_64_BIT max;
unsigned long int j;
unsigned long int fact;
memset (counts, '\0', i * sizeof (unsigned long int));
for (j = 0; j < nsyms; ++j)
++counts[hashcodes[j] % i];
# ifndef BFD_TARGET_PAGESIZE
# define BFD_TARGET_PAGESIZE (4096)
# endif
max = (2 + nsyms) * (ARCH_SIZE / 8);
# if 1
for (j = 0; j < i; ++j)
max += counts[j] * counts[j];
fact = i / (BFD_TARGET_PAGESIZE / (ARCH_SIZE / 8)) + 1;
max *= fact * fact;
# else
for (j = 0; j < i; ++j)
max += (1 + counts[j]) * (1 + counts[j]);
fact = i / (BFD_TARGET_PAGESIZE / (ARCH_SIZE / 8)) + 1;
max *= fact;
# endif
if (max < best_chlen)
{
best_chlen = max;
best_size = i;
}
}
free (counts);
}
else
#endif
{
for (i = 0; elf_buckets[i] != 0; i++)
{
best_size = elf_buckets[i];
if (dynsymcount < elf_buckets[i + 1])
break;
}
}
free (hashcodes);
return best_size;
}
boolean
NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
filter_shlib,
auxiliary_filters, info, sinterpptr,
verdefs)
bfd *output_bfd;
const char *soname;
const char *rpath;
const char *filter_shlib;
const char * const *auxiliary_filters;
struct bfd_link_info *info;
asection **sinterpptr;
struct bfd_elf_version_tree *verdefs;
{
bfd_size_type soname_indx;
bfd *dynobj;
struct elf_backend_data *bed;
struct elf_assign_sym_version_info asvinfo;
*sinterpptr = NULL;
soname_indx = (bfd_size_type) -1;
if (info->hash->creator->flavour != bfd_target_elf_flavour)
return true;
if (! is_elf_hash_table (info))
return false;
elf_hash_table (info)->init_refcount = -1;
bed = get_elf_backend_data (output_bfd);
if (bed->elf_backend_always_size_sections
&& ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
return false;
dynobj = elf_hash_table (info)->dynobj;
if (dynobj == NULL)
return true;
if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
return false;
if (elf_hash_table (info)->dynamic_sections_created)
{
struct elf_info_failed eif;
struct elf_link_hash_entry *h;
asection *dynstr;
*sinterpptr = bfd_get_section_by_name (dynobj, ".interp");
BFD_ASSERT (*sinterpptr != NULL || info->shared);
if (soname != NULL)
{
soname_indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
soname, true);
if (soname_indx == (bfd_size_type) -1
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_SONAME,
soname_indx))
return false;
}
if (info->symbolic)
{
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_SYMBOLIC,
(bfd_vma) 0))
return false;
info->flags |= DF_SYMBOLIC;
}
if (rpath != NULL)
{
bfd_size_type indx;
indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, rpath,
true);
if (info->new_dtags)
_bfd_elf_strtab_addref (elf_hash_table (info)->dynstr, indx);
if (indx == (bfd_size_type) -1
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_RPATH, indx)
|| (info->new_dtags
&& ! elf_add_dynamic_entry (info, (bfd_vma) DT_RUNPATH,
indx)))
return false;
}
if (filter_shlib != NULL)
{
bfd_size_type indx;
indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
filter_shlib, true);
if (indx == (bfd_size_type) -1
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_FILTER, indx))
return false;
}
if (auxiliary_filters != NULL)
{
const char * const *p;
for (p = auxiliary_filters; *p != NULL; p++)
{
bfd_size_type indx;
indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
*p, true);
if (indx == (bfd_size_type) -1
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_AUXILIARY,
indx))
return false;
}
}
eif.info = info;
eif.verdefs = verdefs;
eif.failed = false;
if (info->export_dynamic)
{
elf_link_hash_traverse (elf_hash_table (info), elf_export_symbol,
(PTR) &eif);
if (eif.failed)
return false;
}
asvinfo.output_bfd = output_bfd;
asvinfo.info = info;
asvinfo.verdefs = verdefs;
asvinfo.failed = false;
elf_link_hash_traverse (elf_hash_table (info),
elf_link_assign_sym_version,
(PTR) &asvinfo);
if (asvinfo.failed)
return false;
elf_link_hash_traverse (elf_hash_table (info),
elf_adjust_dynamic_symbol,
(PTR) &eif);
if (eif.failed)
return false;
h = (info->init_function
? elf_link_hash_lookup (elf_hash_table (info),
info->init_function, false,
false, false)
: NULL);
if (h != NULL
&& (h->elf_link_hash_flags & (ELF_LINK_HASH_REF_REGULAR
| ELF_LINK_HASH_DEF_REGULAR)) != 0)
{
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_INIT, (bfd_vma) 0))
return false;
}
h = (info->fini_function
? elf_link_hash_lookup (elf_hash_table (info),
info->fini_function, false,
false, false)
: NULL);
if (h != NULL
&& (h->elf_link_hash_flags & (ELF_LINK_HASH_REF_REGULAR
| ELF_LINK_HASH_DEF_REGULAR)) != 0)
{
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_FINI, (bfd_vma) 0))
return false;
}
if (bfd_get_section_by_name (output_bfd, ".preinit_array") != NULL)
{
if (info->shared)
{
bfd *sub;
asection *o;
for (sub = info->input_bfds; sub != NULL;
sub = sub->link_next)
for (o = sub->sections; o != NULL; o = o->next)
if (elf_section_data (o)->this_hdr.sh_type
== SHT_PREINIT_ARRAY)
{
(*_bfd_error_handler)
(_("%s: .preinit_array section is not allowed in DSO"),
bfd_archive_filename (sub));
break;
}
bfd_set_error (bfd_error_nonrepresentable_section);
return false;
}
if (!elf_add_dynamic_entry (info, (bfd_vma) DT_PREINIT_ARRAY,
(bfd_vma) 0)
|| !elf_add_dynamic_entry (info, (bfd_vma) DT_PREINIT_ARRAYSZ,
(bfd_vma) 0))
return false;
}
if (bfd_get_section_by_name (output_bfd, ".init_array") != NULL)
{
if (!elf_add_dynamic_entry (info, (bfd_vma) DT_INIT_ARRAY,
(bfd_vma) 0)
|| !elf_add_dynamic_entry (info, (bfd_vma) DT_INIT_ARRAYSZ,
(bfd_vma) 0))
return false;
}
if (bfd_get_section_by_name (output_bfd, ".fini_array") != NULL)
{
if (!elf_add_dynamic_entry (info, (bfd_vma) DT_FINI_ARRAY,
(bfd_vma) 0)
|| !elf_add_dynamic_entry (info, (bfd_vma) DT_FINI_ARRAYSZ,
(bfd_vma) 0))
return false;
}
dynstr = bfd_get_section_by_name (dynobj, ".dynstr");
if (dynstr != NULL && dynstr->output_section != bfd_abs_section_ptr)
{
bfd_size_type strsize;
strsize = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_HASH, (bfd_vma) 0)
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_STRTAB, (bfd_vma) 0)
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_SYMTAB, (bfd_vma) 0)
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_STRSZ, strsize)
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_SYMENT,
(bfd_vma) sizeof (Elf_External_Sym)))
return false;
}
}
if (bed->elf_backend_size_dynamic_sections
&& ! (*bed->elf_backend_size_dynamic_sections) (output_bfd, info))
return false;
if (elf_hash_table (info)->dynamic_sections_created)
{
bfd_size_type dynsymcount;
asection *s;
size_t bucketcount = 0;
size_t hash_entry_size;
unsigned int dtagcount;
s = bfd_get_section_by_name (dynobj, ".gnu.version_d");
BFD_ASSERT (s != NULL);
verdefs = asvinfo.verdefs;
if (verdefs != NULL && verdefs->vernum == 0)
verdefs = verdefs->next;
if (verdefs == NULL)
_bfd_strip_section_from_output (info, s);
else
{
unsigned int cdefs;
bfd_size_type size;
struct bfd_elf_version_tree *t;
bfd_byte *p;
Elf_Internal_Verdef def;
Elf_Internal_Verdaux defaux;
cdefs = 0;
size = 0;
size += sizeof (Elf_External_Verdef);
size += sizeof (Elf_External_Verdaux);
++cdefs;
for (t = verdefs; t != NULL; t = t->next)
{
struct bfd_elf_version_deps *n;
size += sizeof (Elf_External_Verdef);
size += sizeof (Elf_External_Verdaux);
++cdefs;
for (n = t->deps; n != NULL; n = n->next)
size += sizeof (Elf_External_Verdaux);
}
s->_raw_size = size;
s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size);
if (s->contents == NULL && s->_raw_size != 0)
return false;
p = s->contents;
def.vd_version = VER_DEF_CURRENT;
def.vd_flags = VER_FLG_BASE;
def.vd_ndx = 1;
def.vd_cnt = 1;
def.vd_aux = sizeof (Elf_External_Verdef);
def.vd_next = (sizeof (Elf_External_Verdef)
+ sizeof (Elf_External_Verdaux));
if (soname_indx != (bfd_size_type) -1)
{
_bfd_elf_strtab_addref (elf_hash_table (info)->dynstr,
soname_indx);
def.vd_hash = bfd_elf_hash (soname);
defaux.vda_name = soname_indx;
}
else
{
const char *name;
bfd_size_type indx;
name = basename (output_bfd->filename);
def.vd_hash = bfd_elf_hash (name);
indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
name, false);
if (indx == (bfd_size_type) -1)
return false;
defaux.vda_name = indx;
}
defaux.vda_next = 0;
_bfd_elf_swap_verdef_out (output_bfd, &def,
(Elf_External_Verdef *) p);
p += sizeof (Elf_External_Verdef);
_bfd_elf_swap_verdaux_out (output_bfd, &defaux,
(Elf_External_Verdaux *) p);
p += sizeof (Elf_External_Verdaux);
for (t = verdefs; t != NULL; t = t->next)
{
unsigned int cdeps;
struct bfd_elf_version_deps *n;
struct elf_link_hash_entry *h;
cdeps = 0;
for (n = t->deps; n != NULL; n = n->next)
++cdeps;
h = NULL;
if (! (_bfd_generic_link_add_one_symbol
(info, dynobj, t->name, BSF_GLOBAL, bfd_abs_section_ptr,
(bfd_vma) 0, (const char *) NULL, false,
get_elf_backend_data (dynobj)->collect,
(struct bfd_link_hash_entry **) &h)))
return false;
h->elf_link_hash_flags &= ~ ELF_LINK_NON_ELF;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_OBJECT;
h->verinfo.vertree = t;
if (! _bfd_elf_link_record_dynamic_symbol (info, h))
return false;
def.vd_version = VER_DEF_CURRENT;
def.vd_flags = 0;
if (t->globals == NULL && t->locals == NULL && ! t->used)
def.vd_flags |= VER_FLG_WEAK;
def.vd_ndx = t->vernum + 1;
def.vd_cnt = cdeps + 1;
def.vd_hash = bfd_elf_hash (t->name);
def.vd_aux = sizeof (Elf_External_Verdef);
if (t->next != NULL)
def.vd_next = (sizeof (Elf_External_Verdef)
+ (cdeps + 1) * sizeof (Elf_External_Verdaux));
else
def.vd_next = 0;
_bfd_elf_swap_verdef_out (output_bfd, &def,
(Elf_External_Verdef *) p);
p += sizeof (Elf_External_Verdef);
defaux.vda_name = h->dynstr_index;
_bfd_elf_strtab_addref (elf_hash_table (info)->dynstr,
h->dynstr_index);
if (t->deps == NULL)
defaux.vda_next = 0;
else
defaux.vda_next = sizeof (Elf_External_Verdaux);
t->name_indx = defaux.vda_name;
_bfd_elf_swap_verdaux_out (output_bfd, &defaux,
(Elf_External_Verdaux *) p);
p += sizeof (Elf_External_Verdaux);
for (n = t->deps; n != NULL; n = n->next)
{
if (n->version_needed == NULL)
{
defaux.vda_name = 0;
}
else
{
defaux.vda_name = n->version_needed->name_indx;
_bfd_elf_strtab_addref (elf_hash_table (info)->dynstr,
defaux.vda_name);
}
if (n->next == NULL)
defaux.vda_next = 0;
else
defaux.vda_next = sizeof (Elf_External_Verdaux);
_bfd_elf_swap_verdaux_out (output_bfd, &defaux,
(Elf_External_Verdaux *) p);
p += sizeof (Elf_External_Verdaux);
}
}
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_VERDEF, (bfd_vma) 0)
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_VERDEFNUM,
(bfd_vma) cdefs))
return false;
elf_tdata (output_bfd)->cverdefs = cdefs;
}
if (info->new_dtags && info->flags)
{
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_FLAGS, info->flags))
return false;
}
if (info->flags_1)
{
if (! info->shared)
info->flags_1 &= ~ (DF_1_INITFIRST
| DF_1_NODELETE
| DF_1_NOOPEN);
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_FLAGS_1,
info->flags_1))
return false;
}
s = bfd_get_section_by_name (dynobj, ".gnu.version_r");
BFD_ASSERT (s != NULL);
{
struct elf_find_verdep_info sinfo;
sinfo.output_bfd = output_bfd;
sinfo.info = info;
sinfo.vers = elf_tdata (output_bfd)->cverdefs;
if (sinfo.vers == 0)
sinfo.vers = 1;
sinfo.failed = false;
elf_link_hash_traverse (elf_hash_table (info),
elf_link_find_version_dependencies,
(PTR) &sinfo);
if (elf_tdata (output_bfd)->verref == NULL)
_bfd_strip_section_from_output (info, s);
else
{
Elf_Internal_Verneed *t;
unsigned int size;
unsigned int crefs;
bfd_byte *p;
size = 0;
crefs = 0;
for (t = elf_tdata (output_bfd)->verref;
t != NULL;
t = t->vn_nextref)
{
Elf_Internal_Vernaux *a;
size += sizeof (Elf_External_Verneed);
++crefs;
for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
size += sizeof (Elf_External_Vernaux);
}
s->_raw_size = size;
s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size);
if (s->contents == NULL)
return false;
p = s->contents;
for (t = elf_tdata (output_bfd)->verref;
t != NULL;
t = t->vn_nextref)
{
unsigned int caux;
Elf_Internal_Vernaux *a;
bfd_size_type indx;
caux = 0;
for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
++caux;
t->vn_version = VER_NEED_CURRENT;
t->vn_cnt = caux;
indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
elf_dt_name (t->vn_bfd) != NULL
? elf_dt_name (t->vn_bfd)
: basename (t->vn_bfd->filename),
false);
if (indx == (bfd_size_type) -1)
return false;
t->vn_file = indx;
t->vn_aux = sizeof (Elf_External_Verneed);
if (t->vn_nextref == NULL)
t->vn_next = 0;
else
t->vn_next = (sizeof (Elf_External_Verneed)
+ caux * sizeof (Elf_External_Vernaux));
_bfd_elf_swap_verneed_out (output_bfd, t,
(Elf_External_Verneed *) p);
p += sizeof (Elf_External_Verneed);
for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
{
a->vna_hash = bfd_elf_hash (a->vna_nodename);
indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
a->vna_nodename, false);
if (indx == (bfd_size_type) -1)
return false;
a->vna_name = indx;
if (a->vna_nextptr == NULL)
a->vna_next = 0;
else
a->vna_next = sizeof (Elf_External_Vernaux);
_bfd_elf_swap_vernaux_out (output_bfd, a,
(Elf_External_Vernaux *) p);
p += sizeof (Elf_External_Vernaux);
}
}
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_VERNEED,
(bfd_vma) 0)
|| ! elf_add_dynamic_entry (info, (bfd_vma) DT_VERNEEDNUM,
(bfd_vma) crefs))
return false;
elf_tdata (output_bfd)->cverrefs = crefs;
}
}
dynsymcount = _bfd_elf_link_renumber_dynsyms (output_bfd, info);
s = bfd_get_section_by_name (dynobj, ".gnu.version");
BFD_ASSERT (s != NULL);
if (dynsymcount == 0
|| (verdefs == NULL && elf_tdata (output_bfd)->verref == NULL))
{
_bfd_strip_section_from_output (info, s);
dynsymcount = _bfd_elf_link_renumber_dynsyms (output_bfd, info);
}
else
{
s->_raw_size = dynsymcount * sizeof (Elf_External_Versym);
s->contents = (bfd_byte *) bfd_zalloc (output_bfd, s->_raw_size);
if (s->contents == NULL)
return false;
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_VERSYM, (bfd_vma) 0))
return false;
}
s = bfd_get_section_by_name (dynobj, ".dynsym");
BFD_ASSERT (s != NULL);
s->_raw_size = dynsymcount * sizeof (Elf_External_Sym);
s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size);
if (s->contents == NULL && s->_raw_size != 0)
return false;
if (dynsymcount != 0)
{
Elf_Internal_Sym isym;
isym.st_value = 0;
isym.st_size = 0;
isym.st_name = 0;
isym.st_info = 0;
isym.st_other = 0;
isym.st_shndx = 0;
elf_swap_symbol_out (output_bfd, &isym, (PTR) s->contents, (PTR) 0);
}
bucketcount = compute_bucket_count (info);
s = bfd_get_section_by_name (dynobj, ".hash");
BFD_ASSERT (s != NULL);
hash_entry_size = elf_section_data (s)->this_hdr.sh_entsize;
s->_raw_size = ((2 + bucketcount + dynsymcount) * hash_entry_size);
s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size);
if (s->contents == NULL)
return false;
memset (s->contents, 0, (size_t) s->_raw_size);
bfd_put (8 * hash_entry_size, output_bfd, (bfd_vma) bucketcount,
s->contents);
bfd_put (8 * hash_entry_size, output_bfd, (bfd_vma) dynsymcount,
s->contents + hash_entry_size);
elf_hash_table (info)->bucketcount = bucketcount;
s = bfd_get_section_by_name (dynobj, ".dynstr");
BFD_ASSERT (s != NULL);
elf_finalize_dynstr (output_bfd, info);
s->_raw_size = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
for (dtagcount = 0; dtagcount <= info->spare_dynamic_tags; ++dtagcount)
if (! elf_add_dynamic_entry (info, (bfd_vma) DT_NULL, (bfd_vma) 0))
return false;
}
return true;
}
static boolean elf_adjust_dynstr_offsets
PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean
elf_adjust_dynstr_offsets (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
struct elf_strtab_hash *dynstr = (struct elf_strtab_hash *) data;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->dynindx != -1)
h->dynstr_index = _bfd_elf_strtab_offset (dynstr, h->dynstr_index);
return true;
}
static boolean
elf_finalize_dynstr (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
struct elf_link_local_dynamic_entry *entry;
struct elf_strtab_hash *dynstr = elf_hash_table (info)->dynstr;
bfd *dynobj = elf_hash_table (info)->dynobj;
asection *sdyn;
bfd_size_type size;
Elf_External_Dyn *dyncon, *dynconend;
_bfd_elf_strtab_finalize (dynstr);
size = _bfd_elf_strtab_size (dynstr);
sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
BFD_ASSERT (sdyn != NULL);
dyncon = (Elf_External_Dyn *) sdyn->contents;
dynconend = (Elf_External_Dyn *) (sdyn->contents +
sdyn->_raw_size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
elf_swap_dyn_in (dynobj, dyncon, & dyn);
switch (dyn.d_tag)
{
case DT_STRSZ:
dyn.d_un.d_val = size;
elf_swap_dyn_out (dynobj, & dyn, dyncon);
break;
case DT_NEEDED:
case DT_SONAME:
case DT_RPATH:
case DT_RUNPATH:
case DT_FILTER:
case DT_AUXILIARY:
dyn.d_un.d_val = _bfd_elf_strtab_offset (dynstr, dyn.d_un.d_val);
elf_swap_dyn_out (dynobj, & dyn, dyncon);
break;
default:
break;
}
}
for (entry = elf_hash_table (info)->dynlocal; entry ; entry = entry->next)
entry->isym.st_name = _bfd_elf_strtab_offset (dynstr,
entry->isym.st_name);
elf_link_hash_traverse (elf_hash_table (info),
elf_adjust_dynstr_offsets, dynstr);
if (elf_tdata (output_bfd)->cverdefs)
{
asection *s;
bfd_byte *p;
bfd_size_type i;
Elf_Internal_Verdef def;
Elf_Internal_Verdaux defaux;
s = bfd_get_section_by_name (dynobj, ".gnu.version_d");
p = (bfd_byte *) s->contents;
do
{
_bfd_elf_swap_verdef_in (output_bfd, (Elf_External_Verdef *) p,
&def);
p += sizeof (Elf_External_Verdef);
for (i = 0; i < def.vd_cnt; ++i)
{
_bfd_elf_swap_verdaux_in (output_bfd,
(Elf_External_Verdaux *) p, &defaux);
defaux.vda_name = _bfd_elf_strtab_offset (dynstr,
defaux.vda_name);
_bfd_elf_swap_verdaux_out (output_bfd,
&defaux, (Elf_External_Verdaux *) p);
p += sizeof (Elf_External_Verdaux);
}
}
while (def.vd_next);
}
if (elf_tdata (output_bfd)->verref)
{
asection *s;
bfd_byte *p;
bfd_size_type i;
Elf_Internal_Verneed need;
Elf_Internal_Vernaux needaux;
s = bfd_get_section_by_name (dynobj, ".gnu.version_r");
p = (bfd_byte *) s->contents;
do
{
_bfd_elf_swap_verneed_in (output_bfd, (Elf_External_Verneed *) p,
&need);
need.vn_file = _bfd_elf_strtab_offset (dynstr, need.vn_file);
_bfd_elf_swap_verneed_out (output_bfd, &need,
(Elf_External_Verneed *) p);
p += sizeof (Elf_External_Verneed);
for (i = 0; i < need.vn_cnt; ++i)
{
_bfd_elf_swap_vernaux_in (output_bfd,
(Elf_External_Vernaux *) p, &needaux);
needaux.vna_name = _bfd_elf_strtab_offset (dynstr,
needaux.vna_name);
_bfd_elf_swap_vernaux_out (output_bfd,
&needaux,
(Elf_External_Vernaux *) p);
p += sizeof (Elf_External_Vernaux);
}
}
while (need.vn_next);
}
return true;
}
static boolean
elf_fix_symbol_flags (h, eif)
struct elf_link_hash_entry *h;
struct elf_info_failed *eif;
{
if ((h->elf_link_hash_flags & ELF_LINK_NON_ELF) != 0)
{
while (h->root.type == bfd_link_hash_indirect)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->root.type != bfd_link_hash_defined
&& h->root.type != bfd_link_hash_defweak)
h->elf_link_hash_flags |= (ELF_LINK_HASH_REF_REGULAR
| ELF_LINK_HASH_REF_REGULAR_NONWEAK);
else
{
if (h->root.u.def.section->owner != NULL
&& (bfd_get_flavour (h->root.u.def.section->owner)
== bfd_target_elf_flavour))
h->elf_link_hash_flags |= (ELF_LINK_HASH_REF_REGULAR
| ELF_LINK_HASH_REF_REGULAR_NONWEAK);
else
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
}
if (h->dynindx == -1
&& ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
|| (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0))
{
if (! _bfd_elf_link_record_dynamic_symbol (eif->info, h))
{
eif->failed = true;
return false;
}
}
}
else
{
if ((h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
&& (h->root.u.def.section->owner != NULL
? (bfd_get_flavour (h->root.u.def.section->owner)
!= bfd_target_elf_flavour)
: (bfd_is_abs_section (h->root.u.def.section)
&& (h->elf_link_hash_flags
& ELF_LINK_HASH_DEF_DYNAMIC) == 0)))
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
}
if (h->root.type == bfd_link_hash_defined
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
&& (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) != 0
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
&& (h->root.u.def.section->owner->flags & DYNAMIC) == 0)
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0
&& eif->info->shared
&& is_elf_hash_table (eif->info)
&& (eif->info->symbolic
|| ELF_ST_VISIBILITY (h->other) == STV_INTERNAL
|| ELF_ST_VISIBILITY (h->other) == STV_HIDDEN)
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0)
{
struct elf_backend_data *bed;
boolean force_local;
bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
force_local = (ELF_ST_VISIBILITY (h->other) == STV_INTERNAL
|| ELF_ST_VISIBILITY (h->other) == STV_HIDDEN);
(*bed->elf_backend_hide_symbol) (eif->info, h, force_local);
}
if (h->weakdef != NULL)
{
struct elf_link_hash_entry *weakdef;
BFD_ASSERT (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak);
weakdef = h->weakdef;
BFD_ASSERT (weakdef->root.type == bfd_link_hash_defined
|| weakdef->root.type == bfd_link_hash_defweak);
BFD_ASSERT (weakdef->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC);
if ((weakdef->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0)
h->weakdef = NULL;
else
{
struct elf_backend_data *bed;
bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
(*bed->elf_backend_copy_indirect_symbol) (weakdef, h);
}
}
return true;
}
static boolean
elf_adjust_dynamic_symbol (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
struct elf_info_failed *eif = (struct elf_info_failed *) data;
bfd *dynobj;
struct elf_backend_data *bed;
if (h->root.type == bfd_link_hash_warning)
{
h->plt.offset = (bfd_vma) -1;
h->got.offset = (bfd_vma) -1;
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
if (h->root.type == bfd_link_hash_indirect)
return true;
if (! is_elf_hash_table (eif->info))
return false;
if (! elf_fix_symbol_flags (h, eif))
return false;
if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) == 0
&& ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0
|| (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
|| ((h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0
&& (h->weakdef == NULL || h->weakdef->dynindx == -1))))
{
h->plt.offset = (bfd_vma) -1;
return true;
}
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DYNAMIC_ADJUSTED) != 0)
return true;
h->elf_link_hash_flags |= ELF_LINK_HASH_DYNAMIC_ADJUSTED;
if (h->weakdef != NULL)
{
h->weakdef->elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR;
if (! elf_adjust_dynamic_symbol (h->weakdef, (PTR) eif))
return false;
}
if (h->size == 0
&& h->type == STT_NOTYPE
&& (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) == 0)
(*_bfd_error_handler)
(_("warning: type and size of dynamic symbol `%s' are not defined"),
h->root.root.string);
dynobj = elf_hash_table (eif->info)->dynobj;
bed = get_elf_backend_data (dynobj);
if (! (*bed->elf_backend_adjust_dynamic_symbol) (eif->info, h))
{
eif->failed = true;
return false;
}
return true;
}
static boolean
elf_export_symbol (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
struct elf_info_failed *eif = (struct elf_info_failed *) data;
if (h->root.type == bfd_link_hash_indirect)
return true;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->dynindx == -1
&& (h->elf_link_hash_flags
& (ELF_LINK_HASH_DEF_REGULAR | ELF_LINK_HASH_REF_REGULAR)) != 0)
{
struct bfd_elf_version_tree *t;
struct bfd_elf_version_expr *d;
for (t = eif->verdefs; t != NULL; t = t->next)
{
if (t->globals != NULL)
{
for (d = t->globals; d != NULL; d = d->next)
{
if ((*d->match) (d, h->root.root.string))
goto doit;
}
}
if (t->locals != NULL)
{
for (d = t->locals ; d != NULL; d = d->next)
{
if ((*d->match) (d, h->root.root.string))
return true;
}
}
}
if (!eif->verdefs)
{
doit:
if (! _bfd_elf_link_record_dynamic_symbol (eif->info, h))
{
eif->failed = true;
return false;
}
}
}
return true;
}
static boolean
elf_link_find_version_dependencies (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
struct elf_find_verdep_info *rinfo = (struct elf_find_verdep_info *) data;
Elf_Internal_Verneed *t;
Elf_Internal_Vernaux *a;
bfd_size_type amt;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
|| (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0
|| h->dynindx == -1
|| h->verinfo.verdef == NULL)
return true;
for (t = elf_tdata (rinfo->output_bfd)->verref; t != NULL; t = t->vn_nextref)
{
if (t->vn_bfd != h->verinfo.verdef->vd_bfd)
continue;
for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
if (a->vna_nodename == h->verinfo.verdef->vd_nodename)
return true;
break;
}
if (t == NULL)
{
amt = sizeof *t;
t = (Elf_Internal_Verneed *) bfd_zalloc (rinfo->output_bfd, amt);
if (t == NULL)
{
rinfo->failed = true;
return false;
}
t->vn_bfd = h->verinfo.verdef->vd_bfd;
t->vn_nextref = elf_tdata (rinfo->output_bfd)->verref;
elf_tdata (rinfo->output_bfd)->verref = t;
}
amt = sizeof *a;
a = (Elf_Internal_Vernaux *) bfd_zalloc (rinfo->output_bfd, amt);
a->vna_nodename = h->verinfo.verdef->vd_nodename;
a->vna_flags = h->verinfo.verdef->vd_flags;
a->vna_nextptr = t->vn_auxptr;
h->verinfo.verdef->vd_exp_refno = rinfo->vers;
++rinfo->vers;
a->vna_other = h->verinfo.verdef->vd_exp_refno + 1;
t->vn_auxptr = a;
return true;
}
static boolean
elf_link_assign_sym_version (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
struct elf_assign_sym_version_info *sinfo;
struct bfd_link_info *info;
struct elf_backend_data *bed;
struct elf_info_failed eif;
char *p;
bfd_size_type amt;
sinfo = (struct elf_assign_sym_version_info *) data;
info = sinfo->info;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
eif.failed = false;
eif.info = info;
if (! elf_fix_symbol_flags (h, &eif))
{
if (eif.failed)
sinfo->failed = true;
return false;
}
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
return true;
bed = get_elf_backend_data (sinfo->output_bfd);
p = strchr (h->root.root.string, ELF_VER_CHR);
if (p != NULL && h->verinfo.vertree == NULL)
{
struct bfd_elf_version_tree *t;
boolean hidden;
hidden = true;
++p;
if (*p == ELF_VER_CHR)
{
hidden = false;
++p;
}
if (*p == '\0')
{
if (hidden)
h->elf_link_hash_flags |= ELF_LINK_HIDDEN;
return true;
}
for (t = sinfo->verdefs; t != NULL; t = t->next)
{
if (strcmp (t->name, p) == 0)
{
size_t len;
char *alc;
struct bfd_elf_version_expr *d;
len = p - h->root.root.string;
alc = bfd_malloc ((bfd_size_type) len);
if (alc == NULL)
return false;
strncpy (alc, h->root.root.string, len - 1);
alc[len - 1] = '\0';
if (alc[len - 2] == ELF_VER_CHR)
alc[len - 2] = '\0';
h->verinfo.vertree = t;
t->used = true;
d = NULL;
if (t->globals != NULL)
{
for (d = t->globals; d != NULL; d = d->next)
if ((*d->match) (d, alc))
break;
}
if (d == NULL && t->locals != NULL)
{
for (d = t->locals; d != NULL; d = d->next)
{
if ((*d->match) (d, alc))
{
if (h->dynindx != -1
&& info->shared
&& ! info->export_dynamic)
{
(*bed->elf_backend_hide_symbol) (info, h, true);
}
break;
}
}
}
free (alc);
break;
}
}
if (t == NULL && ! info->shared)
{
struct bfd_elf_version_tree **pp;
int version_index;
if (h->dynindx == -1)
return true;
amt = sizeof *t;
t = ((struct bfd_elf_version_tree *)
bfd_alloc (sinfo->output_bfd, amt));
if (t == NULL)
{
sinfo->failed = true;
return false;
}
t->next = NULL;
t->name = p;
t->globals = NULL;
t->locals = NULL;
t->deps = NULL;
t->name_indx = (unsigned int) -1;
t->used = true;
version_index = 1;
if (sinfo->verdefs != NULL && sinfo->verdefs->vernum == 0)
version_index = 0;
for (pp = &sinfo->verdefs; *pp != NULL; pp = &(*pp)->next)
++version_index;
t->vernum = version_index;
*pp = t;
h->verinfo.vertree = t;
}
else if (t == NULL)
{
(*_bfd_error_handler)
(_("%s: undefined versioned symbol name %s"),
bfd_get_filename (sinfo->output_bfd), h->root.root.string);
bfd_set_error (bfd_error_bad_value);
sinfo->failed = true;
return false;
}
if (hidden)
h->elf_link_hash_flags |= ELF_LINK_HIDDEN;
}
if (h->verinfo.vertree == NULL && sinfo->verdefs != NULL)
{
struct bfd_elf_version_tree *t;
struct bfd_elf_version_tree *deflt;
struct bfd_elf_version_expr *d;
deflt = NULL;
for (t = sinfo->verdefs; t != NULL; t = t->next)
{
if (t->globals != NULL)
{
for (d = t->globals; d != NULL; d = d->next)
{
if ((*d->match) (d, h->root.root.string))
{
h->verinfo.vertree = t;
break;
}
}
if (d != NULL)
break;
}
if (t->locals != NULL)
{
for (d = t->locals; d != NULL; d = d->next)
{
if (d->pattern[0] == '*' && d->pattern[1] == '\0')
deflt = t;
else if ((*d->match) (d, h->root.root.string))
{
h->verinfo.vertree = t;
if (h->dynindx != -1
&& info->shared
&& ! info->export_dynamic)
{
(*bed->elf_backend_hide_symbol) (info, h, true);
}
break;
}
}
if (d != NULL)
break;
}
}
if (deflt != NULL && h->verinfo.vertree == NULL)
{
h->verinfo.vertree = deflt;
if (h->dynindx != -1
&& info->shared
&& ! info->export_dynamic)
{
(*bed->elf_backend_hide_symbol) (info, h, true);
}
}
}
return true;
}
struct elf_final_link_info
{
struct bfd_link_info *info;
bfd *output_bfd;
struct bfd_strtab_hash *symstrtab;
asection *dynsym_sec;
asection *hash_sec;
asection *symver_sec;
bfd_byte *contents;
PTR external_relocs;
Elf_Internal_Rela *internal_relocs;
Elf_External_Sym *external_syms;
Elf_External_Sym_Shndx *locsym_shndx;
Elf_Internal_Sym *internal_syms;
long *indices;
asection **sections;
Elf_External_Sym *symbuf;
Elf_External_Sym_Shndx *symshndxbuf;
size_t symbuf_count;
size_t symbuf_size;
};
static boolean elf_link_output_sym
PARAMS ((struct elf_final_link_info *, const char *,
Elf_Internal_Sym *, asection *));
static boolean elf_link_flush_output_syms
PARAMS ((struct elf_final_link_info *));
static boolean elf_link_output_extsym
PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean elf_link_sec_merge_syms
PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean elf_link_input_bfd
PARAMS ((struct elf_final_link_info *, bfd *));
static boolean elf_reloc_link_order
PARAMS ((bfd *, struct bfd_link_info *, asection *,
struct bfd_link_order *));
struct elf_outext_info
{
boolean failed;
boolean localsyms;
struct elf_final_link_info *finfo;
};
static boolean
elf_link_size_reloc_section (abfd, rel_hdr, o)
bfd *abfd;
Elf_Internal_Shdr *rel_hdr;
asection *o;
{
bfd_size_type reloc_count;
bfd_size_type num_rel_hashes;
if (rel_hdr == &elf_section_data (o)->rel_hdr)
reloc_count = elf_section_data (o)->rel_count;
else
reloc_count = elf_section_data (o)->rel_count2;
num_rel_hashes = o->reloc_count;
if (num_rel_hashes < reloc_count)
num_rel_hashes = reloc_count;
rel_hdr->sh_size = rel_hdr->sh_entsize * reloc_count;
rel_hdr->contents = (PTR) bfd_zalloc (abfd, rel_hdr->sh_size);
if (rel_hdr->contents == NULL && rel_hdr->sh_size != 0)
return false;
if (elf_section_data (o)->rel_hashes == NULL
&& num_rel_hashes)
{
struct elf_link_hash_entry **p;
p = ((struct elf_link_hash_entry **)
bfd_zmalloc (num_rel_hashes
* sizeof (struct elf_link_hash_entry *)));
if (p == NULL)
return false;
elf_section_data (o)->rel_hashes = p;
}
return true;
}
static void
elf_link_adjust_relocs (abfd, rel_hdr, count, rel_hash)
bfd *abfd;
Elf_Internal_Shdr *rel_hdr;
unsigned int count;
struct elf_link_hash_entry **rel_hash;
{
unsigned int i;
struct elf_backend_data *bed = get_elf_backend_data (abfd);
Elf_Internal_Rel *irel;
Elf_Internal_Rela *irela;
bfd_size_type amt = sizeof (Elf_Internal_Rel) * bed->s->int_rels_per_ext_rel;
irel = (Elf_Internal_Rel *) bfd_zmalloc (amt);
if (irel == NULL)
{
(*_bfd_error_handler) (_("Error: out of memory"));
abort ();
}
amt = sizeof (Elf_Internal_Rela) * bed->s->int_rels_per_ext_rel;
irela = (Elf_Internal_Rela *) bfd_zmalloc (amt);
if (irela == NULL)
{
(*_bfd_error_handler) (_("Error: out of memory"));
abort ();
}
for (i = 0; i < count; i++, rel_hash++)
{
if (*rel_hash == NULL)
continue;
BFD_ASSERT ((*rel_hash)->indx >= 0);
if (rel_hdr->sh_entsize == sizeof (Elf_External_Rel))
{
Elf_External_Rel *erel;
unsigned int j;
erel = (Elf_External_Rel *) rel_hdr->contents + i;
if (bed->s->swap_reloc_in)
(*bed->s->swap_reloc_in) (abfd, (bfd_byte *) erel, irel);
else
elf_swap_reloc_in (abfd, erel, irel);
for (j = 0; j < bed->s->int_rels_per_ext_rel; j++)
irel[j].r_info = ELF_R_INFO ((*rel_hash)->indx,
ELF_R_TYPE (irel[j].r_info));
if (bed->s->swap_reloc_out)
(*bed->s->swap_reloc_out) (abfd, irel, (bfd_byte *) erel);
else
elf_swap_reloc_out (abfd, irel, erel);
}
else
{
Elf_External_Rela *erela;
unsigned int j;
BFD_ASSERT (rel_hdr->sh_entsize
== sizeof (Elf_External_Rela));
erela = (Elf_External_Rela *) rel_hdr->contents + i;
if (bed->s->swap_reloca_in)
(*bed->s->swap_reloca_in) (abfd, (bfd_byte *) erela, irela);
else
elf_swap_reloca_in (abfd, erela, irela);
for (j = 0; j < bed->s->int_rels_per_ext_rel; j++)
irela[j].r_info = ELF_R_INFO ((*rel_hash)->indx,
ELF_R_TYPE (irela[j].r_info));
if (bed->s->swap_reloca_out)
(*bed->s->swap_reloca_out) (abfd, irela, (bfd_byte *) erela);
else
elf_swap_reloca_out (abfd, irela, erela);
}
}
free (irel);
free (irela);
}
struct elf_link_sort_rela {
bfd_vma offset;
enum elf_reloc_type_class type;
union {
Elf_Internal_Rel rel;
Elf_Internal_Rela rela;
} u;
};
static int
elf_link_sort_cmp1 (A, B)
const PTR A;
const PTR B;
{
struct elf_link_sort_rela *a = (struct elf_link_sort_rela *) A;
struct elf_link_sort_rela *b = (struct elf_link_sort_rela *) B;
int relativea, relativeb;
relativea = a->type == reloc_class_relative;
relativeb = b->type == reloc_class_relative;
if (relativea < relativeb)
return 1;
if (relativea > relativeb)
return -1;
if (ELF_R_SYM (a->u.rel.r_info) < ELF_R_SYM (b->u.rel.r_info))
return -1;
if (ELF_R_SYM (a->u.rel.r_info) > ELF_R_SYM (b->u.rel.r_info))
return 1;
if (a->u.rel.r_offset < b->u.rel.r_offset)
return -1;
if (a->u.rel.r_offset > b->u.rel.r_offset)
return 1;
return 0;
}
static int
elf_link_sort_cmp2 (A, B)
const PTR A;
const PTR B;
{
struct elf_link_sort_rela *a = (struct elf_link_sort_rela *) A;
struct elf_link_sort_rela *b = (struct elf_link_sort_rela *) B;
int copya, copyb;
if (a->offset < b->offset)
return -1;
if (a->offset > b->offset)
return 1;
copya = (a->type == reloc_class_copy) * 2 + (a->type == reloc_class_plt);
copyb = (b->type == reloc_class_copy) * 2 + (b->type == reloc_class_plt);
if (copya < copyb)
return -1;
if (copya > copyb)
return 1;
if (a->u.rel.r_offset < b->u.rel.r_offset)
return -1;
if (a->u.rel.r_offset > b->u.rel.r_offset)
return 1;
return 0;
}
static size_t
elf_link_sort_relocs (abfd, info, psec)
bfd *abfd;
struct bfd_link_info *info;
asection **psec;
{
bfd *dynobj = elf_hash_table (info)->dynobj;
asection *reldyn, *o;
boolean rel = false;
bfd_size_type count, size;
size_t i, j, ret;
struct elf_link_sort_rela *rela;
struct elf_backend_data *bed = get_elf_backend_data (abfd);
reldyn = bfd_get_section_by_name (abfd, ".rela.dyn");
if (reldyn == NULL || reldyn->_raw_size == 0)
{
reldyn = bfd_get_section_by_name (abfd, ".rel.dyn");
if (reldyn == NULL || reldyn->_raw_size == 0)
return 0;
rel = true;
count = reldyn->_raw_size / sizeof (Elf_External_Rel);
}
else
count = reldyn->_raw_size / sizeof (Elf_External_Rela);
size = 0;
for (o = dynobj->sections; o != NULL; o = o->next)
if ((o->flags & (SEC_HAS_CONTENTS|SEC_LINKER_CREATED))
== (SEC_HAS_CONTENTS|SEC_LINKER_CREATED)
&& o->output_section == reldyn)
size += o->_raw_size;
if (size != reldyn->_raw_size)
return 0;
rela = (struct elf_link_sort_rela *) bfd_zmalloc (sizeof (*rela) * count);
if (rela == NULL)
{
(*info->callbacks->warning)
(info, _("Not enough memory to sort relocations"), 0, abfd, 0,
(bfd_vma) 0);
return 0;
}
for (o = dynobj->sections; o != NULL; o = o->next)
if ((o->flags & (SEC_HAS_CONTENTS|SEC_LINKER_CREATED))
== (SEC_HAS_CONTENTS|SEC_LINKER_CREATED)
&& o->output_section == reldyn)
{
if (rel)
{
Elf_External_Rel *erel, *erelend;
struct elf_link_sort_rela *s;
erel = (Elf_External_Rel *) o->contents;
erelend = (Elf_External_Rel *) (o->contents + o->_raw_size);
s = rela + o->output_offset / sizeof (Elf_External_Rel);
for (; erel < erelend; erel++, s++)
{
if (bed->s->swap_reloc_in)
(*bed->s->swap_reloc_in) (abfd, (bfd_byte *) erel, &s->u.rel);
else
elf_swap_reloc_in (abfd, erel, &s->u.rel);
s->type = (*bed->elf_backend_reloc_type_class) (&s->u.rela);
}
}
else
{
Elf_External_Rela *erela, *erelaend;
struct elf_link_sort_rela *s;
erela = (Elf_External_Rela *) o->contents;
erelaend = (Elf_External_Rela *) (o->contents + o->_raw_size);
s = rela + o->output_offset / sizeof (Elf_External_Rela);
for (; erela < erelaend; erela++, s++)
{
if (bed->s->swap_reloca_in)
(*bed->s->swap_reloca_in) (dynobj, (bfd_byte *) erela,
&s->u.rela);
else
elf_swap_reloca_in (dynobj, erela, &s->u.rela);
s->type = (*bed->elf_backend_reloc_type_class) (&s->u.rela);
}
}
}
qsort (rela, (size_t) count, sizeof (*rela), elf_link_sort_cmp1);
for (ret = 0; ret < count && rela[ret].type == reloc_class_relative; ret++)
;
for (i = ret, j = ret; i < count; i++)
{
if (ELF_R_SYM (rela[i].u.rel.r_info) != ELF_R_SYM (rela[j].u.rel.r_info))
j = i;
rela[i].offset = rela[j].u.rel.r_offset;
}
qsort (rela + ret, (size_t) count - ret, sizeof (*rela), elf_link_sort_cmp2);
for (o = dynobj->sections; o != NULL; o = o->next)
if ((o->flags & (SEC_HAS_CONTENTS|SEC_LINKER_CREATED))
== (SEC_HAS_CONTENTS|SEC_LINKER_CREATED)
&& o->output_section == reldyn)
{
if (rel)
{
Elf_External_Rel *erel, *erelend;
struct elf_link_sort_rela *s;
erel = (Elf_External_Rel *) o->contents;
erelend = (Elf_External_Rel *) (o->contents + o->_raw_size);
s = rela + o->output_offset / sizeof (Elf_External_Rel);
for (; erel < erelend; erel++, s++)
{
if (bed->s->swap_reloc_out)
(*bed->s->swap_reloc_out) (abfd, &s->u.rel,
(bfd_byte *) erel);
else
elf_swap_reloc_out (abfd, &s->u.rel, erel);
}
}
else
{
Elf_External_Rela *erela, *erelaend;
struct elf_link_sort_rela *s;
erela = (Elf_External_Rela *) o->contents;
erelaend = (Elf_External_Rela *) (o->contents + o->_raw_size);
s = rela + o->output_offset / sizeof (Elf_External_Rela);
for (; erela < erelaend; erela++, s++)
{
if (bed->s->swap_reloca_out)
(*bed->s->swap_reloca_out) (dynobj, &s->u.rela,
(bfd_byte *) erela);
else
elf_swap_reloca_out (dynobj, &s->u.rela, erela);
}
}
}
free (rela);
*psec = reldyn;
return ret;
}
boolean
elf_bfd_final_link (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
boolean dynamic;
boolean emit_relocs;
bfd *dynobj;
struct elf_final_link_info finfo;
register asection *o;
register struct bfd_link_order *p;
register bfd *sub;
bfd_size_type max_contents_size;
bfd_size_type max_external_reloc_size;
bfd_size_type max_internal_reloc_count;
bfd_size_type max_sym_count;
bfd_size_type max_sym_shndx_count;
file_ptr off;
Elf_Internal_Sym elfsym;
unsigned int i;
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Shdr *symstrtab_hdr;
struct elf_backend_data *bed = get_elf_backend_data (abfd);
struct elf_outext_info eoinfo;
boolean merged;
size_t relativecount = 0;
asection *reldyn = 0;
bfd_size_type amt;
if (! is_elf_hash_table (info))
return false;
if (info->shared)
abfd->flags |= DYNAMIC;
dynamic = elf_hash_table (info)->dynamic_sections_created;
dynobj = elf_hash_table (info)->dynobj;
emit_relocs = (info->relocateable
|| info->emitrelocations
|| bed->elf_backend_emit_relocs);
finfo.info = info;
finfo.output_bfd = abfd;
finfo.symstrtab = elf_stringtab_init ();
if (finfo.symstrtab == NULL)
return false;
if (! dynamic)
{
finfo.dynsym_sec = NULL;
finfo.hash_sec = NULL;
finfo.symver_sec = NULL;
}
else
{
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);
finfo.symver_sec = bfd_get_section_by_name (dynobj, ".gnu.version");
}
finfo.contents = NULL;
finfo.external_relocs = NULL;
finfo.internal_relocs = NULL;
finfo.external_syms = NULL;
finfo.locsym_shndx = NULL;
finfo.internal_syms = NULL;
finfo.indices = NULL;
finfo.sections = NULL;
finfo.symbuf = NULL;
finfo.symshndxbuf = NULL;
finfo.symbuf_count = 0;
max_contents_size = 0;
max_external_reloc_size = 0;
max_internal_reloc_count = 0;
max_sym_count = 0;
max_sym_shndx_count = 0;
merged = false;
for (o = abfd->sections; o != (asection *) NULL; o = o->next)
{
o->reloc_count = 0;
for (p = o->link_order_head; p != NULL; p = p->next)
{
if (p->type == bfd_section_reloc_link_order
|| p->type == bfd_symbol_reloc_link_order)
++o->reloc_count;
else if (p->type == bfd_indirect_link_order)
{
asection *sec;
sec = p->u.indirect.section;
sec->linker_mark = true;
if (sec->flags & SEC_MERGE)
merged = true;
if (info->relocateable || info->emitrelocations)
o->reloc_count += sec->reloc_count;
else if (bed->elf_backend_count_relocs)
{
Elf_Internal_Rela * relocs;
relocs = (NAME(_bfd_elf,link_read_relocs)
(abfd, sec, (PTR) NULL,
(Elf_Internal_Rela *) NULL, info->keep_memory));
o->reloc_count
+= (*bed->elf_backend_count_relocs) (sec, relocs);
if (!info->keep_memory)
free (relocs);
}
if (sec->_raw_size > max_contents_size)
max_contents_size = sec->_raw_size;
if (sec->_cooked_size > max_contents_size)
max_contents_size = sec->_cooked_size;
if (bfd_get_flavour (sec->owner) == bfd_target_elf_flavour
&& (sec->owner->flags & DYNAMIC) == 0)
{
size_t sym_count;
if (elf_bad_symtab (sec->owner))
sym_count = (elf_tdata (sec->owner)->symtab_hdr.sh_size
/ sizeof (Elf_External_Sym));
else
sym_count = elf_tdata (sec->owner)->symtab_hdr.sh_info;
if (sym_count > max_sym_count)
max_sym_count = sym_count;
if (sym_count > max_sym_shndx_count
&& elf_symtab_shndx (sec->owner) != 0)
max_sym_shndx_count = sym_count;
if ((sec->flags & SEC_RELOC) != 0)
{
size_t ext_size;
ext_size = elf_section_data (sec)->rel_hdr.sh_size;
if (ext_size > max_external_reloc_size)
max_external_reloc_size = ext_size;
if (sec->reloc_count > max_internal_reloc_count)
max_internal_reloc_count = sec->reloc_count;
}
}
}
}
if (o->reloc_count > 0)
o->flags |= SEC_RELOC;
else
{
o->flags &=~ SEC_RELOC;
}
if ((o->flags & SEC_ALLOC) == 0
&& ! o->user_set_vma)
o->vma = 0;
}
if (! info->relocateable && merged)
elf_link_hash_traverse (elf_hash_table (info),
elf_link_sec_merge_syms, (PTR) abfd);
bfd_get_symcount (abfd) = info->strip == strip_all ? 0 : 1;
BFD_ASSERT (! abfd->output_has_begun);
if (! _bfd_elf_compute_section_file_positions (abfd, info))
goto error_return;
if (emit_relocs)
for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
for (o = sub->sections; o != NULL; o = o->next)
{
asection *output_section;
if (! o->linker_mark)
{
continue;
}
output_section = o->output_section;
if (output_section != NULL
&& (o->flags & SEC_RELOC) != 0)
{
struct bfd_elf_section_data *esdi
= elf_section_data (o);
struct bfd_elf_section_data *esdo
= elf_section_data (output_section);
unsigned int *rel_count;
unsigned int *rel_count2;
bfd_size_type entsize;
bfd_size_type entsize2;
entsize = esdi->rel_hdr.sh_entsize;
entsize2 = esdi->rel_hdr2 ? esdi->rel_hdr2->sh_entsize : 0;
BFD_ASSERT ((entsize == sizeof (Elf_External_Rel)
|| entsize == sizeof (Elf_External_Rela))
&& entsize2 != entsize
&& (entsize2 == 0
|| entsize2 == sizeof (Elf_External_Rel)
|| entsize2 == sizeof (Elf_External_Rela)));
if (entsize == esdo->rel_hdr.sh_entsize)
{
rel_count = &esdo->rel_count;
rel_count2 = &esdo->rel_count2;
}
else
{
rel_count = &esdo->rel_count2;
rel_count2 = &esdo->rel_count;
}
*rel_count += NUM_SHDR_ENTRIES (& esdi->rel_hdr);
if (esdi->rel_hdr2)
*rel_count2 += NUM_SHDR_ENTRIES (esdi->rel_hdr2);
output_section->flags |= SEC_RELOC;
}
}
for (o = abfd->sections; o != NULL; o = o->next)
{
if ((o->flags & SEC_RELOC) != 0)
{
if (!elf_link_size_reloc_section (abfd,
&elf_section_data (o)->rel_hdr,
o))
goto error_return;
if (elf_section_data (o)->rel_hdr2
&& !elf_link_size_reloc_section (abfd,
elf_section_data (o)->rel_hdr2,
o))
goto error_return;
}
elf_section_data (o)->rel_count = 0;
elf_section_data (o)->rel_count2 = 0;
}
_bfd_elf_assign_file_positions_for_relocs (abfd);
bfd_get_symcount (abfd) = 0;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
symtab_hdr->sh_type = SHT_SYMTAB;
symtab_hdr->sh_flags = 0;
symtab_hdr->sh_addr = 0;
symtab_hdr->sh_size = 0;
symtab_hdr->sh_entsize = sizeof (Elf_External_Sym);
symtab_hdr->sh_addralign = bed->s->file_align;
off = elf_tdata (abfd)->next_file_pos;
off = _bfd_elf_assign_file_position_for_section (symtab_hdr, off, true);
if (! info->keep_memory || max_sym_count < 20)
finfo.symbuf_size = 20;
else
finfo.symbuf_size = max_sym_count;
amt = finfo.symbuf_size;
amt *= sizeof (Elf_External_Sym);
finfo.symbuf = (Elf_External_Sym *) bfd_malloc (amt);
if (finfo.symbuf == NULL)
goto error_return;
if (elf_numsections (abfd) > SHN_LORESERVE)
{
amt = finfo.symbuf_size;
amt *= sizeof (Elf_External_Sym_Shndx);
finfo.symshndxbuf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
if (finfo.symshndxbuf == NULL)
goto error_return;
}
if (info->strip != strip_all
|| emit_relocs)
{
elfsym.st_value = 0;
elfsym.st_size = 0;
elfsym.st_info = 0;
elfsym.st_other = 0;
elfsym.st_shndx = SHN_UNDEF;
if (! elf_link_output_sym (&finfo, (const char *) NULL,
&elfsym, bfd_und_section_ptr))
goto error_return;
}
#if 0
elfsym.st_value = 0;
elfsym.st_size = 0;
elfsym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FILE);
elfsym.st_other = 0;
elfsym.st_shndx = SHN_ABS;
if (! elf_link_output_sym (&finfo, bfd_get_filename (abfd),
&elfsym, bfd_abs_section_ptr))
goto error_return;
#endif
if (info->strip != strip_all
|| emit_relocs)
{
elfsym.st_size = 0;
elfsym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION);
elfsym.st_other = 0;
for (i = 1; i < elf_numsections (abfd); i++)
{
o = section_from_elf_index (abfd, i);
if (o != NULL)
o->target_index = bfd_get_symcount (abfd);
elfsym.st_shndx = i;
if (info->relocateable || o == NULL)
elfsym.st_value = 0;
else
elfsym.st_value = o->vma;
if (! elf_link_output_sym (&finfo, (const char *) NULL,
&elfsym, o))
goto error_return;
if (i == SHN_LORESERVE)
i += SHN_HIRESERVE + 1 - SHN_LORESERVE;
}
}
if (max_contents_size != 0)
{
finfo.contents = (bfd_byte *) bfd_malloc (max_contents_size);
if (finfo.contents == NULL)
goto error_return;
}
if (max_external_reloc_size != 0)
{
finfo.external_relocs = (PTR) bfd_malloc (max_external_reloc_size);
if (finfo.external_relocs == NULL)
goto error_return;
}
if (max_internal_reloc_count != 0)
{
amt = max_internal_reloc_count * bed->s->int_rels_per_ext_rel;
amt *= sizeof (Elf_Internal_Rela);
finfo.internal_relocs = (Elf_Internal_Rela *) bfd_malloc (amt);
if (finfo.internal_relocs == NULL)
goto error_return;
}
if (max_sym_count != 0)
{
amt = max_sym_count * sizeof (Elf_External_Sym);
finfo.external_syms = (Elf_External_Sym *) bfd_malloc (amt);
if (finfo.external_syms == NULL)
goto error_return;
amt = max_sym_count * sizeof (Elf_Internal_Sym);
finfo.internal_syms = (Elf_Internal_Sym *) bfd_malloc (amt);
if (finfo.internal_syms == NULL)
goto error_return;
amt = max_sym_count * sizeof (long);
finfo.indices = (long *) bfd_malloc (amt);
if (finfo.indices == NULL)
goto error_return;
amt = max_sym_count * sizeof (asection *);
finfo.sections = (asection **) bfd_malloc (amt);
if (finfo.sections == NULL)
goto error_return;
}
if (max_sym_shndx_count != 0)
{
amt = max_sym_shndx_count * sizeof (Elf_External_Sym_Shndx);
finfo.locsym_shndx = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
if (finfo.locsym_shndx == NULL)
goto error_return;
}
for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
sub->output_has_begun = false;
for (o = abfd->sections; o != NULL; o = o->next)
{
for (p = o->link_order_head; p != NULL; p = p->next)
{
Elf_Internal_Shdr *rhdr;
if (p->type == bfd_indirect_link_order
&& (bfd_get_flavour (p->u.indirect.section->owner)
== bfd_target_elf_flavour)
&& (((rhdr = &elf_section_data (p->u.indirect.section)->rel_hdr)
->sh_entsize == 0)
|| rhdr->sh_entsize == sizeof (Elf_External_Rel)
|| rhdr->sh_entsize == sizeof (Elf_External_Rela))
&& (((rhdr = elf_section_data (p->u.indirect.section)->rel_hdr2)
== NULL)
|| rhdr->sh_entsize == sizeof (Elf_External_Rel)
|| rhdr->sh_entsize == sizeof (Elf_External_Rela)))
{
sub = p->u.indirect.section->owner;
if (! sub->output_has_begun)
{
if (! elf_link_input_bfd (&finfo, sub))
goto error_return;
sub->output_has_begun = true;
}
}
else if (p->type == bfd_section_reloc_link_order
|| p->type == bfd_symbol_reloc_link_order)
{
if (! elf_reloc_link_order (abfd, info, o, p))
goto error_return;
}
else
{
if (! _bfd_default_link_order (abfd, info, o, p))
goto error_return;
}
}
}
eoinfo.failed = false;
eoinfo.finfo = &finfo;
eoinfo.localsyms = true;
elf_link_hash_traverse (elf_hash_table (info), elf_link_output_extsym,
(PTR) &eoinfo);
if (eoinfo.failed)
return false;
symtab_hdr->sh_info = bfd_get_symcount (abfd);
if (dynamic
&& finfo.dynsym_sec->output_section != bfd_abs_section_ptr)
{
Elf_Internal_Sym sym;
Elf_External_Sym *dynsym =
(Elf_External_Sym *) finfo.dynsym_sec->contents;
long last_local = 0;
if (info->shared)
{
asection *s;
sym.st_size = 0;
sym.st_name = 0;
sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION);
sym.st_other = 0;
for (s = abfd->sections; s != NULL; s = s->next)
{
int indx;
Elf_External_Sym *dest;
indx = elf_section_data (s)->this_idx;
BFD_ASSERT (indx > 0);
sym.st_shndx = indx;
sym.st_value = s->vma;
dest = dynsym + elf_section_data (s)->dynindx;
elf_swap_symbol_out (abfd, &sym, (PTR) dest, (PTR) 0);
}
last_local = bfd_count_sections (abfd);
}
if (elf_hash_table (info)->dynlocal)
{
struct elf_link_local_dynamic_entry *e;
for (e = elf_hash_table (info)->dynlocal; e ; e = e->next)
{
asection *s;
Elf_External_Sym *dest;
sym.st_size = e->isym.st_size;
sym.st_other = e->isym.st_other;
sym = e->isym;
if (e->isym.st_shndx != SHN_UNDEF
&& (e->isym.st_shndx < SHN_LORESERVE
|| e->isym.st_shndx > SHN_HIRESERVE))
{
s = bfd_section_from_elf_index (e->input_bfd,
e->isym.st_shndx);
sym.st_shndx =
elf_section_data (s->output_section)->this_idx;
sym.st_value = (s->output_section->vma
+ s->output_offset
+ e->isym.st_value);
}
if (last_local < e->dynindx)
last_local = e->dynindx;
dest = dynsym + e->dynindx;
elf_swap_symbol_out (abfd, &sym, (PTR) dest, (PTR) 0);
}
}
elf_section_data (finfo.dynsym_sec->output_section)->this_hdr.sh_info =
last_local + 1;
}
eoinfo.failed = false;
eoinfo.localsyms = false;
eoinfo.finfo = &finfo;
elf_link_hash_traverse (elf_hash_table (info), elf_link_output_extsym,
(PTR) &eoinfo);
if (eoinfo.failed)
return false;
if (bed->elf_backend_output_arch_syms)
{
typedef boolean (*out_sym_func) PARAMS ((PTR, const char *,
Elf_Internal_Sym *,
asection *));
if (! ((*bed->elf_backend_output_arch_syms)
(abfd, info, (PTR) &finfo, (out_sym_func) elf_link_output_sym)))
return false;
}
if (! elf_link_flush_output_syms (&finfo))
return false;
off += symtab_hdr->sh_size;
symstrtab_hdr = &elf_tdata (abfd)->strtab_hdr;
symstrtab_hdr->sh_type = SHT_STRTAB;
symstrtab_hdr->sh_flags = 0;
symstrtab_hdr->sh_addr = 0;
symstrtab_hdr->sh_size = _bfd_stringtab_size (finfo.symstrtab);
symstrtab_hdr->sh_entsize = 0;
symstrtab_hdr->sh_link = 0;
symstrtab_hdr->sh_info = 0;
symstrtab_hdr->sh_addralign = 1;
off = _bfd_elf_assign_file_position_for_section (symstrtab_hdr, off, true);
elf_tdata (abfd)->next_file_pos = off;
if (bfd_get_symcount (abfd) > 0)
{
if (bfd_seek (abfd, symstrtab_hdr->sh_offset, SEEK_SET) != 0
|| ! _bfd_stringtab_emit (abfd, finfo.symstrtab))
return false;
}
for (o = abfd->sections; o != NULL; o = o->next)
{
if ((o->flags & SEC_RELOC) == 0)
continue;
elf_link_adjust_relocs (abfd, &elf_section_data (o)->rel_hdr,
elf_section_data (o)->rel_count,
elf_section_data (o)->rel_hashes);
if (elf_section_data (o)->rel_hdr2 != NULL)
elf_link_adjust_relocs (abfd, elf_section_data (o)->rel_hdr2,
elf_section_data (o)->rel_count2,
(elf_section_data (o)->rel_hashes
+ elf_section_data (o)->rel_count));
o->reloc_count = 0;
}
if (dynamic && info->combreloc && dynobj != NULL)
relativecount = elf_link_sort_relocs (abfd, info, &reldyn);
if (dynamic)
{
Elf_External_Dyn *dyncon, *dynconend;
o = bfd_get_section_by_name (dynobj, ".dynamic");
BFD_ASSERT (o != NULL);
dyncon = (Elf_External_Dyn *) o->contents;
dynconend = (Elf_External_Dyn *) (o->contents + o->_raw_size);
for (; dyncon < dynconend; dyncon++)
{
Elf_Internal_Dyn dyn;
const char *name;
unsigned int type;
elf_swap_dyn_in (dynobj, dyncon, &dyn);
switch (dyn.d_tag)
{
default:
break;
case DT_NULL:
if (relativecount > 0 && dyncon + 1 < dynconend)
{
switch (elf_section_data (reldyn)->this_hdr.sh_type)
{
case SHT_REL: dyn.d_tag = DT_RELCOUNT; break;
case SHT_RELA: dyn.d_tag = DT_RELACOUNT; break;
default: break;
}
if (dyn.d_tag != DT_NULL)
{
dyn.d_un.d_val = relativecount;
elf_swap_dyn_out (dynobj, &dyn, dyncon);
relativecount = 0;
}
}
break;
case DT_INIT:
name = info->init_function;
goto get_sym;
case DT_FINI:
name = info->fini_function;
get_sym:
{
struct elf_link_hash_entry *h;
h = elf_link_hash_lookup (elf_hash_table (info), name,
false, false, true);
if (h != NULL
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak))
{
dyn.d_un.d_val = h->root.u.def.value;
o = h->root.u.def.section;
if (o->output_section != NULL)
dyn.d_un.d_val += (o->output_section->vma
+ o->output_offset);
else
{
dyn.d_un.d_val = 0;
}
elf_swap_dyn_out (dynobj, &dyn, dyncon);
}
}
break;
case DT_PREINIT_ARRAYSZ:
name = ".preinit_array";
goto get_size;
case DT_INIT_ARRAYSZ:
name = ".init_array";
goto get_size;
case DT_FINI_ARRAYSZ:
name = ".fini_array";
get_size:
o = bfd_get_section_by_name (abfd, name);
BFD_ASSERT (o != NULL);
if (o->_raw_size == 0)
(*_bfd_error_handler)
(_("warning: %s section has zero size"), name);
dyn.d_un.d_val = o->_raw_size;
elf_swap_dyn_out (dynobj, &dyn, dyncon);
break;
case DT_PREINIT_ARRAY:
name = ".preinit_array";
goto get_vma;
case DT_INIT_ARRAY:
name = ".init_array";
goto get_vma;
case DT_FINI_ARRAY:
name = ".fini_array";
goto get_vma;
case DT_HASH:
name = ".hash";
goto get_vma;
case DT_STRTAB:
name = ".dynstr";
goto get_vma;
case DT_SYMTAB:
name = ".dynsym";
goto get_vma;
case DT_VERDEF:
name = ".gnu.version_d";
goto get_vma;
case DT_VERNEED:
name = ".gnu.version_r";
goto get_vma;
case DT_VERSYM:
name = ".gnu.version";
get_vma:
o = bfd_get_section_by_name (abfd, name);
BFD_ASSERT (o != NULL);
dyn.d_un.d_ptr = o->vma;
elf_swap_dyn_out (dynobj, &dyn, dyncon);
break;
case DT_REL:
case DT_RELA:
case DT_RELSZ:
case DT_RELASZ:
if (dyn.d_tag == DT_REL || dyn.d_tag == DT_RELSZ)
type = SHT_REL;
else
type = SHT_RELA;
dyn.d_un.d_val = 0;
for (i = 1; i < elf_numsections (abfd); i++)
{
Elf_Internal_Shdr *hdr;
hdr = elf_elfsections (abfd)[i];
if (hdr->sh_type == type
&& (hdr->sh_flags & SHF_ALLOC) != 0)
{
if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ)
dyn.d_un.d_val += hdr->sh_size;
else
{
if (dyn.d_un.d_val == 0
|| hdr->sh_addr < dyn.d_un.d_val)
dyn.d_un.d_val = hdr->sh_addr;
}
}
}
elf_swap_dyn_out (dynobj, &dyn, dyncon);
break;
}
}
}
if (dynobj != NULL)
{
if (! (*bed->elf_backend_finish_dynamic_sections) (abfd, info))
goto error_return;
for (o = dynobj->sections; o != NULL; o = o->next)
{
if ((o->flags & SEC_HAS_CONTENTS) == 0
|| o->_raw_size == 0
|| o->output_section == bfd_abs_section_ptr)
continue;
if ((o->flags & SEC_LINKER_CREATED) == 0)
{
continue;
}
if ((elf_section_data (o->output_section)->this_hdr.sh_type
!= SHT_STRTAB)
|| strcmp (bfd_get_section_name (abfd, o), ".dynstr") != 0)
{
if (! bfd_set_section_contents (abfd, o->output_section,
o->contents,
(file_ptr) o->output_offset,
o->_raw_size))
goto error_return;
}
else
{
off = elf_section_data (o->output_section)->this_hdr.sh_offset;
if (bfd_seek (abfd, off, SEEK_SET) != 0
|| ! _bfd_elf_strtab_emit (abfd,
elf_hash_table (info)->dynstr))
goto error_return;
}
}
}
if (elf_hash_table (info)->stab_info != NULL)
{
if (! _bfd_write_stab_strings (abfd, &elf_hash_table (info)->stab_info))
goto error_return;
}
if (info->eh_frame_hdr && elf_hash_table (info)->dynobj)
{
o = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
".eh_frame_hdr");
if (o
&& (elf_section_data (o)->sec_info_type
== ELF_INFO_TYPE_EH_FRAME_HDR))
{
if (! _bfd_elf_write_section_eh_frame_hdr (abfd, o))
goto error_return;
}
}
if (finfo.symstrtab != NULL)
_bfd_stringtab_free (finfo.symstrtab);
if (finfo.contents != NULL)
free (finfo.contents);
if (finfo.external_relocs != NULL)
free (finfo.external_relocs);
if (finfo.internal_relocs != NULL)
free (finfo.internal_relocs);
if (finfo.external_syms != NULL)
free (finfo.external_syms);
if (finfo.locsym_shndx != NULL)
free (finfo.locsym_shndx);
if (finfo.internal_syms != NULL)
free (finfo.internal_syms);
if (finfo.indices != NULL)
free (finfo.indices);
if (finfo.sections != NULL)
free (finfo.sections);
if (finfo.symbuf != NULL)
free (finfo.symbuf);
if (finfo.symshndxbuf != NULL)
free (finfo.symbuf);
for (o = abfd->sections; o != NULL; o = o->next)
{
if ((o->flags & SEC_RELOC) != 0
&& elf_section_data (o)->rel_hashes != NULL)
free (elf_section_data (o)->rel_hashes);
}
elf_tdata (abfd)->linker = true;
return true;
error_return:
if (finfo.symstrtab != NULL)
_bfd_stringtab_free (finfo.symstrtab);
if (finfo.contents != NULL)
free (finfo.contents);
if (finfo.external_relocs != NULL)
free (finfo.external_relocs);
if (finfo.internal_relocs != NULL)
free (finfo.internal_relocs);
if (finfo.external_syms != NULL)
free (finfo.external_syms);
if (finfo.locsym_shndx != NULL)
free (finfo.locsym_shndx);
if (finfo.internal_syms != NULL)
free (finfo.internal_syms);
if (finfo.indices != NULL)
free (finfo.indices);
if (finfo.sections != NULL)
free (finfo.sections);
if (finfo.symbuf != NULL)
free (finfo.symbuf);
if (finfo.symshndxbuf != NULL)
free (finfo.symbuf);
for (o = abfd->sections; o != NULL; o = o->next)
{
if ((o->flags & SEC_RELOC) != 0
&& elf_section_data (o)->rel_hashes != NULL)
free (elf_section_data (o)->rel_hashes);
}
return false;
}
static boolean
elf_link_output_sym (finfo, name, elfsym, input_sec)
struct elf_final_link_info *finfo;
const char *name;
Elf_Internal_Sym *elfsym;
asection *input_sec;
{
Elf_External_Sym *dest;
Elf_External_Sym_Shndx *destshndx;
boolean (*output_symbol_hook) PARAMS ((bfd *,
struct bfd_link_info *info,
const char *,
Elf_Internal_Sym *,
asection *));
output_symbol_hook = get_elf_backend_data (finfo->output_bfd)->
elf_backend_link_output_symbol_hook;
if (output_symbol_hook != NULL)
{
if (! ((*output_symbol_hook)
(finfo->output_bfd, finfo->info, name, elfsym, input_sec)))
return false;
}
if (name == (const char *) NULL || *name == '\0')
elfsym->st_name = 0;
else if (input_sec->flags & SEC_EXCLUDE)
elfsym->st_name = 0;
else
{
elfsym->st_name = (unsigned long) _bfd_stringtab_add (finfo->symstrtab,
name, true, false);
if (elfsym->st_name == (unsigned long) -1)
return false;
}
if (finfo->symbuf_count >= finfo->symbuf_size)
{
if (! elf_link_flush_output_syms (finfo))
return false;
}
dest = finfo->symbuf + finfo->symbuf_count;
destshndx = finfo->symshndxbuf;
if (destshndx != NULL)
destshndx += finfo->symbuf_count;
elf_swap_symbol_out (finfo->output_bfd, elfsym, (PTR) dest, (PTR) destshndx);
++finfo->symbuf_count;
++ bfd_get_symcount (finfo->output_bfd);
return true;
}
static boolean
elf_link_flush_output_syms (finfo)
struct elf_final_link_info *finfo;
{
if (finfo->symbuf_count > 0)
{
Elf_Internal_Shdr *hdr;
file_ptr pos;
bfd_size_type amt;
hdr = &elf_tdata (finfo->output_bfd)->symtab_hdr;
pos = hdr->sh_offset + hdr->sh_size;
amt = finfo->symbuf_count * sizeof (Elf_External_Sym);
if (bfd_seek (finfo->output_bfd, pos, SEEK_SET) != 0
|| bfd_bwrite ((PTR) finfo->symbuf, amt, finfo->output_bfd) != amt)
return false;
hdr->sh_size += amt;
if (finfo->symshndxbuf != NULL)
{
hdr = &elf_tdata (finfo->output_bfd)->symtab_shndx_hdr;
pos = hdr->sh_offset + hdr->sh_size;
amt = finfo->symbuf_count * sizeof (Elf_External_Sym_Shndx);
if (bfd_seek (finfo->output_bfd, pos, SEEK_SET) != 0
|| (bfd_bwrite ((PTR) finfo->symshndxbuf, amt, finfo->output_bfd)
!= amt))
return false;
hdr->sh_size += amt;
}
finfo->symbuf_count = 0;
}
return true;
}
static boolean
elf_link_sec_merge_syms (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
asection *sec;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if ((h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
&& ((sec = h->root.u.def.section)->flags & SEC_MERGE)
&& elf_section_data (sec)->sec_info_type == ELF_INFO_TYPE_MERGE)
{
bfd *output_bfd = (bfd *) data;
h->root.u.def.value =
_bfd_merged_section_offset (output_bfd,
&h->root.u.def.section,
elf_section_data (sec)->sec_info,
h->root.u.def.value, (bfd_vma) 0);
}
return true;
}
static boolean
elf_link_output_extsym (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
struct elf_outext_info *eoinfo = (struct elf_outext_info *) data;
struct elf_final_link_info *finfo = eoinfo->finfo;
boolean strip;
Elf_Internal_Sym sym;
asection *input_sec;
if (h->root.type == bfd_link_hash_warning)
{
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->root.type == bfd_link_hash_new)
return true;
}
if (eoinfo->localsyms)
{
if ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
return true;
}
else
{
if ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
return true;
}
if (! finfo->info->relocateable
&& ! finfo->info->allow_shlib_undefined
&& ! finfo->info->shared
&& h->root.type == bfd_link_hash_undefined
&& (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0
&& (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
{
if (! ((*finfo->info->callbacks->undefined_symbol)
(finfo->info, h->root.root.string, h->root.u.undef.abfd,
(asection *) NULL, (bfd_vma) 0, true)))
{
eoinfo->failed = true;
return false;
}
}
if (h->indx == -2)
strip = false;
else if (((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
|| (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0)
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
&& (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
strip = true;
else if (finfo->info->strip == strip_all
|| (finfo->info->strip == strip_some
&& bfd_hash_lookup (finfo->info->keep_hash,
h->root.root.string,
false, false) == NULL))
strip = true;
else
strip = false;
if (strip
&& h->dynindx == -1
&& (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
return true;
sym.st_value = 0;
sym.st_size = h->size;
sym.st_other = h->other;
if ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
sym.st_info = ELF_ST_INFO (STB_LOCAL, h->type);
else if (h->root.type == bfd_link_hash_undefweak
|| h->root.type == bfd_link_hash_defweak)
sym.st_info = ELF_ST_INFO (STB_WEAK, h->type);
else
sym.st_info = ELF_ST_INFO (STB_GLOBAL, h->type);
switch (h->root.type)
{
default:
case bfd_link_hash_new:
case bfd_link_hash_warning:
abort ();
return false;
case bfd_link_hash_undefined:
case bfd_link_hash_undefweak:
input_sec = bfd_und_section_ptr;
sym.st_shndx = SHN_UNDEF;
break;
case bfd_link_hash_defined:
case bfd_link_hash_defweak:
{
input_sec = h->root.u.def.section;
if (input_sec->output_section != NULL)
{
sym.st_shndx =
_bfd_elf_section_from_bfd_section (finfo->output_bfd,
input_sec->output_section);
if (sym.st_shndx == SHN_BAD)
{
(*_bfd_error_handler)
(_("%s: could not find output section %s for input section %s"),
bfd_get_filename (finfo->output_bfd),
input_sec->output_section->name,
input_sec->name);
eoinfo->failed = true;
return false;
}
sym.st_value = h->root.u.def.value + input_sec->output_offset;
if (! finfo->info->relocateable)
sym.st_value += input_sec->output_section->vma;
}
else
{
BFD_ASSERT (input_sec->owner == NULL
|| (input_sec->owner->flags & DYNAMIC) != 0);
sym.st_shndx = SHN_UNDEF;
input_sec = bfd_und_section_ptr;
}
}
break;
case bfd_link_hash_common:
input_sec = h->root.u.c.p->section;
sym.st_shndx = SHN_COMMON;
sym.st_value = 1 << h->root.u.c.p->alignment_power;
break;
case bfd_link_hash_indirect:
return true;
}
if ((h->dynindx != -1
|| (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
&& (finfo->info->shared
|| (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
&& elf_hash_table (finfo->info)->dynamic_sections_created)
{
struct elf_backend_data *bed;
bed = get_elf_backend_data (finfo->output_bfd);
if (! ((*bed->elf_backend_finish_dynamic_symbol)
(finfo->output_bfd, finfo->info, h, &sym)))
{
eoinfo->failed = true;
return false;
}
}
if (sym.st_shndx == SHN_UNDEF
&& (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) != 0
&& (ELF_ST_BIND (sym.st_info) == STB_GLOBAL
|| ELF_ST_BIND (sym.st_info) == STB_WEAK))
{
int bindtype;
if ((h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR_NONWEAK) != 0)
bindtype = STB_GLOBAL;
else
bindtype = STB_WEAK;
sym.st_info = ELF_ST_INFO (bindtype, ELF_ST_TYPE (sym.st_info));
}
if (! finfo->info->relocateable
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
sym.st_other ^= ELF_ST_VISIBILITY (sym.st_other);
if (h->dynindx != -1
&& elf_hash_table (finfo->info)->dynamic_sections_created)
{
size_t bucketcount;
size_t bucket;
size_t hash_entry_size;
bfd_byte *bucketpos;
bfd_vma chain;
Elf_External_Sym *esym;
sym.st_name = h->dynstr_index;
esym = (Elf_External_Sym *) finfo->dynsym_sec->contents + h->dynindx;
elf_swap_symbol_out (finfo->output_bfd, &sym, (PTR) esym, (PTR) 0);
bucketcount = elf_hash_table (finfo->info)->bucketcount;
bucket = h->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, (bfd_vma) 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)
{
Elf_Internal_Versym iversym;
Elf_External_Versym *eversym;
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
{
if (h->verinfo.verdef == NULL)
iversym.vs_vers = 0;
else
iversym.vs_vers = h->verinfo.verdef->vd_exp_refno + 1;
}
else
{
if (h->verinfo.vertree == NULL)
iversym.vs_vers = 1;
else
iversym.vs_vers = h->verinfo.vertree->vernum + 1;
}
if ((h->elf_link_hash_flags & ELF_LINK_HIDDEN) != 0)
iversym.vs_vers |= VERSYM_HIDDEN;
eversym = (Elf_External_Versym *) finfo->symver_sec->contents;
eversym += h->dynindx;
_bfd_elf_swap_versym_out (finfo->output_bfd, &iversym, eversym);
}
}
if (strip)
return true;
h->indx = bfd_get_symcount (finfo->output_bfd);
if (! elf_link_output_sym (finfo, h->root.root.string, &sym, input_sec))
{
eoinfo->failed = true;
return false;
}
return true;
}
static void
elf_link_output_relocs (output_bfd, input_section, input_rel_hdr,
internal_relocs)
bfd *output_bfd;
asection *input_section;
Elf_Internal_Shdr *input_rel_hdr;
Elf_Internal_Rela *internal_relocs;
{
Elf_Internal_Rela *irela;
Elf_Internal_Rela *irelaend;
Elf_Internal_Shdr *output_rel_hdr;
asection *output_section;
unsigned int *rel_countp = NULL;
struct elf_backend_data *bed;
bfd_size_type amt;
output_section = input_section->output_section;
output_rel_hdr = NULL;
if (elf_section_data (output_section)->rel_hdr.sh_entsize
== input_rel_hdr->sh_entsize)
{
output_rel_hdr = &elf_section_data (output_section)->rel_hdr;
rel_countp = &elf_section_data (output_section)->rel_count;
}
else if (elf_section_data (output_section)->rel_hdr2
&& (elf_section_data (output_section)->rel_hdr2->sh_entsize
== input_rel_hdr->sh_entsize))
{
output_rel_hdr = elf_section_data (output_section)->rel_hdr2;
rel_countp = &elf_section_data (output_section)->rel_count2;
}
BFD_ASSERT (output_rel_hdr != NULL);
bed = get_elf_backend_data (output_bfd);
irela = internal_relocs;
irelaend = irela + NUM_SHDR_ENTRIES (input_rel_hdr)
* bed->s->int_rels_per_ext_rel;
if (input_rel_hdr->sh_entsize == sizeof (Elf_External_Rel))
{
Elf_External_Rel *erel;
Elf_Internal_Rel *irel;
amt = bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rel);
irel = (Elf_Internal_Rel *) bfd_zmalloc (amt);
if (irel == NULL)
{
(*_bfd_error_handler) (_("Error: out of memory"));
abort ();
}
erel = ((Elf_External_Rel *) output_rel_hdr->contents + *rel_countp);
for (; irela < irelaend; irela += bed->s->int_rels_per_ext_rel, erel++)
{
unsigned int i;
for (i = 0; i < bed->s->int_rels_per_ext_rel; i++)
{
irel[i].r_offset = irela[i].r_offset;
irel[i].r_info = irela[i].r_info;
BFD_ASSERT (irela[i].r_addend == 0);
}
if (bed->s->swap_reloc_out)
(*bed->s->swap_reloc_out) (output_bfd, irel, (PTR) erel);
else
elf_swap_reloc_out (output_bfd, irel, erel);
}
free (irel);
}
else
{
Elf_External_Rela *erela;
BFD_ASSERT (input_rel_hdr->sh_entsize == sizeof (Elf_External_Rela));
erela = ((Elf_External_Rela *) output_rel_hdr->contents + *rel_countp);
for (; irela < irelaend; irela += bed->s->int_rels_per_ext_rel, erela++)
if (bed->s->swap_reloca_out)
(*bed->s->swap_reloca_out) (output_bfd, irela, (PTR) erela);
else
elf_swap_reloca_out (output_bfd, irela, erela);
}
*rel_countp += NUM_SHDR_ENTRIES (input_rel_hdr);
}
static boolean
elf_link_input_bfd (finfo, input_bfd)
struct elf_final_link_info *finfo;
bfd *input_bfd;
{
boolean (*relocate_section) PARAMS ((bfd *, struct bfd_link_info *,
bfd *, asection *, bfd_byte *,
Elf_Internal_Rela *,
Elf_Internal_Sym *, asection **));
bfd *output_bfd;
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Shdr *shndx_hdr;
size_t locsymcount;
size_t extsymoff;
Elf_External_Sym *external_syms;
Elf_External_Sym *esym;
Elf_External_Sym *esymend;
Elf_External_Sym_Shndx *shndx_buf;
Elf_External_Sym_Shndx *shndx;
Elf_Internal_Sym *isym;
long *pindex;
asection **ppsection;
asection *o;
struct elf_backend_data *bed;
boolean emit_relocs;
struct elf_link_hash_entry **sym_hashes;
output_bfd = finfo->output_bfd;
bed = get_elf_backend_data (output_bfd);
relocate_section = bed->elf_backend_relocate_section;
if ((input_bfd->flags & DYNAMIC) != 0)
return true;
emit_relocs = (finfo->info->relocateable
|| finfo->info->emitrelocations
|| bed->elf_backend_emit_relocs);
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
if (elf_bad_symtab (input_bfd))
{
locsymcount = symtab_hdr->sh_size / sizeof (Elf_External_Sym);
extsymoff = 0;
}
else
{
locsymcount = symtab_hdr->sh_info;
extsymoff = symtab_hdr->sh_info;
}
if (symtab_hdr->contents != NULL)
external_syms = (Elf_External_Sym *) symtab_hdr->contents;
else if (locsymcount == 0)
external_syms = NULL;
else
{
bfd_size_type amt = locsymcount * sizeof (Elf_External_Sym);
external_syms = finfo->external_syms;
if (bfd_seek (input_bfd, symtab_hdr->sh_offset, SEEK_SET) != 0
|| bfd_bread (external_syms, amt, input_bfd) != amt)
return false;
}
shndx_hdr = &elf_tdata (input_bfd)->symtab_shndx_hdr;
shndx_buf = NULL;
if (shndx_hdr->sh_size != 0 && locsymcount != 0)
{
bfd_size_type amt = locsymcount * sizeof (Elf_External_Sym_Shndx);
shndx_buf = finfo->locsym_shndx;
if (bfd_seek (input_bfd, shndx_hdr->sh_offset, SEEK_SET) != 0
|| bfd_bread (shndx_buf, amt, input_bfd) != amt)
return false;
}
for (esym = external_syms, esymend = esym + locsymcount,
isym = finfo->internal_syms, pindex = finfo->indices,
ppsection = finfo->sections, shndx = shndx_buf;
esym < esymend;
esym++, isym++, pindex++, ppsection++,
shndx = (shndx != NULL ? shndx + 1 : NULL))
{
asection *isec;
const char *name;
Elf_Internal_Sym osym;
elf_swap_symbol_in (input_bfd, esym, shndx, isym);
*pindex = -1;
if (elf_bad_symtab (input_bfd))
{
if (ELF_ST_BIND (isym->st_info) != STB_LOCAL)
{
*ppsection = NULL;
continue;
}
}
if (isym->st_shndx == SHN_UNDEF)
isec = bfd_und_section_ptr;
else if (isym->st_shndx < SHN_LORESERVE
|| isym->st_shndx > SHN_HIRESERVE)
{
isec = section_from_elf_index (input_bfd, isym->st_shndx);
if (isec
&& elf_section_data (isec)->sec_info_type == ELF_INFO_TYPE_MERGE
&& ELF_ST_TYPE (isym->st_info) != STT_SECTION)
isym->st_value =
_bfd_merged_section_offset (output_bfd, &isec,
elf_section_data (isec)->sec_info,
isym->st_value, (bfd_vma) 0);
}
else if (isym->st_shndx == SHN_ABS)
isec = bfd_abs_section_ptr;
else if (isym->st_shndx == SHN_COMMON)
isec = bfd_com_section_ptr;
else
{
isec = NULL;
}
*ppsection = isec;
if (esym == external_syms)
continue;
if (ELF_ST_TYPE (isym->st_info) == STT_SECTION)
{
continue;
}
if (finfo->info->strip == strip_all)
continue;
if (finfo->info->discard == discard_all)
continue;
if ((isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE)
&& isec != NULL
&& ((! isec->linker_mark && (isec->flags & SEC_HAS_CONTENTS) != 0)
|| (! finfo->info->relocateable
&& (isec->flags & SEC_EXCLUDE) != 0)))
continue;
name = bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link,
isym->st_name);
if (name == NULL)
return false;
if ((finfo->info->strip == strip_some
&& (bfd_hash_lookup (finfo->info->keep_hash, name, false, false)
== NULL))
|| (((finfo->info->discard == discard_sec_merge
&& (isec->flags & SEC_MERGE) && ! finfo->info->relocateable)
|| finfo->info->discard == discard_l)
&& bfd_is_local_label_name (input_bfd, name)))
continue;
osym = *isym;
osym.st_shndx = _bfd_elf_section_from_bfd_section (output_bfd,
isec->output_section);
if (osym.st_shndx == SHN_BAD)
return false;
*pindex = bfd_get_symcount (output_bfd);
osym.st_value += isec->output_offset;
if (! finfo->info->relocateable)
osym.st_value += isec->output_section->vma;
if (! elf_link_output_sym (finfo, name, &osym, isec))
return false;
}
sym_hashes = elf_sym_hashes (input_bfd);
for (o = input_bfd->sections; o != NULL; o = o->next)
{
bfd_byte *contents;
if (! o->linker_mark)
{
continue;
}
if ((o->flags & SEC_HAS_CONTENTS) == 0
|| (o->_raw_size == 0 && (o->flags & SEC_RELOC) == 0))
continue;
if ((o->flags & SEC_LINKER_CREATED) != 0)
{
continue;
}
if (elf_section_data (o)->this_hdr.contents != NULL)
contents = elf_section_data (o)->this_hdr.contents;
else
{
contents = finfo->contents;
if (! bfd_get_section_contents (input_bfd, o, contents,
(file_ptr) 0, o->_raw_size))
return false;
}
if ((o->flags & SEC_RELOC) != 0)
{
Elf_Internal_Rela *internal_relocs;
internal_relocs = (NAME(_bfd_elf,link_read_relocs)
(input_bfd, o, finfo->external_relocs,
finfo->internal_relocs, false));
if (internal_relocs == NULL
&& o->reloc_count > 0)
return false;
if (!finfo->info->relocateable
&& !elf_section_ignore_discarded_relocs (o))
{
Elf_Internal_Rela *rel, *relend;
rel = internal_relocs;
relend = rel + o->reloc_count * bed->s->int_rels_per_ext_rel;
for ( ; rel < relend; rel++)
{
unsigned long r_symndx = ELF_R_SYM (rel->r_info);
if (r_symndx >= locsymcount
|| (elf_bad_symtab (input_bfd)
&& finfo->sections[r_symndx] == NULL))
{
struct elf_link_hash_entry *h;
h = sym_hashes[r_symndx - extsymoff];
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if ((h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
&& elf_discarded_section (h->root.u.def.section))
{
#if BFD_VERSION_DATE < 20031005
if ((o->flags & SEC_DEBUGGING) != 0)
{
#if BFD_VERSION_DATE > 20021005
(*finfo->info->callbacks->warning)
(finfo->info,
_("warning: relocation against removed section; zeroing"),
NULL, input_bfd, o, rel->r_offset);
#endif
BFD_ASSERT (r_symndx != 0);
memset (rel, 0, sizeof (*rel));
}
else
#endif
{
if (! ((*finfo->info->callbacks->undefined_symbol)
(finfo->info, h->root.root.string,
input_bfd, o, rel->r_offset,
true)))
return false;
}
}
}
else
{
asection *sec = finfo->sections[r_symndx];
if (sec != NULL && elf_discarded_section (sec))
{
#if BFD_VERSION_DATE < 20031005
if ((o->flags & SEC_DEBUGGING) != 0
|| (sec->flags & SEC_LINK_ONCE) != 0)
{
#if BFD_VERSION_DATE > 20021005
(*finfo->info->callbacks->warning)
(finfo->info,
_("warning: relocation against removed section"),
NULL, input_bfd, o, rel->r_offset);
#endif
BFD_ASSERT (r_symndx != 0);
rel->r_info
= ELF_R_INFO (0, ELF_R_TYPE (rel->r_info));
rel->r_addend = 0;
}
else
#endif
{
boolean ok;
const char *msg
= _("local symbols in discarded section %s");
bfd_size_type amt
= strlen (sec->name) + strlen (msg) - 1;
char *buf = (char *) bfd_malloc (amt);
if (buf != NULL)
sprintf (buf, msg, sec->name);
else
buf = (char *) sec->name;
ok = (*finfo->info->callbacks
->undefined_symbol) (finfo->info, buf,
input_bfd, o,
rel->r_offset,
true);
if (buf != sec->name)
free (buf);
if (!ok)
return false;
}
}
}
}
}
if (! (*relocate_section) (output_bfd, finfo->info,
input_bfd, o, contents,
internal_relocs,
finfo->internal_syms,
finfo->sections))
return false;
if (emit_relocs)
{
Elf_Internal_Rela *irela;
Elf_Internal_Rela *irelaend;
struct elf_link_hash_entry **rel_hash;
Elf_Internal_Shdr *input_rel_hdr;
unsigned int next_erel;
void (*reloc_emitter) PARAMS ((bfd *, asection *,
Elf_Internal_Shdr *,
Elf_Internal_Rela *));
irela = internal_relocs;
irelaend = irela + o->reloc_count * bed->s->int_rels_per_ext_rel;
rel_hash = (elf_section_data (o->output_section)->rel_hashes
+ elf_section_data (o->output_section)->rel_count
+ elf_section_data (o->output_section)->rel_count2);
for (next_erel = 0; irela < irelaend; irela++, next_erel++)
{
unsigned long r_symndx;
asection *sec;
if (next_erel == bed->s->int_rels_per_ext_rel)
{
rel_hash++;
next_erel = 0;
}
irela->r_offset += o->output_offset;
if (finfo->info->emitrelocations)
irela->r_offset += o->output_section->vma;
r_symndx = ELF_R_SYM (irela->r_info);
if (r_symndx == 0)
continue;
if (r_symndx >= locsymcount
|| (elf_bad_symtab (input_bfd)
&& finfo->sections[r_symndx] == NULL))
{
struct elf_link_hash_entry *rh;
unsigned long indx;
indx = r_symndx - extsymoff;
rh = elf_sym_hashes (input_bfd)[indx];
while (rh->root.type == bfd_link_hash_indirect
|| rh->root.type == bfd_link_hash_warning)
rh = (struct elf_link_hash_entry *) rh->root.u.i.link;
BFD_ASSERT (rh->indx < 0);
rh->indx = -2;
*rel_hash = rh;
continue;
}
*rel_hash = NULL;
isym = finfo->internal_syms + r_symndx;
sec = finfo->sections[r_symndx];
if (ELF_ST_TYPE (isym->st_info) == STT_SECTION)
{
if (sec != NULL
&& (bfd_is_abs_section (sec)
|| (sec->output_section != NULL
&& bfd_is_abs_section (sec->output_section))))
r_symndx = 0;
else if (sec == NULL || sec->owner == NULL)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
else
{
r_symndx = sec->output_section->target_index;
BFD_ASSERT (r_symndx != 0);
}
}
else
{
if (finfo->indices[r_symndx] == -1)
{
unsigned long shlink;
const char *name;
asection *osec;
if (finfo->info->strip == strip_all)
{
bfd_set_error (bfd_error_invalid_operation);
return false;
}
shlink = symtab_hdr->sh_link;
name = (bfd_elf_string_from_elf_section
(input_bfd, shlink, isym->st_name));
if (name == NULL)
return false;
osec = sec->output_section;
isym->st_shndx =
_bfd_elf_section_from_bfd_section (output_bfd,
osec);
if (isym->st_shndx == SHN_BAD)
return false;
isym->st_value += sec->output_offset;
if (! finfo->info->relocateable)
isym->st_value += osec->vma;
finfo->indices[r_symndx]
= bfd_get_symcount (output_bfd);
if (! elf_link_output_sym (finfo, name, isym, sec))
return false;
}
r_symndx = finfo->indices[r_symndx];
}
irela->r_info = ELF_R_INFO (r_symndx,
ELF_R_TYPE (irela->r_info));
}
if (bed->elf_backend_emit_relocs
&& !(finfo->info->relocateable
|| finfo->info->emitrelocations))
reloc_emitter = bed->elf_backend_emit_relocs;
else
reloc_emitter = elf_link_output_relocs;
input_rel_hdr = &elf_section_data (o)->rel_hdr;
(*reloc_emitter) (output_bfd, o, input_rel_hdr, internal_relocs);
input_rel_hdr = elf_section_data (o)->rel_hdr2;
if (input_rel_hdr)
{
internal_relocs += (NUM_SHDR_ENTRIES (input_rel_hdr)
* bed->s->int_rels_per_ext_rel);
reloc_emitter (output_bfd, o, input_rel_hdr, internal_relocs);
}
}
}
if (bed->elf_backend_write_section
&& (*bed->elf_backend_write_section) (output_bfd, o, contents))
{
}
else switch (elf_section_data (o)->sec_info_type)
{
case ELF_INFO_TYPE_STABS:
if (! (_bfd_write_section_stabs
(output_bfd,
&elf_hash_table (finfo->info)->stab_info,
o, &elf_section_data (o)->sec_info, contents)))
return false;
break;
case ELF_INFO_TYPE_MERGE:
if (! (_bfd_write_merged_section
(output_bfd, o, elf_section_data (o)->sec_info)))
return false;
break;
case ELF_INFO_TYPE_EH_FRAME:
{
asection *ehdrsec;
ehdrsec
= bfd_get_section_by_name (elf_hash_table (finfo->info)->dynobj,
".eh_frame_hdr");
if (! (_bfd_elf_write_section_eh_frame (output_bfd, o, ehdrsec,
contents)))
return false;
}
break;
default:
{
bfd_size_type sec_size;
sec_size = (o->_cooked_size != 0 ? o->_cooked_size : o->_raw_size);
if (! (o->flags & SEC_EXCLUDE)
&& ! bfd_set_section_contents (output_bfd, o->output_section,
contents,
(file_ptr) o->output_offset,
sec_size))
return false;
}
break;
}
}
return true;
}
static boolean
elf_reloc_link_order (output_bfd, info, output_section, link_order)
bfd *output_bfd;
struct bfd_link_info *info;
asection *output_section;
struct bfd_link_order *link_order;
{
reloc_howto_type *howto;
long indx;
bfd_vma offset;
bfd_vma addend;
struct elf_link_hash_entry **rel_hash_ptr;
Elf_Internal_Shdr *rel_hdr;
struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
howto = bfd_reloc_type_lookup (output_bfd, link_order->u.reloc.p->reloc);
if (howto == NULL)
{
bfd_set_error (bfd_error_bad_value);
return false;
}
addend = link_order->u.reloc.p->addend;
rel_hash_ptr = (elf_section_data (output_section)->rel_hashes
+ elf_section_data (output_section)->rel_count
+ elf_section_data (output_section)->rel_count2);
if (link_order->type == bfd_section_reloc_link_order)
{
indx = link_order->u.reloc.p->u.section->target_index;
BFD_ASSERT (indx != 0);
*rel_hash_ptr = NULL;
}
else
{
struct elf_link_hash_entry *h;
h = ((struct elf_link_hash_entry *)
bfd_wrapped_link_hash_lookup (output_bfd, info,
link_order->u.reloc.p->u.name,
false, false, true));
if (h != NULL
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak))
{
asection *section;
section = h->root.u.def.section;
indx = section->output_section->target_index;
*rel_hash_ptr = NULL;
addend += section->output_section->vma + section->output_offset;
}
else if (h != NULL)
{
h->indx = -2;
*rel_hash_ptr = h;
indx = 0;
}
else
{
if (! ((*info->callbacks->unattached_reloc)
(info, link_order->u.reloc.p->u.name, (bfd *) NULL,
(asection *) NULL, (bfd_vma) 0)))
return false;
indx = 0;
}
}
if (howto->partial_inplace && addend != 0)
{
bfd_size_type size;
bfd_reloc_status_type rstat;
bfd_byte *buf;
boolean ok;
const char *sym_name;
size = bfd_get_reloc_size (howto);
buf = (bfd_byte *) bfd_zmalloc (size);
if (buf == (bfd_byte *) NULL)
return false;
rstat = _bfd_relocate_contents (howto, output_bfd, (bfd_vma) addend, buf);
switch (rstat)
{
case bfd_reloc_ok:
break;
default:
case bfd_reloc_outofrange:
abort ();
case bfd_reloc_overflow:
if (link_order->type == bfd_section_reloc_link_order)
sym_name = bfd_section_name (output_bfd,
link_order->u.reloc.p->u.section);
else
sym_name = link_order->u.reloc.p->u.name;
if (! ((*info->callbacks->reloc_overflow)
(info, sym_name, howto->name, addend,
(bfd *) NULL, (asection *) NULL, (bfd_vma) 0)))
{
free (buf);
return false;
}
break;
}
ok = bfd_set_section_contents (output_bfd, output_section, (PTR) buf,
(file_ptr) link_order->offset, size);
free (buf);
if (! ok)
return false;
}
offset = link_order->offset;
if (! info->relocateable)
offset += output_section->vma;
rel_hdr = &elf_section_data (output_section)->rel_hdr;
if (rel_hdr->sh_type == SHT_REL)
{
bfd_size_type size;
Elf_Internal_Rel *irel;
Elf_External_Rel *erel;
unsigned int i;
size = bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rel);
irel = (Elf_Internal_Rel *) bfd_zmalloc (size);
if (irel == NULL)
return false;
for (i = 0; i < bed->s->int_rels_per_ext_rel; i++)
irel[i].r_offset = offset;
irel[0].r_info = ELF_R_INFO (indx, howto->type);
erel = ((Elf_External_Rel *) rel_hdr->contents
+ elf_section_data (output_section)->rel_count);
if (bed->s->swap_reloc_out)
(*bed->s->swap_reloc_out) (output_bfd, irel, (bfd_byte *) erel);
else
elf_swap_reloc_out (output_bfd, irel, erel);
free (irel);
}
else
{
bfd_size_type size;
Elf_Internal_Rela *irela;
Elf_External_Rela *erela;
unsigned int i;
size = bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rela);
irela = (Elf_Internal_Rela *) bfd_zmalloc (size);
if (irela == NULL)
return false;
for (i = 0; i < bed->s->int_rels_per_ext_rel; i++)
irela[i].r_offset = offset;
irela[0].r_info = ELF_R_INFO (indx, howto->type);
irela[0].r_addend = addend;
erela = ((Elf_External_Rela *) rel_hdr->contents
+ elf_section_data (output_section)->rel_count);
if (bed->s->swap_reloca_out)
(*bed->s->swap_reloca_out) (output_bfd, irela, (bfd_byte *) erela);
else
elf_swap_reloca_out (output_bfd, irela, erela);
}
++elf_section_data (output_section)->rel_count;
return true;
}
boolean
elf_create_pointer_linker_section (abfd, info, lsect, h, rel)
bfd *abfd;
struct bfd_link_info *info;
elf_linker_section_t *lsect;
struct elf_link_hash_entry *h;
const Elf_Internal_Rela *rel;
{
elf_linker_section_pointers_t **ptr_linker_section_ptr = NULL;
elf_linker_section_pointers_t *linker_section_ptr;
unsigned long r_symndx = ELF_R_SYM (rel->r_info);
bfd_size_type amt;
BFD_ASSERT (lsect != NULL);
if (h != NULL)
{
if (_bfd_elf_find_pointer_linker_section (h->linker_section_pointer,
rel->r_addend,
lsect->which))
return true;
ptr_linker_section_ptr = &h->linker_section_pointer;
if (h->dynindx == -1)
{
if (! elf_link_record_dynamic_symbol (info, h))
return false;
}
if (lsect->rel_section)
lsect->rel_section->_raw_size += sizeof (Elf_External_Rela);
}
else
{
elf_linker_section_pointers_t **ptr = elf_local_ptr_offsets (abfd);
if (!ptr)
{
unsigned int num_symbols = elf_tdata (abfd)->symtab_hdr.sh_info;
register unsigned int i;
amt = num_symbols;
amt *= sizeof (elf_linker_section_pointers_t *);
ptr = (elf_linker_section_pointers_t **) bfd_alloc (abfd, amt);
if (!ptr)
return false;
elf_local_ptr_offsets (abfd) = ptr;
for (i = 0; i < num_symbols; i++)
ptr[i] = (elf_linker_section_pointers_t *) 0;
}
if (_bfd_elf_find_pointer_linker_section (ptr[r_symndx],
rel->r_addend,
lsect->which))
return true;
ptr_linker_section_ptr = &ptr[r_symndx];
if (info->shared)
{
BFD_ASSERT (lsect->rel_section != NULL);
lsect->rel_section->_raw_size += sizeof (Elf_External_Rela);
}
}
BFD_ASSERT (ptr_linker_section_ptr != NULL);
amt = sizeof (elf_linker_section_pointers_t);
linker_section_ptr = (elf_linker_section_pointers_t *) bfd_alloc (abfd, amt);
if (!linker_section_ptr)
return false;
linker_section_ptr->next = *ptr_linker_section_ptr;
linker_section_ptr->addend = rel->r_addend;
linker_section_ptr->which = lsect->which;
linker_section_ptr->written_address_p = false;
*ptr_linker_section_ptr = linker_section_ptr;
#if 0
if (lsect->hole_size && lsect->hole_offset < lsect->max_hole_offset)
{
linker_section_ptr->offset = (lsect->section->_raw_size
- lsect->hole_size + (ARCH_SIZE / 8));
lsect->hole_offset += ARCH_SIZE / 8;
lsect->sym_offset += ARCH_SIZE / 8;
if (lsect->sym_hash)
{
lsect->sym_hash->root.u.def.value += ARCH_SIZE / 8;
#ifdef DEBUG
fprintf (stderr, "Bump up %s by %ld, current value = %ld\n",
lsect->sym_hash->root.root.string,
(long) ARCH_SIZE / 8,
(long) lsect->sym_hash->root.u.def.value);
#endif
}
}
else
#endif
linker_section_ptr->offset = lsect->section->_raw_size;
lsect->section->_raw_size += ARCH_SIZE / 8;
#ifdef DEBUG
fprintf (stderr,
"Create pointer in linker section %s, offset = %ld, section size = %ld\n",
lsect->name, (long) linker_section_ptr->offset,
(long) lsect->section->_raw_size);
#endif
return true;
}
#if ARCH_SIZE==64
#define bfd_put_ptr(BFD,VAL,ADDR) bfd_put_64 (BFD, VAL, ADDR)
#endif
#if ARCH_SIZE==32
#define bfd_put_ptr(BFD,VAL,ADDR) bfd_put_32 (BFD, VAL, ADDR)
#endif
bfd_vma
elf_finish_pointer_linker_section (output_bfd, input_bfd, info, lsect, h,
relocation, rel, relative_reloc)
bfd *output_bfd;
bfd *input_bfd;
struct bfd_link_info *info;
elf_linker_section_t *lsect;
struct elf_link_hash_entry *h;
bfd_vma relocation;
const Elf_Internal_Rela *rel;
int relative_reloc;
{
elf_linker_section_pointers_t *linker_section_ptr;
BFD_ASSERT (lsect != NULL);
if (h != NULL)
{
linker_section_ptr = (_bfd_elf_find_pointer_linker_section
(h->linker_section_pointer,
rel->r_addend,
lsect->which));
BFD_ASSERT (linker_section_ptr != NULL);
if (! elf_hash_table (info)->dynamic_sections_created
|| (info->shared
&& info->symbolic
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
{
if (!linker_section_ptr->written_address_p)
{
linker_section_ptr->written_address_p = true;
bfd_put_ptr (output_bfd,
relocation + linker_section_ptr->addend,
(lsect->section->contents
+ linker_section_ptr->offset));
}
}
}
else
{
unsigned long r_symndx = ELF_R_SYM (rel->r_info);
BFD_ASSERT (elf_local_ptr_offsets (input_bfd) != NULL);
BFD_ASSERT (elf_local_ptr_offsets (input_bfd)[r_symndx] != NULL);
linker_section_ptr = (_bfd_elf_find_pointer_linker_section
(elf_local_ptr_offsets (input_bfd)[r_symndx],
rel->r_addend,
lsect->which));
BFD_ASSERT (linker_section_ptr != NULL);
if (!linker_section_ptr->written_address_p)
{
linker_section_ptr->written_address_p = true;
bfd_put_ptr (output_bfd, relocation + linker_section_ptr->addend,
lsect->section->contents + linker_section_ptr->offset);
if (info->shared)
{
asection *srel = lsect->rel_section;
Elf_Internal_Rela *outrel;
Elf_External_Rela *erel;
struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
unsigned int i;
bfd_size_type amt;
amt = sizeof (Elf_Internal_Rela) * bed->s->int_rels_per_ext_rel;
outrel = (Elf_Internal_Rela *) bfd_zmalloc (amt);
if (outrel == NULL)
{
(*_bfd_error_handler) (_("Error: out of memory"));
return 0;
}
if (!srel)
{
srel = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
lsect->rel_name);
lsect->rel_section = srel;
}
BFD_ASSERT (srel != NULL);
for (i = 0; i < bed->s->int_rels_per_ext_rel; i++)
outrel[i].r_offset = (lsect->section->output_section->vma
+ lsect->section->output_offset
+ linker_section_ptr->offset);
outrel[0].r_info = ELF_R_INFO (0, relative_reloc);
outrel[0].r_addend = 0;
erel = (Elf_External_Rela *) lsect->section->contents;
erel += elf_section_data (lsect->section)->rel_count;
elf_swap_reloca_out (output_bfd, outrel, erel);
++elf_section_data (lsect->section)->rel_count;
free (outrel);
}
}
}
relocation = (lsect->section->output_offset
+ linker_section_ptr->offset
- lsect->hole_offset
- lsect->sym_offset);
#ifdef DEBUG
fprintf (stderr,
"Finish pointer in linker section %s, offset = %ld (0x%lx)\n",
lsect->name, (long) relocation, (long) relocation);
#endif
return relocation - linker_section_ptr->addend;
}
static boolean elf_gc_mark
PARAMS ((struct bfd_link_info *info, asection *sec,
asection * (*gc_mark_hook)
PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
struct elf_link_hash_entry *, Elf_Internal_Sym *))));
static boolean elf_gc_sweep
PARAMS ((struct bfd_link_info *info,
boolean (*gc_sweep_hook)
PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *o,
const Elf_Internal_Rela *relocs))));
static boolean elf_gc_sweep_symbol
PARAMS ((struct elf_link_hash_entry *h, PTR idxptr));
static boolean elf_gc_allocate_got_offsets
PARAMS ((struct elf_link_hash_entry *h, PTR offarg));
static boolean elf_gc_propagate_vtable_entries_used
PARAMS ((struct elf_link_hash_entry *h, PTR dummy));
static boolean elf_gc_smash_unused_vtentry_relocs
PARAMS ((struct elf_link_hash_entry *h, PTR dummy));
static boolean
elf_gc_mark (info, sec, gc_mark_hook)
struct bfd_link_info *info;
asection *sec;
asection * (*gc_mark_hook)
PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
struct elf_link_hash_entry *, Elf_Internal_Sym *));
{
boolean ret;
asection *group_sec;
sec->gc_mark = 1;
group_sec = elf_section_data (sec)->next_in_group;
if (group_sec && !group_sec->gc_mark)
if (!elf_gc_mark (info, group_sec, gc_mark_hook))
return false;
ret = true;
if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0)
{
Elf_Internal_Rela *relstart, *rel, *relend;
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Shdr *shndx_hdr;
struct elf_link_hash_entry **sym_hashes;
size_t nlocsyms;
size_t extsymoff;
Elf_External_Sym *locsyms, *freesyms = NULL;
Elf_External_Sym_Shndx *locsym_shndx;
bfd *input_bfd = sec->owner;
struct elf_backend_data *bed = get_elf_backend_data (input_bfd);
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
if (elf_bad_symtab (input_bfd))
{
nlocsyms = symtab_hdr->sh_size / sizeof (Elf_External_Sym);
extsymoff = 0;
}
else
extsymoff = nlocsyms = symtab_hdr->sh_info;
if (symtab_hdr->contents)
locsyms = (Elf_External_Sym *) symtab_hdr->contents;
else if (nlocsyms == 0)
locsyms = NULL;
else
{
bfd_size_type amt = nlocsyms * sizeof (Elf_External_Sym);
locsyms = freesyms = bfd_malloc (amt);
if (freesyms == NULL
|| bfd_seek (input_bfd, symtab_hdr->sh_offset, SEEK_SET) != 0
|| bfd_bread (locsyms, amt, input_bfd) != amt)
{
ret = false;
goto out1;
}
}
shndx_hdr = &elf_tdata (input_bfd)->symtab_shndx_hdr;
locsym_shndx = NULL;
if (shndx_hdr->sh_size != 0 && nlocsyms != 0)
{
bfd_size_type amt = nlocsyms * sizeof (Elf_External_Sym_Shndx);
locsym_shndx = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
if (bfd_seek (input_bfd, shndx_hdr->sh_offset, SEEK_SET) != 0
|| bfd_bread (locsym_shndx, amt, input_bfd) != amt)
return false;
}
relstart = (NAME(_bfd_elf,link_read_relocs)
(sec->owner, sec, NULL, (Elf_Internal_Rela *) NULL,
info->keep_memory));
if (relstart == NULL)
{
ret = false;
goto out1;
}
relend = relstart + sec->reloc_count * bed->s->int_rels_per_ext_rel;
for (rel = relstart; rel < relend; rel++)
{
unsigned long r_symndx;
asection *rsec;
struct elf_link_hash_entry *h;
Elf_Internal_Sym s;
r_symndx = ELF_R_SYM (rel->r_info);
if (r_symndx == 0)
continue;
if (elf_bad_symtab (sec->owner))
{
elf_swap_symbol_in (input_bfd,
locsyms + r_symndx,
locsym_shndx + (locsym_shndx ? r_symndx : 0),
&s);
if (ELF_ST_BIND (s.st_info) == STB_LOCAL)
rsec = (*gc_mark_hook) (sec->owner, info, rel, NULL, &s);
else
{
h = sym_hashes[r_symndx - extsymoff];
rsec = (*gc_mark_hook) (sec->owner, info, rel, h, NULL);
}
}
else if (r_symndx >= nlocsyms)
{
h = sym_hashes[r_symndx - extsymoff];
rsec = (*gc_mark_hook) (sec->owner, info, rel, h, NULL);
}
else
{
elf_swap_symbol_in (input_bfd,
locsyms + r_symndx,
locsym_shndx + (locsym_shndx ? r_symndx : 0),
&s);
rsec = (*gc_mark_hook) (sec->owner, info, rel, NULL, &s);
}
if (rsec && !rsec->gc_mark)
{
if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
rsec->gc_mark = 1;
else if (!elf_gc_mark (info, rsec, gc_mark_hook))
{
ret = false;
goto out2;
}
}
}
out2:
if (!info->keep_memory)
free (relstart);
out1:
if (freesyms)
free (freesyms);
}
return ret;
}
static boolean
elf_gc_sweep (info, gc_sweep_hook)
struct bfd_link_info *info;
boolean (*gc_sweep_hook)
PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *o,
const Elf_Internal_Rela *relocs));
{
bfd *sub;
for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
{
asection *o;
if (bfd_get_flavour (sub) != bfd_target_elf_flavour)
continue;
for (o = sub->sections; o != NULL; o = o->next)
{
if ((o->flags & SEC_LINKER_CREATED)
|| (o->flags & SEC_DEBUGGING))
o->gc_mark = 1;
if (o->gc_mark)
continue;
if (o->flags & SEC_EXCLUDE)
continue;
o->flags |= SEC_EXCLUDE;
if (gc_sweep_hook
&& (o->flags & SEC_RELOC) && o->reloc_count > 0)
{
Elf_Internal_Rela *internal_relocs;
boolean r;
internal_relocs = (NAME(_bfd_elf,link_read_relocs)
(o->owner, o, NULL, NULL, info->keep_memory));
if (internal_relocs == NULL)
return false;
r = (*gc_sweep_hook) (o->owner, info, o, internal_relocs);
if (!info->keep_memory)
free (internal_relocs);
if (!r)
return false;
}
}
}
{
int i = 0;
elf_link_hash_traverse (elf_hash_table (info),
elf_gc_sweep_symbol,
(PTR) &i);
elf_hash_table (info)->dynsymcount = i;
}
return true;
}
static boolean
elf_gc_sweep_symbol (h, idxptr)
struct elf_link_hash_entry *h;
PTR idxptr;
{
int *idx = (int *) idxptr;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->dynindx != -1
&& ((h->root.type != bfd_link_hash_defined
&& h->root.type != bfd_link_hash_defweak)
|| h->root.u.def.section->gc_mark))
h->dynindx = (*idx)++;
return true;
}
static boolean
elf_gc_propagate_vtable_entries_used (h, okp)
struct elf_link_hash_entry *h;
PTR okp;
{
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->vtable_parent == NULL)
return true;
if (h->vtable_parent == (struct elf_link_hash_entry *) -1)
return true;
if (h->vtable_entries_used && h->vtable_entries_used[-1])
return true;
elf_gc_propagate_vtable_entries_used (h->vtable_parent, okp);
if (h->vtable_entries_used == NULL)
{
h->vtable_entries_used = h->vtable_parent->vtable_entries_used;
h->vtable_entries_size = h->vtable_parent->vtable_entries_size;
}
else
{
size_t n;
boolean *cu, *pu;
cu = h->vtable_entries_used;
cu[-1] = true;
pu = h->vtable_parent->vtable_entries_used;
if (pu != NULL)
{
asection *sec = h->root.u.def.section;
struct elf_backend_data *bed = get_elf_backend_data (sec->owner);
int file_align = bed->s->file_align;
n = h->vtable_parent->vtable_entries_size / file_align;
while (n--)
{
if (*pu)
*cu = true;
pu++;
cu++;
}
}
}
return true;
}
static boolean
elf_gc_smash_unused_vtentry_relocs (h, okp)
struct elf_link_hash_entry *h;
PTR okp;
{
asection *sec;
bfd_vma hstart, hend;
Elf_Internal_Rela *relstart, *relend, *rel;
struct elf_backend_data *bed;
int file_align;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->vtable_parent == NULL)
return true;
BFD_ASSERT (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak);
sec = h->root.u.def.section;
hstart = h->root.u.def.value;
hend = hstart + h->size;
relstart = (NAME(_bfd_elf,link_read_relocs)
(sec->owner, sec, NULL, (Elf_Internal_Rela *) NULL, true));
if (!relstart)
return *(boolean *) okp = false;
bed = get_elf_backend_data (sec->owner);
file_align = bed->s->file_align;
relend = relstart + sec->reloc_count * bed->s->int_rels_per_ext_rel;
for (rel = relstart; rel < relend; ++rel)
if (rel->r_offset >= hstart && rel->r_offset < hend)
{
if (h->vtable_entries_used
&& (rel->r_offset - hstart) < h->vtable_entries_size)
{
bfd_vma entry = (rel->r_offset - hstart) / file_align;
if (h->vtable_entries_used[entry])
continue;
}
rel->r_offset = rel->r_info = rel->r_addend = 0;
}
return true;
}
boolean
elf_gc_sections (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
boolean ok = true;
bfd *sub;
asection * (*gc_mark_hook)
PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
struct elf_link_hash_entry *h, Elf_Internal_Sym *));
if (!get_elf_backend_data (abfd)->can_gc_sections
|| info->relocateable || info->emitrelocations
|| elf_hash_table (info)->dynamic_sections_created)
return true;
elf_link_hash_traverse (elf_hash_table (info),
elf_gc_propagate_vtable_entries_used,
(PTR) &ok);
if (!ok)
return false;
elf_link_hash_traverse (elf_hash_table (info),
elf_gc_smash_unused_vtentry_relocs,
(PTR) &ok);
if (!ok)
return false;
gc_mark_hook = get_elf_backend_data (abfd)->gc_mark_hook;
for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
{
asection *o;
if (bfd_get_flavour (sub) != bfd_target_elf_flavour)
continue;
for (o = sub->sections; o != NULL; o = o->next)
{
if (o->flags & SEC_KEEP)
if (!elf_gc_mark (info, o, gc_mark_hook))
return false;
}
}
if (!elf_gc_sweep (info, get_elf_backend_data (abfd)->gc_sweep_hook))
return false;
return true;
}
boolean
elf_gc_record_vtinherit (abfd, sec, h, offset)
bfd *abfd;
asection *sec;
struct elf_link_hash_entry *h;
bfd_vma offset;
{
struct elf_link_hash_entry **sym_hashes, **sym_hashes_end;
struct elf_link_hash_entry **search, *child;
bfd_size_type extsymcount;
extsymcount = elf_tdata (abfd)->symtab_hdr.sh_size/sizeof (Elf_External_Sym);
if (!elf_bad_symtab (abfd))
extsymcount -= elf_tdata (abfd)->symtab_hdr.sh_info;
sym_hashes = elf_sym_hashes (abfd);
sym_hashes_end = sym_hashes + extsymcount;
for (search = sym_hashes; search != sym_hashes_end; ++search)
{
if ((child = *search) != NULL
&& (child->root.type == bfd_link_hash_defined
|| child->root.type == bfd_link_hash_defweak)
&& child->root.u.def.section == sec
&& child->root.u.def.value == offset)
goto win;
}
(*_bfd_error_handler) ("%s: %s+%lu: No symbol found for INHERIT",
bfd_archive_filename (abfd), sec->name,
(unsigned long) offset);
bfd_set_error (bfd_error_invalid_operation);
return false;
win:
if (!h)
{
child->vtable_parent = (struct elf_link_hash_entry *) -1;
}
else
child->vtable_parent = h;
return true;
}
boolean
elf_gc_record_vtentry (abfd, sec, h, addend)
bfd *abfd ATTRIBUTE_UNUSED;
asection *sec ATTRIBUTE_UNUSED;
struct elf_link_hash_entry *h;
bfd_vma addend;
{
struct elf_backend_data *bed = get_elf_backend_data (abfd);
int file_align = bed->s->file_align;
if (addend >= h->vtable_entries_size)
{
size_t size, bytes;
boolean *ptr = h->vtable_entries_used;
if (h->root.type == bfd_link_hash_undefined)
size = addend;
else
{
size = h->size;
if (size < addend)
{
size = addend;
}
}
bytes = (size / file_align + 1) * sizeof (boolean);
if (ptr)
{
ptr = bfd_realloc (ptr - 1, (bfd_size_type) bytes);
if (ptr != NULL)
{
size_t oldbytes;
oldbytes = ((h->vtable_entries_size / file_align + 1)
* sizeof (boolean));
memset (((char *) ptr) + oldbytes, 0, bytes - oldbytes);
}
}
else
ptr = bfd_zmalloc ((bfd_size_type) bytes);
if (ptr == NULL)
return false;
h->vtable_entries_used = ptr + 1;
h->vtable_entries_size = size;
}
h->vtable_entries_used[addend / file_align] = true;
return true;
}
boolean
elf_gc_common_finalize_got_offsets (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
bfd *i;
struct elf_backend_data *bed = get_elf_backend_data (abfd);
bfd_vma gotoff;
if (bed->want_got_plt)
gotoff = 0;
else
gotoff = bed->got_header_size;
for (i = info->input_bfds; i; i = i->link_next)
{
bfd_signed_vma *local_got;
bfd_size_type j, locsymcount;
Elf_Internal_Shdr *symtab_hdr;
if (bfd_get_flavour (i) != bfd_target_elf_flavour)
continue;
local_got = elf_local_got_refcounts (i);
if (!local_got)
continue;
symtab_hdr = &elf_tdata (i)->symtab_hdr;
if (elf_bad_symtab (i))
locsymcount = symtab_hdr->sh_size / sizeof (Elf_External_Sym);
else
locsymcount = symtab_hdr->sh_info;
for (j = 0; j < locsymcount; ++j)
{
if (local_got[j] > 0)
{
local_got[j] = gotoff;
gotoff += ARCH_SIZE / 8;
}
else
local_got[j] = (bfd_vma) -1;
}
}
elf_link_hash_traverse (elf_hash_table (info),
elf_gc_allocate_got_offsets,
(PTR) &gotoff);
return true;
}
static boolean
elf_gc_allocate_got_offsets (h, offarg)
struct elf_link_hash_entry *h;
PTR offarg;
{
bfd_vma *off = (bfd_vma *) offarg;
if (h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if (h->got.refcount > 0)
{
h->got.offset = off[0];
off[0] += ARCH_SIZE / 8;
}
else
h->got.offset = (bfd_vma) -1;
return true;
}
boolean
elf_gc_common_final_link (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
if (!elf_gc_common_finalize_got_offsets (abfd, info))
return false;
return elf_bfd_final_link (abfd, info);
}
static boolean
elf_collect_hash_codes (h, data)
struct elf_link_hash_entry *h;
PTR data;
{
unsigned long **valuep = (unsigned long **) 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;
if (h->dynindx == -1)
return true;
name = h->root.root.string;
p = strchr (name, ELF_VER_CHR);
if (p != NULL)
{
alc = bfd_malloc ((bfd_size_type) (p - name + 1));
memcpy (alc, name, (size_t) (p - name));
alc[p - name] = '\0';
name = alc;
}
ha = bfd_elf_hash (name);
*(*valuep)++ = ha;
h->elf_hash_value = ha;
if (alc != NULL)
free (alc);
return true;
}
boolean
elf_reloc_symbol_deleted_p (offset, cookie)
bfd_vma offset;
PTR cookie;
{
struct elf_reloc_cookie *rcookie = (struct elf_reloc_cookie *) cookie;
if (rcookie->bad_symtab)
rcookie->rel = rcookie->rels;
for (; rcookie->rel < rcookie->relend; rcookie->rel++)
{
unsigned long r_symndx = ELF_R_SYM (rcookie->rel->r_info);
Elf_Internal_Sym isym;
if (! rcookie->bad_symtab)
if (rcookie->rel->r_offset > offset)
return false;
if (rcookie->rel->r_offset != offset)
continue;
if (rcookie->locsyms && r_symndx < rcookie->locsymcount)
{
Elf_External_Sym *lsym;
Elf_External_Sym_Shndx *lshndx;
lsym = (Elf_External_Sym *) rcookie->locsyms + r_symndx;
lshndx = (Elf_External_Sym_Shndx *) rcookie->locsym_shndx;
if (lshndx != NULL)
lshndx += r_symndx;
elf_swap_symbol_in (rcookie->abfd, lsym, lshndx, &isym);
}
if (r_symndx >= rcookie->locsymcount
|| (rcookie->locsyms
&& ELF_ST_BIND (isym.st_info) != STB_LOCAL))
{
struct elf_link_hash_entry *h;
h = rcookie->sym_hashes[r_symndx - rcookie->extsymoff];
while (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
if ((h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak)
&& elf_discarded_section (h->root.u.def.section))
return true;
else
return false;
}
else if (rcookie->locsyms)
{
asection *isec;
if (isym.st_shndx < SHN_LORESERVE || isym.st_shndx > SHN_HIRESERVE)
{
isec = section_from_elf_index (rcookie->abfd, isym.st_shndx);
if (isec != NULL && elf_discarded_section (isec))
return true;
}
}
return false;
}
return false;
}
boolean
elf_bfd_discard_info (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
struct elf_reloc_cookie cookie;
asection *stab, *eh, *ehdr;
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Shdr *shndx_hdr;
Elf_External_Sym *freesyms;
struct elf_backend_data *bed;
bfd *abfd;
boolean ret = false;
boolean strip = info->strip == strip_all || info->strip == strip_debugger;
if (info->relocateable
|| info->traditional_format
|| info->hash->creator->flavour != bfd_target_elf_flavour
|| ! is_elf_hash_table (info))
return false;
ehdr = NULL;
if (elf_hash_table (info)->dynobj != NULL)
ehdr = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
".eh_frame_hdr");
for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
{
if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
continue;
bed = get_elf_backend_data (abfd);
if ((abfd->flags & DYNAMIC) != 0)
continue;
eh = NULL;
if (ehdr)
{
eh = bfd_get_section_by_name (abfd, ".eh_frame");
if (eh && eh->_raw_size == 0)
eh = NULL;
}
stab = strip ? NULL : bfd_get_section_by_name (abfd, ".stab");
if ((! stab
|| elf_section_data(stab)->sec_info_type != ELF_INFO_TYPE_STABS)
&& ! eh
&& (strip || ! bed->elf_backend_discard_info))
continue;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
cookie.abfd = abfd;
cookie.sym_hashes = elf_sym_hashes (abfd);
cookie.bad_symtab = elf_bad_symtab (abfd);
if (cookie.bad_symtab)
{
cookie.locsymcount =
symtab_hdr->sh_size / sizeof (Elf_External_Sym);
cookie.extsymoff = 0;
}
else
{
cookie.locsymcount = symtab_hdr->sh_info;
cookie.extsymoff = symtab_hdr->sh_info;
}
freesyms = NULL;
if (symtab_hdr->contents)
cookie.locsyms = (void *) symtab_hdr->contents;
else if (cookie.locsymcount == 0)
cookie.locsyms = NULL;
else
{
bfd_size_type amt = cookie.locsymcount * sizeof (Elf_External_Sym);
cookie.locsyms = bfd_malloc (amt);
if (cookie.locsyms == NULL)
return false;
freesyms = cookie.locsyms;
if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0
|| bfd_bread (cookie.locsyms, amt, abfd) != amt)
{
error_ret_free_loc:
free (cookie.locsyms);
return false;
}
}
cookie.locsym_shndx = NULL;
if (shndx_hdr->sh_size != 0 && cookie.locsymcount != 0)
{
bfd_size_type amt;
amt = cookie.locsymcount * sizeof (Elf_External_Sym_Shndx);
cookie.locsym_shndx = bfd_malloc (amt);
if (cookie.locsym_shndx == NULL)
goto error_ret_free_loc;
if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0
|| bfd_bread (cookie.locsym_shndx, amt, abfd) != amt)
{
free (cookie.locsym_shndx);
goto error_ret_free_loc;
}
}
if (stab)
{
cookie.rels = (NAME(_bfd_elf,link_read_relocs)
(abfd, stab, (PTR) NULL,
(Elf_Internal_Rela *) NULL,
info->keep_memory));
if (cookie.rels)
{
cookie.rel = cookie.rels;
cookie.relend =
cookie.rels + stab->reloc_count * bed->s->int_rels_per_ext_rel;
if (_bfd_discard_section_stabs (abfd, stab,
elf_section_data (stab)->sec_info,
elf_reloc_symbol_deleted_p,
&cookie))
ret = true;
if (! info->keep_memory)
free (cookie.rels);
}
}
if (eh)
{
cookie.rels = NULL;
cookie.rel = NULL;
cookie.relend = NULL;
if (eh->reloc_count)
cookie.rels = (NAME(_bfd_elf,link_read_relocs)
(abfd, eh, (PTR) NULL, (Elf_Internal_Rela *) NULL,
info->keep_memory));
if (cookie.rels)
{
cookie.rel = cookie.rels;
cookie.relend =
cookie.rels + eh->reloc_count * bed->s->int_rels_per_ext_rel;
}
if (_bfd_elf_discard_section_eh_frame (abfd, info, eh, ehdr,
elf_reloc_symbol_deleted_p,
&cookie))
ret = true;
if (! info->keep_memory)
free (cookie.rels);
}
if (bed->elf_backend_discard_info)
{
if (bed->elf_backend_discard_info (abfd, &cookie, info))
ret = true;
}
if (cookie.locsym_shndx != NULL)
free (cookie.locsym_shndx);
if (freesyms != NULL)
free (freesyms);
}
if (ehdr && _bfd_elf_discard_section_eh_frame_hdr (output_bfd, info, ehdr))
ret = true;
return ret;
}
static boolean
elf_section_ignore_discarded_relocs (sec)
asection *sec;
{
struct elf_backend_data *bed;
switch (elf_section_data (sec)->sec_info_type)
{
case ELF_INFO_TYPE_STABS:
case ELF_INFO_TYPE_EH_FRAME:
return true;
default:
break;
}
bed = get_elf_backend_data (sec->owner);
if (bed->elf_backend_ignore_discarded_relocs != NULL
&& (*bed->elf_backend_ignore_discarded_relocs) (sec))
return true;
return false;
}