#include "bfd.h"
#include "sysdep.h"
#include "bfdlink.h"
#include "libbfd.h"
#define ARCH_SIZE 0
#include "elf-bfd.h"
bfd_boolean
_bfd_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
{
flagword flags;
asection *s;
struct elf_link_hash_entry *h;
struct bfd_link_hash_entry *bh;
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
int ptralign;
s = bfd_get_section_by_name (abfd, ".got");
if (s != NULL && (s->flags & SEC_LINKER_CREATED) != 0)
return TRUE;
switch (bed->s->arch_size)
{
case 32:
ptralign = 2;
break;
case 64:
ptralign = 3;
break;
default:
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED);
s = bfd_make_section (abfd, ".got");
if (s == NULL
|| !bfd_set_section_flags (abfd, s, flags)
|| !bfd_set_section_alignment (abfd, s, ptralign))
return FALSE;
if (bed->want_got_plt)
{
s = bfd_make_section (abfd, ".got.plt");
if (s == NULL
|| !bfd_set_section_flags (abfd, s, flags)
|| !bfd_set_section_alignment (abfd, s, ptralign))
return FALSE;
}
if (bed->want_got_sym)
{
bh = NULL;
if (!(_bfd_generic_link_add_one_symbol
(info, abfd, "_GLOBAL_OFFSET_TABLE_", BSF_GLOBAL, s,
bed->got_symbol_offset, NULL, FALSE, bed->collect, &bh)))
return FALSE;
h = (struct elf_link_hash_entry *) bh;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_OBJECT;
if (! info->executable
&& ! _bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
elf_hash_table (info)->hgot = h;
}
s->_raw_size += bed->got_header_size + bed->got_symbol_offset;
return TRUE;
}
bfd_boolean
_bfd_elf_link_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
{
flagword flags;
register asection *s;
struct elf_link_hash_entry *h;
struct bfd_link_hash_entry *bh;
const struct elf_backend_data *bed;
if (! is_elf_hash_table (info->hash))
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->executable)
{
s = bfd_make_section (abfd, ".interp");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY))
return FALSE;
}
if (! info->traditional_format)
{
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;
elf_hash_table (info)->eh_info.hdr_sec = s;
}
bed = get_elf_backend_data (abfd);
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, bed->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, bed->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, bed->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, bed->s->log_file_align))
return FALSE;
bh = NULL;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, "_DYNAMIC", BSF_GLOBAL, s, 0, NULL, FALSE,
get_elf_backend_data (abfd)->collect, &bh)))
return FALSE;
h = (struct elf_link_hash_entry *) bh;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_OBJECT;
if (! info->executable
&& ! _bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
s = bfd_make_section (abfd, ".hash");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
return FALSE;
elf_section_data (s)->this_hdr.sh_entsize = bed->s->sizeof_hash_entry;
if (! (*bed->elf_backend_create_dynamic_sections) (abfd, info))
return FALSE;
elf_hash_table (info)->dynamic_sections_created = TRUE;
return TRUE;
}
bfd_boolean
_bfd_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
{
flagword flags, pltflags;
asection *s;
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
| SEC_LINKER_CREATED);
pltflags = flags;
pltflags |= SEC_CODE;
if (bed->plt_not_loaded)
pltflags &= ~ (SEC_CODE | SEC_LOAD | SEC_HAS_CONTENTS);
if (bed->plt_readonly)
pltflags |= SEC_READONLY;
s = bfd_make_section (abfd, ".plt");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, pltflags)
|| ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
return FALSE;
if (bed->want_plt_sym)
{
struct elf_link_hash_entry *h;
struct bfd_link_hash_entry *bh = NULL;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, "_PROCEDURE_LINKAGE_TABLE_", BSF_GLOBAL, s, 0, NULL,
FALSE, get_elf_backend_data (abfd)->collect, &bh)))
return FALSE;
h = (struct elf_link_hash_entry *) bh;
h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
h->type = STT_OBJECT;
if (! info->executable
&& ! _bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
s = bfd_make_section (abfd,
bed->default_use_rela_p ? ".rela.plt" : ".rel.plt");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
return FALSE;
if (! _bfd_elf_create_got_section (abfd, info))
return FALSE;
if (bed->want_dynbss)
{
s = bfd_make_section (abfd, ".dynbss");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, SEC_ALLOC | SEC_LINKER_CREATED))
return FALSE;
if (! info->shared)
{
s = bfd_make_section (abfd,
(bed->default_use_rela_p
? ".rela.bss" : ".rel.bss"));
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
|| ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
return FALSE;
}
}
return TRUE;
}
bfd_boolean
_bfd_elf_link_record_dynamic_symbol (struct bfd_link_info *info,
struct elf_link_hash_entry *h)
{
if (h->dynindx == -1)
{
struct elf_strtab_hash *dynstr;
char *p;
const char *name;
bfd_size_type indx;
switch (ELF_ST_VISIBILITY (h->other))
{
case STV_INTERNAL:
case STV_HIDDEN:
if (h->root.type != bfd_link_hash_undefined
&& h->root.type != bfd_link_hash_undefweak)
{
h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
return TRUE;
}
default:
break;
}
h->dynindx = elf_hash_table (info)->dynsymcount;
++elf_hash_table (info)->dynsymcount;
dynstr = elf_hash_table (info)->dynstr;
if (dynstr == NULL)
{
elf_hash_table (info)->dynstr = dynstr = _bfd_elf_strtab_init ();
if (dynstr == NULL)
return FALSE;
}
name = h->root.root.string;
p = strchr (name, ELF_VER_CHR);
if (p != NULL)
*p = 0;
indx = _bfd_elf_strtab_add (dynstr, name, p != NULL);
if (p != NULL)
*p = ELF_VER_CHR;
if (indx == (bfd_size_type) -1)
return FALSE;
h->dynstr_index = indx;
}
return TRUE;
}
bfd_boolean
bfd_elf_record_link_assignment (bfd *output_bfd ATTRIBUTE_UNUSED,
struct bfd_link_info *info,
const char *name,
bfd_boolean provide)
{
struct elf_link_hash_entry *h;
if (!is_elf_hash_table (info->hash))
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;
}
int
elf_link_record_local_dynamic_symbol (struct bfd_link_info *info,
bfd *input_bfd,
long input_indx)
{
bfd_size_type amt;
struct elf_link_local_dynamic_entry *entry;
struct elf_link_hash_table *eht;
struct elf_strtab_hash *dynstr;
unsigned long dynstr_index;
char *name;
Elf_External_Sym_Shndx eshndx;
char esym[sizeof (Elf64_External_Sym)];
if (! is_elf_hash_table (info->hash))
return 0;
for (entry = elf_hash_table (info)->dynlocal; entry ; entry = entry->next)
if (entry->input_bfd == input_bfd && entry->input_indx == input_indx)
return 1;
amt = sizeof (*entry);
entry = bfd_alloc (input_bfd, amt);
if (entry == NULL)
return 0;
if (!bfd_elf_get_elf_syms (input_bfd, &elf_tdata (input_bfd)->symtab_hdr,
1, input_indx, &entry->isym, esym, &eshndx))
{
bfd_release (input_bfd, entry);
return 0;
}
if (entry->isym.st_shndx != SHN_UNDEF
&& (entry->isym.st_shndx < SHN_LORESERVE
|| entry->isym.st_shndx > SHN_HIRESERVE))
{
asection *s;
s = bfd_section_from_elf_index (input_bfd, entry->isym.st_shndx);
if (s == NULL || bfd_is_abs_section (s->output_section))
{
bfd_release (input_bfd, entry);
return 2;
}
}
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 0;
}
dynstr_index = _bfd_elf_strtab_add (dynstr, name, FALSE);
if (dynstr_index == (unsigned long) -1)
return 0;
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 1;
}
long
_bfd_elf_link_lookup_local_dynindx (struct bfd_link_info *info,
bfd *input_bfd,
long input_indx)
{
struct elf_link_local_dynamic_entry *e;
for (e = elf_hash_table (info)->dynlocal; e ; e = e->next)
if (e->input_bfd == input_bfd && e->input_indx == input_indx)
return e->dynindx;
return -1;
}
static bfd_boolean
elf_link_renumber_hash_table_dynsyms (struct elf_link_hash_entry *h,
void *data)
{
size_t *count = 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->dynindx = ++(*count);
return TRUE;
}
unsigned long
_bfd_elf_link_renumber_dynsyms (bfd *output_bfd, struct bfd_link_info *info)
{
unsigned long dynsymcount = 0;
if (info->shared)
{
asection *p;
for (p = output_bfd->sections; p ; p = p->next)
if ((p->flags & SEC_EXCLUDE) == 0)
elf_section_data (p)->dynindx = ++dynsymcount;
}
if (elf_hash_table (info)->dynlocal)
{
struct elf_link_local_dynamic_entry *p;
for (p = elf_hash_table (info)->dynlocal; p ; p = p->next)
p->dynindx = ++dynsymcount;
}
elf_link_hash_traverse (elf_hash_table (info),
elf_link_renumber_hash_table_dynsyms,
&dynsymcount);
if (dynsymcount != 0)
++dynsymcount;
return elf_hash_table (info)->dynsymcount = dynsymcount;
}
bfd_boolean
_bfd_elf_merge_symbol (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,
bfd_boolean *skip,
bfd_boolean *override,
bfd_boolean *type_change_ok,
bfd_boolean *size_change_ok,
bfd_boolean dt_needed)
{
asection *sec;
struct elf_link_hash_entry *h;
struct elf_link_hash_entry *flip;
int bind;
bfd *oldbfd;
bfd_boolean newdyn, olddyn, olddef, newdef, newdyncommon, olddyncommon;
bfd_boolean newweakdef, oldweakdef, newweakundef, oldweakundef;
*skip = FALSE;
*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 && (h->elf_link_hash_flags & ELF_LINK_DYNAMIC_DEF) == 0)
{
if (!bfd_is_und_section (sec))
h->elf_link_hash_flags |= ELF_LINK_DYNAMIC_DEF;
else
{
if ((h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) == 0)
{
if (bind == STB_WEAK)
h->elf_link_hash_flags |= ELF_LINK_DYNAMIC_WEAK;
}
else if (bind != STB_WEAK)
h->elf_link_hash_flags &= ~ELF_LINK_DYNAMIC_WEAK;
}
}
if (newdyn
&& ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
&& !bfd_is_und_section (sec))
{
*skip = TRUE;
h->elf_link_hash_flags |= ELF_LINK_HASH_REF_DYNAMIC;
if (ELF_ST_VISIBILITY (h->other) == STV_PROTECTED)
return _bfd_elf_link_record_dynamic_symbol (info, h);
else
return TRUE;
}
else if (!newdyn
&& ELF_ST_VISIBILITY (sym->st_other) != STV_DEFAULT
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0)
{
if ((*sym_hash)->root.type == bfd_link_hash_indirect)
h = *sym_hash;
if ((h->root.und_next || info->hash->undefs_tail == &h->root)
&& bfd_is_und_section (sec))
{
h->root.type = bfd_link_hash_undefined;
h->root.u.undef.abfd = abfd;
}
else
{
h->root.type = bfd_link_hash_new;
h->root.u.undef.abfd = NULL;
}
if (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)
{
h->elf_link_hash_flags &= ~ELF_LINK_HASH_DEF_DYNAMIC;
h->elf_link_hash_flags |= (ELF_LINK_HASH_REF_DYNAMIC
| ELF_LINK_DYNAMIC_DEF);
}
h->size = 0;
h->type = 0;
return TRUE;
}
if (bind == STB_WEAK)
{
if (olddef)
{
newweakdef = TRUE;
newweakundef = FALSE;
}
else
{
newweakdef = FALSE;
newweakundef = TRUE;
}
}
else
newweakdef = newweakundef = FALSE;
if (newweakdef && !newdyn && olddyn)
newweakdef = FALSE;
if (h->root.type == bfd_link_hash_defweak)
{
oldweakdef = TRUE;
oldweakundef = FALSE;
}
else if (h->root.type == bfd_link_hash_undefweak)
{
oldweakdef = FALSE;
oldweakundef = TRUE;
}
else
oldweakdef = oldweakundef = FALSE;
if (oldweakdef && !olddyn && newdyn)
oldweakdef = FALSE;
if (newdyn
&& newdef
&& (sec->flags & SEC_ALLOC) != 0
&& (sec->flags & SEC_LOAD) == 0
&& sym->st_size > 0
&& !newweakdef
&& !newweakundef
&& 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 && oldweakdef)
|| oldweakundef
|| newweakdef
|| newweakundef
|| (newdef
&& (h->root.type == bfd_link_hash_undefined
|| h->root.type == bfd_link_hash_undefweak)))
*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
&& (newweakdef
|| newweakundef
|| ELF_ST_TYPE (sym->st_info) == STT_FUNC)))
&& (!oldweakdef
|| dt_needed
|| newweakdef
|| newweakundef))
{
*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;
}
flip = NULL;
if (! newdyn
&& (newdef
|| (bfd_is_com_section (sec)
&& (oldweakdef || h->type == STT_FUNC)))
&& olddyn
&& olddef
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& ((!newweakdef && !newweakundef) || oldweakdef))
{
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;
if ((*sym_hash)->root.type == bfd_link_hash_indirect)
flip = *sym_hash;
else
h->verinfo.vertree = NULL;
}
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;
if ((*sym_hash)->root.type == bfd_link_hash_indirect)
flip = *sym_hash;
else
h->verinfo.vertree = NULL;
}
if (flip != NULL)
{
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
flip->root.type = h->root.type;
h->root.type = bfd_link_hash_indirect;
h->root.u.i.link = (struct bfd_link_hash_entry *) flip;
(*bed->elf_backend_copy_indirect_symbol) (bed, flip, h);
flip->root.u.undef.abfd = h->root.u.undef.abfd;
if (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)
{
h->elf_link_hash_flags &= ~ELF_LINK_HASH_DEF_DYNAMIC;
flip->elf_link_hash_flags |= ELF_LINK_HASH_REF_DYNAMIC;
}
}
if (olddef
&& ! dt_needed
&& oldweakdef
&& newdef
&& newdyn
&& !newweakdef
&& !newweakundef)
{
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
&& !oldweakdef
&& newdef
&& ! newdyn
&& (newweakdef || newweakundef))
*override = TRUE;
return TRUE;
}
bfd_boolean
_bfd_elf_add_default_symbol (bfd *abfd,
struct bfd_link_info *info,
struct elf_link_hash_entry *h,
const char *name,
Elf_Internal_Sym *sym,
asection **psec,
bfd_vma *value,
bfd_boolean *dynsym,
bfd_boolean override,
bfd_boolean dt_needed)
{
bfd_boolean type_change_ok;
bfd_boolean size_change_ok;
bfd_boolean skip;
char *shortname;
struct elf_link_hash_entry *hi;
struct bfd_link_hash_entry *bh;
const struct elf_backend_data *bed;
bfd_boolean collect;
bfd_boolean dynamic;
char *p;
size_t len, shortlen;
asection *sec;
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;
shortlen = p - name;
shortname = bfd_hash_allocate (&info->hash->table, shortlen + 1);
if (shortname == NULL)
return FALSE;
memcpy (shortname, name, shortlen);
shortname[shortlen] = '\0';
type_change_ok = FALSE;
size_change_ok = FALSE;
sec = *psec;
if (!_bfd_elf_merge_symbol (abfd, info, shortname, sym, &sec, value,
&hi, &skip, &override, &type_change_ok,
&size_change_ok, dt_needed))
return FALSE;
if (skip)
goto nondefault;
if (! override)
{
bh = &hi->root;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, shortname, BSF_INDIRECT, bfd_ind_section_ptr,
0, name, FALSE, collect, &bh)))
return FALSE;
hi = (struct elf_link_hash_entry *) bh;
}
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) (bed, 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;
}
}
}
nondefault:
len = strlen (name);
shortname = bfd_hash_allocate (&info->hash->table, len);
if (shortname == NULL)
return FALSE;
memcpy (shortname, name, shortlen);
memcpy (shortname + shortlen, p + 1, len - shortlen);
type_change_ok = FALSE;
size_change_ok = FALSE;
sec = *psec;
if (!_bfd_elf_merge_symbol (abfd, info, shortname, sym, &sec, value,
&hi, &skip, &override, &type_change_ok,
&size_change_ok, dt_needed))
return FALSE;
if (skip)
return TRUE;
if (override)
{
if (hi->root.type != bfd_link_hash_defined
&& hi->root.type != bfd_link_hash_defweak)
(*_bfd_error_handler)
(_("%s: warning: unexpected redefinition of indirect versioned symbol `%s'"),
bfd_archive_filename (abfd), shortname);
}
else
{
bh = &hi->root;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, shortname, BSF_INDIRECT,
bfd_ind_section_ptr, 0, name, FALSE, collect, &bh)))
return FALSE;
hi = (struct elf_link_hash_entry *) bh;
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) (bed, 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;
}
bfd_boolean
_bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data)
{
struct elf_info_failed *eif = 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.list != NULL)
{
d = (*t->match) (&t->globals, NULL, h->root.root.string);
if (d != NULL)
goto doit;
}
if (t->locals.list != NULL)
{
d = (*t->match) (&t->locals, NULL, h->root.root.string);
if (d != NULL)
return TRUE;
}
}
if (!eif->verdefs)
{
doit:
if (! _bfd_elf_link_record_dynamic_symbol (eif->info, h))
{
eif->failed = TRUE;
return FALSE;
}
}
}
return TRUE;
}
bfd_boolean
_bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
void *data)
{
struct elf_find_verdep_info *rinfo = 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 = 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 = 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;
}
bfd_boolean
_bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
{
struct elf_assign_sym_version_info *sinfo;
struct bfd_link_info *info;
const struct elf_backend_data *bed;
struct elf_info_failed eif;
char *p;
bfd_size_type amt;
sinfo = 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 (! _bfd_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;
bfd_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 (len);
if (alc == NULL)
return FALSE;
memcpy (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.list != NULL)
d = (*t->match) (&t->globals, NULL, alc);
if (d == NULL && t->locals.list != NULL)
{
d = (*t->match) (&t->locals, NULL, alc);
if (d != NULL
&& h->dynindx != -1
&& info->shared
&& ! info->export_dynamic)
(*bed->elf_backend_hide_symbol) (info, h, TRUE);
}
free (alc);
break;
}
}
if (t == NULL && info->executable)
{
struct bfd_elf_version_tree **pp;
int version_index;
if (h->dynindx == -1)
return TRUE;
amt = sizeof *t;
t = bfd_zalloc (sinfo->output_bfd, amt);
if (t == NULL)
{
sinfo->failed = TRUE;
return FALSE;
}
t->name = p;
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 *local_ver;
struct bfd_elf_version_expr *d;
local_ver = NULL;
for (t = sinfo->verdefs; t != NULL; t = t->next)
{
if (t->globals.list != NULL)
{
bfd_boolean matched;
matched = FALSE;
d = NULL;
while ((d = (*t->match) (&t->globals, d,
h->root.root.string)) != NULL)
if (d->symver)
matched = TRUE;
else
{
h->verinfo.vertree = t;
local_ver = NULL;
d->script = 1;
break;
}
if (d != NULL)
break;
else if (matched)
(*bed->elf_backend_hide_symbol) (info, h, TRUE);
}
if (t->locals.list != NULL)
{
d = NULL;
while ((d = (*t->match) (&t->locals, d,
h->root.root.string)) != NULL)
{
local_ver = t;
if (d->pattern[0] != '*' || d->pattern[1] != '\0')
break;
}
if (d != NULL)
break;
}
}
if (local_ver != NULL)
{
h->verinfo.vertree = local_ver;
if (h->dynindx != -1
&& info->shared
&& ! info->export_dynamic)
{
(*bed->elf_backend_hide_symbol) (info, h, TRUE);
}
}
}
return TRUE;
}
static bfd_boolean
elf_link_read_relocs_from_section (bfd *abfd,
asection *sec,
Elf_Internal_Shdr *shdr,
void *external_relocs,
Elf_Internal_Rela *internal_relocs)
{
const struct elf_backend_data *bed;
void (*swap_in) (bfd *, const bfd_byte *, Elf_Internal_Rela *);
const bfd_byte *erela;
const bfd_byte *erelaend;
Elf_Internal_Rela *irela;
Elf_Internal_Shdr *symtab_hdr;
size_t nsyms;
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;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
nsyms = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
bed = get_elf_backend_data (abfd);
if (shdr->sh_entsize == bed->s->sizeof_rel)
swap_in = bed->s->swap_reloc_in;
else if (shdr->sh_entsize == bed->s->sizeof_rela)
swap_in = bed->s->swap_reloca_in;
else
{
bfd_set_error (bfd_error_wrong_format);
return FALSE;
}
erela = external_relocs;
erelaend = erela + NUM_SHDR_ENTRIES (shdr) * shdr->sh_entsize;
irela = internal_relocs;
while (erela < erelaend)
{
bfd_vma r_symndx;
(*swap_in) (abfd, erela, irela);
r_symndx = ELF32_R_SYM (irela->r_info);
if (bed->s->arch_size == 64)
r_symndx >>= 24;
if ((size_t) r_symndx >= nsyms)
{
(*_bfd_error_handler)
(_("%s: bad reloc symbol index (0x%lx >= 0x%lx) for offset 0x%lx in section `%s'"),
bfd_archive_filename (abfd), (unsigned long) r_symndx,
(unsigned long) nsyms, irela->r_offset, sec->name);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
irela += bed->s->int_rels_per_ext_rel;
erela += shdr->sh_entsize;
}
return TRUE;
}
Elf_Internal_Rela *
_bfd_elf_link_read_relocs (bfd *abfd,
asection *o,
void *external_relocs,
Elf_Internal_Rela *internal_relocs,
bfd_boolean keep_memory)
{
Elf_Internal_Shdr *rel_hdr;
void *alloc1 = NULL;
Elf_Internal_Rela *alloc2 = NULL;
const 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 = bfd_alloc (abfd, size);
else
internal_relocs = alloc2 = 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 = bfd_malloc (size);
if (alloc1 == NULL)
goto error_return;
external_relocs = alloc1;
}
if (!elf_link_read_relocs_from_section (abfd, o, rel_hdr,
external_relocs,
internal_relocs))
goto error_return;
if (!elf_link_read_relocs_from_section
(abfd, o,
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;
}
bfd_boolean
_bfd_elf_link_size_reloc_section (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 = 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 = 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;
}
bfd_boolean
_bfd_elf_link_output_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;
bfd_byte *erel;
Elf_Internal_Shdr *output_rel_hdr;
asection *output_section;
unsigned int *rel_countp = NULL;
const struct elf_backend_data *bed;
void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *);
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;
}
else
{
(*_bfd_error_handler)
(_("%s: relocation size mismatch in %s section %s"),
bfd_get_filename (output_bfd),
bfd_archive_filename (input_section->owner),
input_section->name);
bfd_set_error (bfd_error_wrong_object_format);
return FALSE;
}
bed = get_elf_backend_data (output_bfd);
if (input_rel_hdr->sh_entsize == bed->s->sizeof_rel)
swap_out = bed->s->swap_reloc_out;
else if (input_rel_hdr->sh_entsize == bed->s->sizeof_rela)
swap_out = bed->s->swap_reloca_out;
else
abort ();
erel = output_rel_hdr->contents;
erel += *rel_countp * input_rel_hdr->sh_entsize;
irela = internal_relocs;
irelaend = irela + (NUM_SHDR_ENTRIES (input_rel_hdr)
* bed->s->int_rels_per_ext_rel);
while (irela < irelaend)
{
(*swap_out) (output_bfd, irela, erel);
irela += bed->s->int_rels_per_ext_rel;
erel += input_rel_hdr->sh_entsize;
}
*rel_countp += NUM_SHDR_ENTRIES (input_rel_hdr);
return TRUE;
}
bfd_boolean
_bfd_elf_fix_symbol_flags (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->hash)
&& (eif->info->symbolic
|| ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0)
{
const struct elf_backend_data *bed;
bfd_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 (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
&& h->root.type == bfd_link_hash_undefweak)
{
const struct elf_backend_data *bed;
bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
(*bed->elf_backend_hide_symbol) (eif->info, h, TRUE);
}
if (h->weakdef != NULL)
{
struct elf_link_hash_entry *weakdef;
weakdef = h->weakdef;
if (h->root.type == bfd_link_hash_indirect)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
BFD_ASSERT (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak);
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
{
const struct elf_backend_data *bed;
bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
(*bed->elf_backend_copy_indirect_symbol) (bed, weakdef, h);
}
}
return TRUE;
}
bfd_boolean
_bfd_elf_adjust_dynamic_symbol (struct elf_link_hash_entry *h, void *data)
{
struct elf_info_failed *eif = data;
bfd *dynobj;
const struct elf_backend_data *bed;
if (! is_elf_hash_table (eif->info->hash))
return FALSE;
if (h->root.type == bfd_link_hash_warning)
{
h->plt = elf_hash_table (eif->info)->init_offset;
h->got = elf_hash_table (eif->info)->init_offset;
h = (struct elf_link_hash_entry *) h->root.u.i.link;
}
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
if (! _bfd_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 = elf_hash_table (eif->info)->init_offset;
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 (! _bfd_elf_adjust_dynamic_symbol (h->weakdef, 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;
}
bfd_boolean
_bfd_elf_link_sec_merge_syms (struct elf_link_hash_entry *h, void *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)
&& sec->sec_info_type == ELF_INFO_TYPE_MERGE)
{
bfd *output_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, 0);
}
return TRUE;
}
bfd_boolean
_bfd_elf_dynamic_symbol_p (struct elf_link_hash_entry *h,
struct bfd_link_info *info,
bfd_boolean ignore_protected)
{
bfd_boolean binding_stays_local_p;
if (h == NULL)
return FALSE;
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->dynindx == -1)
return FALSE;
if (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL)
return FALSE;
binding_stays_local_p = info->executable || info->symbolic;
switch (ELF_ST_VISIBILITY (h->other))
{
case STV_INTERNAL:
case STV_HIDDEN:
return FALSE;
case STV_PROTECTED:
if (!ignore_protected)
binding_stays_local_p = TRUE;
break;
default:
break;
}
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
return TRUE;
return !binding_stays_local_p;
}
bfd_boolean
_bfd_elf_symbol_refs_local_p (struct elf_link_hash_entry *h,
struct bfd_link_info *info,
bfd_boolean local_protected)
{
if (h == NULL)
return TRUE;
if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
return FALSE;
if ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
return TRUE;
if (h->dynindx == -1)
return TRUE;
if (info->executable || info->symbolic)
return TRUE;
if (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
return FALSE;
if (ELF_ST_VISIBILITY (h->other) != STV_PROTECTED)
return TRUE;
return local_protected;
}
struct bfd_section *
_bfd_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
{
struct bfd_section *sec, *tls;
unsigned int align = 0;
for (sec = obfd->sections; sec != NULL; sec = sec->next)
if ((sec->flags & SEC_THREAD_LOCAL) != 0)
break;
tls = sec;
for (; sec != NULL && (sec->flags & SEC_THREAD_LOCAL) != 0; sec = sec->next)
if (sec->alignment_power > align)
align = sec->alignment_power;
elf_hash_table (info)->tls_sec = tls;
if (tls != NULL)
tls->alignment_power = align;
return tls;
}
static bfd_boolean
is_global_data_symbol_definition (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 bfd_boolean
elf_link_is_defined_archive_symbol (bfd * abfd, carsym * symdef)
{
Elf_Internal_Shdr * hdr;
bfd_size_type symcount;
bfd_size_type extsymcount;
bfd_size_type extsymoff;
Elf_Internal_Sym *isymbuf;
Elf_Internal_Sym *isym;
Elf_Internal_Sym *isymend;
bfd_boolean result;
abfd = _bfd_get_elt_at_filepos (abfd, symdef->file_offset);
if (abfd == 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;
else
hdr = &elf_tdata (abfd)->dynsymtab_hdr;
symcount = hdr->sh_size / get_elf_backend_data (abfd)->s->sizeof_sym;
if (elf_bad_symtab (abfd))
{
extsymcount = symcount;
extsymoff = 0;
}
else
{
extsymcount = symcount - hdr->sh_info;
extsymoff = hdr->sh_info;
}
if (extsymcount == 0)
return FALSE;
isymbuf = bfd_elf_get_elf_syms (abfd, hdr, extsymcount, extsymoff,
NULL, NULL, NULL);
if (isymbuf == NULL)
return FALSE;
result = FALSE;
for (isym = isymbuf, isymend = isymbuf + extsymcount; isym < isymend; isym++)
{
const char *name;
name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
isym->st_name);
if (name == NULL)
break;
if (strcmp (name, symdef->name) == 0)
{
result = is_global_data_symbol_definition (abfd, isym);
break;
}
}
free (isymbuf);
return result;
}
bfd_boolean
_bfd_elf_link_add_archive_symbols (bfd *abfd,
struct bfd_link_info *info)
{
symindex c;
bfd_boolean *defined = NULL;
bfd_boolean *included = NULL;
carsym *symdefs;
bfd_boolean loop;
bfd_size_type amt;
if (! bfd_has_map (abfd))
{
if (bfd_openr_next_archived_file (abfd, 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 (bfd_boolean);
defined = bfd_zmalloc (amt);
included = bfd_zmalloc (amt);
if (defined == NULL || included == NULL)
goto error_return;
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;
size_t len, first;
p = strchr (symdef->name, ELF_VER_CHR);
if (p == NULL || p[1] != ELF_VER_CHR)
continue;
len = strlen (symdef->name);
copy = bfd_alloc (abfd, len);
if (copy == NULL)
goto error_return;
first = p - symdef->name + 1;
memcpy (copy, symdef->name, first);
memcpy (copy + first, symdef->name + first + 1, len - first);
h = elf_link_hash_lookup (elf_hash_table (info), copy,
FALSE, FALSE, FALSE);
if (h == NULL)
{
copy[first - 1] = '\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 == 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 (! bfd_link_add_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 != NULL)
free (defined);
if (included != NULL)
free (included);
return FALSE;
}