/* ELF linking support for BFD.
- Copyright (C) 1995-2016 Free Software Foundation, Inc.
+ Copyright (C) 1995-2017 Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
(SEC_ALLOC | SEC_LINKER_CREATED));
if (s == NULL)
return FALSE;
+ htab->sdynbss = s;
+
+ if (bed->want_dynrelro)
+ {
+ /* Similarly, but for symbols that were originally in read-only
+ sections. */
+ s = bfd_make_section_anyway_with_flags (abfd, ".data.rel.ro",
+ (SEC_ALLOC | SEC_READONLY
+ | SEC_HAS_CONTENTS
+ | SEC_LINKER_CREATED));
+ if (s == NULL)
+ return FALSE;
+ htab->sdynrelro = s;
+ }
/* The .rel[a].bss section holds copy relocs. This section is not
normally needed. We need to create it here, though, so that the
be needed, we can discard it later. We will never need this
section when generating a shared object, since they do not use
copy relocs. */
- if (! bfd_link_pic (info))
+ if (bfd_link_executable (info))
{
s = bfd_make_section_anyway_with_flags (abfd,
(bed->rela_plts_and_copies_p
if (s == NULL
|| ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
return FALSE;
+ htab->srelbss = s;
+
+ if (bed->want_dynrelro)
+ {
+ s = (bfd_make_section_anyway_with_flags
+ (abfd, (bed->rela_plts_and_copies_p
+ ? ".rela.data.rel.ro" : ".rel.data.rel.ro"),
+ flags | SEC_READONLY));
+ if (s == NULL
+ || ! bfd_set_section_alignment (abfd, s,
+ bed->s->log_file_align))
+ return FALSE;
+ htab->sreldynrelro = s;
+ }
}
}
if (h == NULL)
return provide;
+ if (h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
if (h->versioned == unknown)
{
/* Set versioned if symbol version is unknown. */
hv->root.u.i.link = (struct bfd_link_hash_entry *) h;
(*bed->elf_backend_copy_indirect_symbol) (info, h, hv);
break;
- case bfd_link_hash_warning:
- abort ();
- break;
+ default:
+ BFD_FAIL ();
+ return FALSE;
}
/* If this symbol is being provided by the linker script, and it is
&& !h->def_regular)
h->verinfo.verdef = NULL;
+ /* Make sure this symbol is not garbage collected. */
+ h->mark = 1;
+
h->def_regular = 1;
if (hidden)
const char *soname = NULL;
char *audit = NULL;
struct bfd_link_needed_list *rpath = NULL, *runpath = NULL;
+ const Elf_Internal_Phdr *phdr;
int ret;
/* ld --just-symbols and dynamic objects don't mix very well.
*pn = rpath;
}
+ /* If we have a PT_GNU_RELRO program header, mark as read-only
+ all sections contained fully therein. This makes relro
+ shared library sections appear as they will at run-time. */
+ phdr = elf_tdata (abfd)->phdr + elf_elfheader (abfd)->e_phnum;
+ while (--phdr >= elf_tdata (abfd)->phdr)
+ if (phdr->p_type == PT_GNU_RELRO)
+ {
+ for (s = abfd->sections; s != NULL; s = s->next)
+ if ((s->flags & SEC_ALLOC) != 0
+ && s->vma >= phdr->p_vaddr
+ && s->vma + s->size <= phdr->p_vaddr + phdr->p_memsz)
+ s->flags |= SEC_READONLY;
+ break;
+ }
+
/* We do not want to include any of the sections in a dynamic
object in the output file. We hack by simply clobbering the
list of sections in the BFD. This could be handled more
return TRUE;
}
+/* Sweep symbols in swept sections. Called via elf_link_hash_traverse. */
+
+struct elf_gc_sweep_symbol_info
+{
+ struct bfd_link_info *info;
+ void (*hide_symbol) (struct bfd_link_info *, struct elf_link_hash_entry *,
+ bfd_boolean);
+};
+
+static bfd_boolean
+elf_gc_sweep_symbol (struct elf_link_hash_entry *h, void *data)
+{
+ if (!h->mark
+ && (((h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && !((h->def_regular || ELF_COMMON_DEF_P (h))
+ && h->root.u.def.section->gc_mark))
+ || h->root.type == bfd_link_hash_undefined
+ || h->root.type == bfd_link_hash_undefweak))
+ {
+ struct elf_gc_sweep_symbol_info *inf;
+
+ inf = (struct elf_gc_sweep_symbol_info *) data;
+ (*inf->hide_symbol) (inf->info, h, TRUE);
+ h->def_regular = 0;
+ h->ref_regular = 0;
+ h->ref_regular_nonweak = 0;
+ }
+
+ return TRUE;
+}
+
/* Set up the sizes and contents of the ELF dynamic sections. This is
called by the ELF linker emulation before_allocation routine. We
must set the sizes of the sections before the linker sets the
size_t soname_indx;
bfd *dynobj;
const struct elf_backend_data *bed;
- struct elf_info_failed asvinfo;
*sinterpptr = NULL;
if (!is_elf_hash_table (info->hash))
return TRUE;
- bed = get_elf_backend_data (output_bfd);
-
- /* Any syms created from now on start with -1 in
- got.refcount/offset and plt.refcount/offset. */
- elf_hash_table (info)->init_got_refcount
- = elf_hash_table (info)->init_got_offset;
- elf_hash_table (info)->init_plt_refcount
- = elf_hash_table (info)->init_plt_offset;
-
- if (bfd_link_relocatable (info)
- && !_bfd_elf_size_group_sections (info))
- return FALSE;
-
- /* The backend may have to create some sections regardless of whether
- we're dynamic or not. */
- if (bed->elf_backend_always_size_sections
- && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
- return FALSE;
-
- /* Determine any GNU_STACK segment requirements, after the backend
- has had a chance to set a default segment size. */
- if (info->execstack)
- elf_stack_flags (output_bfd) = PF_R | PF_W | PF_X;
- else if (info->noexecstack)
- elf_stack_flags (output_bfd) = PF_R | PF_W;
- else
- {
- bfd *inputobj;
- asection *notesec = NULL;
- int exec = 0;
-
- for (inputobj = info->input_bfds;
- inputobj;
- inputobj = inputobj->link.next)
- {
- asection *s;
-
- if (inputobj->flags
- & (DYNAMIC | EXEC_P | BFD_PLUGIN | BFD_LINKER_CREATED))
- continue;
- s = bfd_get_section_by_name (inputobj, ".note.GNU-stack");
- if (s)
- {
- if (s->flags & SEC_CODE)
- exec = PF_X;
- notesec = s;
- }
- else if (bed->default_execstack)
- exec = PF_X;
- }
- if (notesec || info->stacksize > 0)
- elf_stack_flags (output_bfd) = PF_R | PF_W | exec;
- if (notesec && exec && bfd_link_relocatable (info)
- && notesec->output_section != bfd_abs_section_ptr)
- notesec->output_section->flags |= SEC_CODE;
- }
-
dynobj = elf_hash_table (info)->dynobj;
if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
{
- struct elf_info_failed eif;
- struct elf_link_hash_entry *h;
- asection *dynstr;
+ struct bfd_elf_version_tree *verdefs;
+ struct elf_info_failed asvinfo;
struct bfd_elf_version_tree *t;
struct bfd_elf_version_expr *d;
- asection *s;
+ struct elf_info_failed eif;
bfd_boolean all_defined;
-
- *sinterpptr = bfd_get_linker_section (dynobj, ".interp");
- BFD_ASSERT (*sinterpptr != NULL || !bfd_link_executable (info) || info->nointerp);
-
- if (soname != NULL)
- {
- soname_indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
- soname, TRUE);
- if (soname_indx == (size_t) -1
- || !_bfd_elf_add_dynamic_entry (info, DT_SONAME, soname_indx))
- return FALSE;
- }
-
- if (info->symbolic)
- {
- if (!_bfd_elf_add_dynamic_entry (info, DT_SYMBOLIC, 0))
- return FALSE;
- info->flags |= DF_SYMBOLIC;
- }
-
- if (rpath != NULL)
- {
- size_t indx;
- bfd_vma tag;
-
- indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, rpath,
- TRUE);
- if (indx == (size_t) -1)
- return FALSE;
-
- tag = info->new_dtags ? DT_RUNPATH : DT_RPATH;
- if (!_bfd_elf_add_dynamic_entry (info, tag, indx))
- return FALSE;
- }
-
- if (filter_shlib != NULL)
- {
- size_t indx;
-
- indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
- filter_shlib, TRUE);
- if (indx == (size_t) -1
- || !_bfd_elf_add_dynamic_entry (info, DT_FILTER, indx))
- return FALSE;
- }
-
- if (auxiliary_filters != NULL)
- {
- const char * const *p;
-
- for (p = auxiliary_filters; *p != NULL; p++)
- {
- size_t indx;
-
- indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
- *p, TRUE);
- if (indx == (size_t) -1
- || !_bfd_elf_add_dynamic_entry (info, DT_AUXILIARY, indx))
- return FALSE;
- }
- }
-
- if (audit != NULL)
- {
- size_t indx;
-
- indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, audit,
- TRUE);
- if (indx == (size_t) -1
- || !_bfd_elf_add_dynamic_entry (info, DT_AUDIT, indx))
- return FALSE;
- }
-
- if (depaudit != NULL)
- {
- size_t indx;
-
- indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, depaudit,
- TRUE);
- if (indx == (size_t) -1
- || !_bfd_elf_add_dynamic_entry (info, DT_DEPAUDIT, indx))
- return FALSE;
- }
+ asection *s;
eif.info = info;
eif.failed = FALSE;
}
}
- /* Find all symbols which were defined in a dynamic object and make
- the backend pick a reasonable value for them. */
- elf_link_hash_traverse (elf_hash_table (info),
- _bfd_elf_adjust_dynamic_symbol,
- &eif);
- if (eif.failed)
- return FALSE;
+ /* Set up the version definition section. */
+ s = bfd_get_linker_section (dynobj, ".gnu.version_d");
+ BFD_ASSERT (s != NULL);
- /* Add some entries to the .dynamic section. We fill in some of the
- values later, in bfd_elf_final_link, but we must add the entries
- now so that we know the final size of the .dynamic section. */
+ /* We may have created additional version definitions if we are
+ just linking a regular application. */
+ verdefs = info->version_info;
- /* If there are initialization and/or finalization functions to
- call then add the corresponding DT_INIT/DT_FINI entries. */
- h = (info->init_function
- ? elf_link_hash_lookup (elf_hash_table (info),
- info->init_function, FALSE,
- FALSE, FALSE)
- : NULL);
- if (h != NULL
- && (h->ref_regular
- || h->def_regular))
- {
- if (!_bfd_elf_add_dynamic_entry (info, DT_INIT, 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->ref_regular
- || h->def_regular))
- {
- if (!_bfd_elf_add_dynamic_entry (info, DT_FINI, 0))
- return FALSE;
- }
+ /* Skip anonymous version tag. */
+ if (verdefs != NULL && verdefs->vernum == 0)
+ verdefs = verdefs->next;
- s = bfd_get_section_by_name (output_bfd, ".preinit_array");
- if (s != NULL && s->linker_has_input)
- {
- /* DT_PREINIT_ARRAY is not allowed in shared library. */
- if (! bfd_link_executable (info))
- {
- bfd *sub;
- asection *o;
-
- for (sub = info->input_bfds; sub != NULL;
- sub = sub->link.next)
- if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
- for (o = sub->sections; o != NULL; o = o->next)
- if (elf_section_data (o)->this_hdr.sh_type
- == SHT_PREINIT_ARRAY)
- {
- _bfd_error_handler
- (_("%B: .preinit_array section is not allowed in DSO"),
- sub);
- break;
- }
-
- bfd_set_error (bfd_error_nonrepresentable_section);
- return FALSE;
- }
-
- if (!_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAY, 0)
- || !_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAYSZ, 0))
- return FALSE;
- }
- s = bfd_get_section_by_name (output_bfd, ".init_array");
- if (s != NULL && s->linker_has_input)
- {
- if (!_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAY, 0)
- || !_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAYSZ, 0))
- return FALSE;
- }
- s = bfd_get_section_by_name (output_bfd, ".fini_array");
- if (s != NULL && s->linker_has_input)
- {
- if (!_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAY, 0)
- || !_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAYSZ, 0))
- return FALSE;
- }
-
- dynstr = bfd_get_linker_section (dynobj, ".dynstr");
- /* If .dynstr is excluded from the link, we don't want any of
- these tags. Strictly, we should be checking each section
- individually; This quick check covers for the case where
- someone does a /DISCARD/ : { *(*) }. */
- 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 ((info->emit_hash
- && !_bfd_elf_add_dynamic_entry (info, DT_HASH, 0))
- || (info->emit_gnu_hash
- && !_bfd_elf_add_dynamic_entry (info, DT_GNU_HASH, 0))
- || !_bfd_elf_add_dynamic_entry (info, DT_STRTAB, 0)
- || !_bfd_elf_add_dynamic_entry (info, DT_SYMTAB, 0)
- || !_bfd_elf_add_dynamic_entry (info, DT_STRSZ, strsize)
- || !_bfd_elf_add_dynamic_entry (info, DT_SYMENT,
- bed->s->sizeof_sym))
- return FALSE;
- }
- }
-
- if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
- return FALSE;
-
- /* The backend must work out the sizes of all the other dynamic
- sections. */
- if (dynobj != NULL
- && bed->elf_backend_size_dynamic_sections != NULL
- && ! (*bed->elf_backend_size_dynamic_sections) (output_bfd, info))
- return FALSE;
-
- if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
- {
- unsigned long section_sym_count;
- struct bfd_elf_version_tree *verdefs;
- asection *s;
-
- /* Set up the version definition section. */
- s = bfd_get_linker_section (dynobj, ".gnu.version_d");
- BFD_ASSERT (s != NULL);
-
- /* We may have created additional version definitions if we are
- just linking a regular application. */
- verdefs = info->version_info;
-
- /* Skip anonymous version tag. */
- if (verdefs != NULL && verdefs->vernum == 0)
- verdefs = verdefs->next;
-
- if (verdefs == NULL && !info->create_default_symver)
- s->flags |= SEC_EXCLUDE;
- else
+ if (verdefs == NULL && !info->create_default_symver)
+ s->flags |= SEC_EXCLUDE;
+ 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;
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
- def.vd_version = VER_DEF_CURRENT;
- def.vd_flags = 0;
- if (t->globals.list == NULL
- && t->locals.list == NULL
- && ! t->used)
- def.vd_flags |= VER_FLG_WEAK;
- def.vd_ndx = t->vernum + (info->create_default_symver ? 2 : 1);
- def.vd_cnt = cdeps + 1;
- def.vd_hash = bfd_elf_hash (t->name);
- def.vd_aux = sizeof (Elf_External_Verdef);
- def.vd_next = 0;
+ def.vd_version = VER_DEF_CURRENT;
+ def.vd_flags = 0;
+ if (t->globals.list == NULL
+ && t->locals.list == NULL
+ && ! t->used)
+ def.vd_flags |= VER_FLG_WEAK;
+ def.vd_ndx = t->vernum + (info->create_default_symver ? 2 : 1);
+ def.vd_cnt = cdeps + 1;
+ def.vd_hash = bfd_elf_hash (t->name);
+ def.vd_aux = sizeof (Elf_External_Verdef);
+ def.vd_next = 0;
+
+ /* If a basever node is next, it *must* be the last node in
+ the chain, otherwise Verdef construction breaks. */
+ if (t->next != NULL && t->next->vernum == 0)
+ BFD_ASSERT (t->next->next == NULL);
+
+ if (t->next != NULL && t->next->vernum != 0)
+ def.vd_next = (sizeof (Elf_External_Verdef)
+ + (cdeps + 1) * sizeof (Elf_External_Verdaux));
+
+ _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);
+ defaux.vda_next = 0;
+ if (t->deps != NULL)
+ 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)
+ {
+ /* This can happen if there was an error in the
+ version script. */
+ 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);
+ }
+ }
+
+ elf_tdata (output_bfd)->cverdefs = cdefs;
+ }
+
+ /* Work out the size of the version reference section. */
+
+ s = bfd_get_linker_section (dynobj, ".gnu.version_r");
+ BFD_ASSERT (s != NULL);
+ {
+ struct elf_find_verdep_info sinfo;
+
+ 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),
+ _bfd_elf_link_find_version_dependencies,
+ &sinfo);
+ if (sinfo.failed)
+ return FALSE;
+
+ if (elf_tdata (output_bfd)->verref == NULL)
+ s->flags |= SEC_EXCLUDE;
+ else
+ {
+ Elf_Internal_Verneed *vn;
+ unsigned int size;
+ unsigned int crefs;
+ bfd_byte *p;
+
+ /* Build the version dependency section. */
+ size = 0;
+ crefs = 0;
+ for (vn = elf_tdata (output_bfd)->verref;
+ vn != NULL;
+ vn = vn->vn_nextref)
+ {
+ Elf_Internal_Vernaux *a;
+
+ size += sizeof (Elf_External_Verneed);
+ ++crefs;
+ for (a = vn->vn_auxptr; a != NULL; a = a->vna_nextptr)
+ size += sizeof (Elf_External_Vernaux);
+ }
+
+ s->size = size;
+ s->contents = (unsigned char *) bfd_alloc (output_bfd, s->size);
+ if (s->contents == NULL)
+ return FALSE;
+
+ p = s->contents;
+ for (vn = elf_tdata (output_bfd)->verref;
+ vn != NULL;
+ vn = vn->vn_nextref)
+ {
+ unsigned int caux;
+ Elf_Internal_Vernaux *a;
+ size_t indx;
+
+ caux = 0;
+ for (a = vn->vn_auxptr; a != NULL; a = a->vna_nextptr)
+ ++caux;
+
+ vn->vn_version = VER_NEED_CURRENT;
+ vn->vn_cnt = caux;
+ indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+ elf_dt_name (vn->vn_bfd) != NULL
+ ? elf_dt_name (vn->vn_bfd)
+ : lbasename (vn->vn_bfd->filename),
+ FALSE);
+ if (indx == (size_t) -1)
+ return FALSE;
+ vn->vn_file = indx;
+ vn->vn_aux = sizeof (Elf_External_Verneed);
+ if (vn->vn_nextref == NULL)
+ vn->vn_next = 0;
+ else
+ vn->vn_next = (sizeof (Elf_External_Verneed)
+ + caux * sizeof (Elf_External_Vernaux));
+
+ _bfd_elf_swap_verneed_out (output_bfd, vn,
+ (Elf_External_Verneed *) p);
+ p += sizeof (Elf_External_Verneed);
+
+ for (a = vn->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 == (size_t) -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);
+ }
+ }
+
+ elf_tdata (output_bfd)->cverrefs = crefs;
+ }
+ }
+ }
+
+ bed = get_elf_backend_data (output_bfd);
+
+ if (info->gc_sections && bed->can_gc_sections)
+ {
+ struct elf_gc_sweep_symbol_info sweep_info;
+ unsigned long section_sym_count;
+
+ /* Remove the symbols that were in the swept sections from the
+ dynamic symbol table. GCFIXME: Anyone know how to get them
+ out of the static symbol table as well? */
+ sweep_info.info = info;
+ sweep_info.hide_symbol = bed->elf_backend_hide_symbol;
+ elf_link_hash_traverse (elf_hash_table (info), elf_gc_sweep_symbol,
+ &sweep_info);
+
+ _bfd_elf_link_renumber_dynsyms (output_bfd, info, §ion_sym_count);
+ }
+
+ /* Any syms created from now on start with -1 in
+ got.refcount/offset and plt.refcount/offset. */
+ elf_hash_table (info)->init_got_refcount
+ = elf_hash_table (info)->init_got_offset;
+ elf_hash_table (info)->init_plt_refcount
+ = elf_hash_table (info)->init_plt_offset;
+
+ if (bfd_link_relocatable (info)
+ && !_bfd_elf_size_group_sections (info))
+ return FALSE;
+
+ /* The backend may have to create some sections regardless of whether
+ we're dynamic or not. */
+ if (bed->elf_backend_always_size_sections
+ && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
+ return FALSE;
+
+ /* Determine any GNU_STACK segment requirements, after the backend
+ has had a chance to set a default segment size. */
+ if (info->execstack)
+ elf_stack_flags (output_bfd) = PF_R | PF_W | PF_X;
+ else if (info->noexecstack)
+ elf_stack_flags (output_bfd) = PF_R | PF_W;
+ else
+ {
+ bfd *inputobj;
+ asection *notesec = NULL;
+ int exec = 0;
+
+ for (inputobj = info->input_bfds;
+ inputobj;
+ inputobj = inputobj->link.next)
+ {
+ asection *s;
+
+ if (inputobj->flags
+ & (DYNAMIC | EXEC_P | BFD_PLUGIN | BFD_LINKER_CREATED))
+ continue;
+ s = bfd_get_section_by_name (inputobj, ".note.GNU-stack");
+ if (s)
+ {
+ if (s->flags & SEC_CODE)
+ exec = PF_X;
+ notesec = s;
+ }
+ else if (bed->default_execstack)
+ exec = PF_X;
+ }
+ if (notesec || info->stacksize > 0)
+ elf_stack_flags (output_bfd) = PF_R | PF_W | exec;
+ if (notesec && exec && bfd_link_relocatable (info)
+ && notesec->output_section != bfd_abs_section_ptr)
+ notesec->output_section->flags |= SEC_CODE;
+ }
+
+ if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
+ {
+ struct elf_info_failed eif;
+ struct elf_link_hash_entry *h;
+ asection *dynstr;
+ asection *s;
+
+ *sinterpptr = bfd_get_linker_section (dynobj, ".interp");
+ BFD_ASSERT (*sinterpptr != NULL || !bfd_link_executable (info) || info->nointerp);
+
+ if (soname != NULL)
+ {
+ soname_indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+ soname, TRUE);
+ if (soname_indx == (size_t) -1
+ || !_bfd_elf_add_dynamic_entry (info, DT_SONAME, soname_indx))
+ return FALSE;
+ }
+
+ if (info->symbolic)
+ {
+ if (!_bfd_elf_add_dynamic_entry (info, DT_SYMBOLIC, 0))
+ return FALSE;
+ info->flags |= DF_SYMBOLIC;
+ }
+
+ if (rpath != NULL)
+ {
+ size_t indx;
+ bfd_vma tag;
+
+ indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, rpath,
+ TRUE);
+ if (indx == (size_t) -1)
+ return FALSE;
+
+ tag = info->new_dtags ? DT_RUNPATH : DT_RPATH;
+ if (!_bfd_elf_add_dynamic_entry (info, tag, indx))
+ return FALSE;
+ }
+
+ if (filter_shlib != NULL)
+ {
+ size_t indx;
+
+ indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+ filter_shlib, TRUE);
+ if (indx == (size_t) -1
+ || !_bfd_elf_add_dynamic_entry (info, DT_FILTER, indx))
+ return FALSE;
+ }
+
+ if (auxiliary_filters != NULL)
+ {
+ const char * const *p;
+
+ for (p = auxiliary_filters; *p != NULL; p++)
+ {
+ size_t indx;
+
+ indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+ *p, TRUE);
+ if (indx == (size_t) -1
+ || !_bfd_elf_add_dynamic_entry (info, DT_AUXILIARY, indx))
+ return FALSE;
+ }
+ }
+
+ if (audit != NULL)
+ {
+ size_t indx;
+
+ indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, audit,
+ TRUE);
+ if (indx == (size_t) -1
+ || !_bfd_elf_add_dynamic_entry (info, DT_AUDIT, indx))
+ return FALSE;
+ }
+
+ if (depaudit != NULL)
+ {
+ size_t indx;
+
+ indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, depaudit,
+ TRUE);
+ if (indx == (size_t) -1
+ || !_bfd_elf_add_dynamic_entry (info, DT_DEPAUDIT, indx))
+ return FALSE;
+ }
+
+ eif.info = info;
+ eif.failed = FALSE;
+
+ /* Find all symbols which were defined in a dynamic object and make
+ the backend pick a reasonable value for them. */
+ elf_link_hash_traverse (elf_hash_table (info),
+ _bfd_elf_adjust_dynamic_symbol,
+ &eif);
+ if (eif.failed)
+ return FALSE;
+
+ /* Add some entries to the .dynamic section. We fill in some of the
+ values later, in bfd_elf_final_link, but we must add the entries
+ now so that we know the final size of the .dynamic section. */
+
+ /* If there are initialization and/or finalization functions to
+ call then add the corresponding DT_INIT/DT_FINI entries. */
+ h = (info->init_function
+ ? elf_link_hash_lookup (elf_hash_table (info),
+ info->init_function, FALSE,
+ FALSE, FALSE)
+ : NULL);
+ if (h != NULL
+ && (h->ref_regular
+ || h->def_regular))
+ {
+ if (!_bfd_elf_add_dynamic_entry (info, DT_INIT, 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->ref_regular
+ || h->def_regular))
+ {
+ if (!_bfd_elf_add_dynamic_entry (info, DT_FINI, 0))
+ return FALSE;
+ }
+
+ s = bfd_get_section_by_name (output_bfd, ".preinit_array");
+ if (s != NULL && s->linker_has_input)
+ {
+ /* DT_PREINIT_ARRAY is not allowed in shared library. */
+ if (! bfd_link_executable (info))
+ {
+ bfd *sub;
+ asection *o;
+
+ for (sub = info->input_bfds; sub != NULL;
+ sub = sub->link.next)
+ if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
+ for (o = sub->sections; o != NULL; o = o->next)
+ if (elf_section_data (o)->this_hdr.sh_type
+ == SHT_PREINIT_ARRAY)
+ {
+ _bfd_error_handler
+ (_("%B: .preinit_array section is not allowed in DSO"),
+ sub);
+ break;
+ }
+
+ bfd_set_error (bfd_error_nonrepresentable_section);
+ return FALSE;
+ }
- /* If a basever node is next, it *must* be the last node in
- the chain, otherwise Verdef construction breaks. */
- if (t->next != NULL && t->next->vernum == 0)
- BFD_ASSERT (t->next->next == NULL);
+ if (!_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAY, 0)
+ || !_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAYSZ, 0))
+ return FALSE;
+ }
+ s = bfd_get_section_by_name (output_bfd, ".init_array");
+ if (s != NULL && s->linker_has_input)
+ {
+ if (!_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAY, 0)
+ || !_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAYSZ, 0))
+ return FALSE;
+ }
+ s = bfd_get_section_by_name (output_bfd, ".fini_array");
+ if (s != NULL && s->linker_has_input)
+ {
+ if (!_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAY, 0)
+ || !_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAYSZ, 0))
+ return FALSE;
+ }
- if (t->next != NULL && t->next->vernum != 0)
- def.vd_next = (sizeof (Elf_External_Verdef)
- + (cdeps + 1) * sizeof (Elf_External_Verdaux));
+ dynstr = bfd_get_linker_section (dynobj, ".dynstr");
+ /* If .dynstr is excluded from the link, we don't want any of
+ these tags. Strictly, we should be checking each section
+ individually; This quick check covers for the case where
+ someone does a /DISCARD/ : { *(*) }. */
+ if (dynstr != NULL && dynstr->output_section != bfd_abs_section_ptr)
+ {
+ bfd_size_type strsize;
- _bfd_elf_swap_verdef_out (output_bfd, &def,
- (Elf_External_Verdef *) p);
- p += sizeof (Elf_External_Verdef);
+ strsize = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
+ if ((info->emit_hash
+ && !_bfd_elf_add_dynamic_entry (info, DT_HASH, 0))
+ || (info->emit_gnu_hash
+ && !_bfd_elf_add_dynamic_entry (info, DT_GNU_HASH, 0))
+ || !_bfd_elf_add_dynamic_entry (info, DT_STRTAB, 0)
+ || !_bfd_elf_add_dynamic_entry (info, DT_SYMTAB, 0)
+ || !_bfd_elf_add_dynamic_entry (info, DT_STRSZ, strsize)
+ || !_bfd_elf_add_dynamic_entry (info, DT_SYMENT,
+ bed->s->sizeof_sym))
+ return FALSE;
+ }
+ }
- defaux.vda_name = h->dynstr_index;
- _bfd_elf_strtab_addref (elf_hash_table (info)->dynstr,
- h->dynstr_index);
- defaux.vda_next = 0;
- if (t->deps != NULL)
- defaux.vda_next = sizeof (Elf_External_Verdaux);
- t->name_indx = defaux.vda_name;
+ if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
+ return FALSE;
- _bfd_elf_swap_verdaux_out (output_bfd, &defaux,
- (Elf_External_Verdaux *) p);
- p += sizeof (Elf_External_Verdaux);
+ /* The backend must work out the sizes of all the other dynamic
+ sections. */
+ if (dynobj != NULL
+ && bed->elf_backend_size_dynamic_sections != NULL
+ && ! (*bed->elf_backend_size_dynamic_sections) (output_bfd, info))
+ return FALSE;
- for (n = t->deps; n != NULL; n = n->next)
- {
- if (n->version_needed == NULL)
- {
- /* This can happen if there was an error in the
- version script. */
- 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);
+ if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
+ {
+ unsigned long section_sym_count;
- _bfd_elf_swap_verdaux_out (output_bfd, &defaux,
- (Elf_External_Verdaux *) p);
- p += sizeof (Elf_External_Verdaux);
- }
- }
+ if (elf_tdata (output_bfd)->cverdefs)
+ {
+ unsigned int crefs = elf_tdata (output_bfd)->cverdefs;
if (!_bfd_elf_add_dynamic_entry (info, DT_VERDEF, 0)
- || !_bfd_elf_add_dynamic_entry (info, DT_VERDEFNUM, cdefs))
+ || !_bfd_elf_add_dynamic_entry (info, DT_VERDEFNUM, crefs))
return FALSE;
-
- elf_tdata (output_bfd)->cverdefs = cdefs;
}
if ((info->new_dtags && info->flags) || (info->flags & DF_STATIC_TLS))
return FALSE;
}
- /* Work out the size of the version reference section. */
-
- s = bfd_get_linker_section (dynobj, ".gnu.version_r");
- BFD_ASSERT (s != NULL);
- {
- struct elf_find_verdep_info sinfo;
-
- 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),
- _bfd_elf_link_find_version_dependencies,
- &sinfo);
- if (sinfo.failed)
- return FALSE;
-
- if (elf_tdata (output_bfd)->verref == NULL)
- s->flags |= SEC_EXCLUDE;
- else
- {
- Elf_Internal_Verneed *t;
- unsigned int size;
- unsigned int crefs;
- bfd_byte *p;
-
- /* Build the version dependency section. */
- 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->size = size;
- s->contents = (unsigned char *) bfd_alloc (output_bfd, s->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;
- size_t 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)
- : lbasename (t->vn_bfd->filename),
- FALSE);
- if (indx == (size_t) -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 == (size_t) -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 (!_bfd_elf_add_dynamic_entry (info, DT_VERNEED, 0)
- || !_bfd_elf_add_dynamic_entry (info, DT_VERNEEDNUM, crefs))
- return FALSE;
+ if (elf_tdata (output_bfd)->cverrefs)
+ {
+ unsigned int crefs = elf_tdata (output_bfd)->cverrefs;
- elf_tdata (output_bfd)->cverrefs = crefs;
- }
- }
+ if (!_bfd_elf_add_dynamic_entry (info, DT_VERNEED, 0)
+ || !_bfd_elf_add_dynamic_entry (info, DT_VERNEEDNUM, crefs))
+ return FALSE;
+ }
if ((elf_tdata (output_bfd)->cverrefs == 0
&& elf_tdata (output_bfd)->cverdefs == 0)
|| _bfd_elf_link_renumber_dynsyms (output_bfd, info,
§ion_sym_count) == 0)
{
+ asection *s;
+
s = bfd_get_linker_section (dynobj, ".gnu.version");
s->flags |= SEC_EXCLUDE;
}
struct elf_link_hash_table *htab;
/* Copy down any references that we may have already seen to the
- symbol which just became indirect if DIR isn't a hidden versioned
- symbol. */
+ symbol which just became indirect. */
if (dir->versioned != versioned_hidden)
- {
- dir->ref_dynamic |= ind->ref_dynamic;
- dir->ref_regular |= ind->ref_regular;
- dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
- dir->non_got_ref |= ind->non_got_ref;
- dir->needs_plt |= ind->needs_plt;
- dir->pointer_equality_needed |= ind->pointer_equality_needed;
- }
+ dir->ref_dynamic |= ind->ref_dynamic;
+ dir->ref_regular |= ind->ref_regular;
+ dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
+ dir->non_got_ref |= ind->non_got_ref;
+ dir->needs_plt |= ind->needs_plt;
+ dir->pointer_equality_needed |= ind->pointer_equality_needed;
if (ind->root.type != bfd_link_hash_indirect)
return;
asection *sec;
sec = p->u.indirect.section;
- esdi = elf_section_data (sec);
/* Mark all sections which are to be included in the
link. This will normally be every section. We need
if (sec->flags & SEC_MERGE)
merged = TRUE;
- if (esdo->this_hdr.sh_type == SHT_REL
- || esdo->this_hdr.sh_type == SHT_RELA)
- /* Some backends use reloc_count in relocation sections
- to count particular types of relocs. Of course,
- reloc sections themselves can't have relocations. */
- reloc_count = 0;
- else if (emit_relocs)
- {
- reloc_count = sec->reloc_count;
- if (bed->elf_backend_count_additional_relocs)
- {
- int c;
- c = (*bed->elf_backend_count_additional_relocs) (sec);
- additional_reloc_count += c;
- }
- }
- else if (bed->elf_backend_count_relocs)
- reloc_count = (*bed->elf_backend_count_relocs) (info, sec);
-
if (sec->rawsize > max_contents_size)
max_contents_size = sec->rawsize;
if (sec->size > max_contents_size)
max_contents_size = sec->size;
- /* We are interested in just local symbols, not all
- symbols. */
if (bfd_get_flavour (sec->owner) == bfd_target_elf_flavour
&& (sec->owner->flags & DYNAMIC) == 0)
{
size_t sym_count;
+ /* We are interested in just local symbols, not all
+ symbols. */
if (elf_bad_symtab (sec->owner))
sym_count = (elf_tdata (sec->owner)->symtab_hdr.sh_size
/ bed->s->sizeof_sym);
&& elf_symtab_shndx_list (sec->owner) != NULL)
max_sym_shndx_count = sym_count;
+ if (esdo->this_hdr.sh_type == SHT_REL
+ || esdo->this_hdr.sh_type == SHT_RELA)
+ /* Some backends use reloc_count in relocation sections
+ to count particular types of relocs. Of course,
+ reloc sections themselves can't have relocations. */
+ ;
+ else if (emit_relocs)
+ {
+ reloc_count = sec->reloc_count;
+ if (bed->elf_backend_count_additional_relocs)
+ {
+ int c;
+ c = (*bed->elf_backend_count_additional_relocs) (sec);
+ additional_reloc_count += c;
+ }
+ }
+ else if (bed->elf_backend_count_relocs)
+ reloc_count = (*bed->elf_backend_count_relocs) (info, sec);
+
+ esdi = elf_section_data (sec);
+
if ((sec->flags & SEC_RELOC) != 0)
{
size_t ext_size = 0;
Elf_Internal_Dyn dyn;
const char *name;
unsigned int type;
+ bfd_size_type sh_size;
+ bfd_vma sh_addr;
bed->s->swap_dyn_in (dynobj, dyncon, &dyn);
type = SHT_REL;
else
type = SHT_RELA;
- dyn.d_un.d_val = 0;
- dyn.d_un.d_ptr = 0;
+ sh_size = 0;
+ sh_addr = 0;
for (i = 1; i < elf_numsections (abfd); i++)
{
Elf_Internal_Shdr *hdr;
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_ptr == 0
- || hdr->sh_addr < dyn.d_un.d_ptr)
- dyn.d_un.d_ptr = hdr->sh_addr;
- }
+ sh_size += hdr->sh_size;
+ if (sh_addr == 0
+ || sh_addr > hdr->sh_addr)
+ sh_addr = hdr->sh_addr;
}
}
+
if (bed->dtrel_excludes_plt && htab->srelplt != NULL)
{
/* Don't count procedure linkage table relocs in the
overall reloc count. */
- if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ)
- dyn.d_un.d_val -= htab->srelplt->size;
+ sh_size -= htab->srelplt->size;
+ if (sh_size == 0)
+ /* If the size is zero, make the address zero too.
+ This is to avoid a glibc bug. If the backend
+ emits DT_RELA/DT_RELASZ even when DT_RELASZ is
+ zero, then we'll put DT_RELA at the end of
+ DT_JMPREL. glibc will interpret the end of
+ DT_RELA matching the end of DT_JMPREL as the
+ case where DT_RELA includes DT_JMPREL, and for
+ LD_BIND_NOW will decide that processing DT_RELA
+ will process the PLT relocs too. Net result:
+ No PLT relocs applied. */
+ sh_addr = 0;
+
/* If .rela.plt is the first .rela section, exclude
it from DT_RELA. */
- else if (dyn.d_un.d_ptr == (htab->srelplt->output_section->vma
- + htab->srelplt->output_offset))
- dyn.d_un.d_ptr += htab->srelplt->size;
+ else if (sh_addr == (htab->srelplt->output_section->vma
+ + htab->srelplt->output_offset))
+ sh_addr += htab->srelplt->size;
}
+
+ if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ)
+ dyn.d_un.d_val = sh_size;
+ else
+ dyn.d_un.d_ptr = sh_addr;
break;
}
bed->s->swap_dyn_out (dynobj, &dyn, dyncon);
return TRUE;
}
-/* Sweep symbols in swept sections. Called via elf_link_hash_traverse. */
-
-struct elf_gc_sweep_symbol_info
-{
- struct bfd_link_info *info;
- void (*hide_symbol) (struct bfd_link_info *, struct elf_link_hash_entry *,
- bfd_boolean);
-};
-
-static bfd_boolean
-elf_gc_sweep_symbol (struct elf_link_hash_entry *h, void *data)
-{
- if (!h->mark
- && (((h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak)
- && !((h->def_regular || ELF_COMMON_DEF_P (h))
- && h->root.u.def.section->gc_mark))
- || h->root.type == bfd_link_hash_undefined
- || h->root.type == bfd_link_hash_undefweak))
- {
- struct elf_gc_sweep_symbol_info *inf;
-
- inf = (struct elf_gc_sweep_symbol_info *) data;
- (*inf->hide_symbol) (inf->info, h, TRUE);
- h->def_regular = 0;
- h->ref_regular = 0;
- h->ref_regular_nonweak = 0;
- }
-
- return TRUE;
-}
-
/* The sweep phase of garbage collection. Remove all garbage sections. */
typedef bfd_boolean (*gc_sweep_hook_fn)
bfd *sub;
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
gc_sweep_hook_fn gc_sweep_hook = bed->gc_sweep_hook;
- unsigned long section_sym_count;
- struct elf_gc_sweep_symbol_info sweep_info;
for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
{
if (info->print_gc_sections && o->size != 0)
/* xgettext:c-format */
- _bfd_error_handler (_("Removing unused section '%s' in file '%B'"), sub, o->name);
+ _bfd_error_handler (_("Removing unused section '%s' in file '%B'"),
+ sub, o->name);
/* But we also have to update some of the relocation
info we collected before. */
}
}
- /* Remove the symbols that were in the swept sections from the dynamic
- symbol table. GCFIXME: Anyone know how to get them out of the
- static symbol table as well? */
- sweep_info.info = info;
- sweep_info.hide_symbol = bed->elf_backend_hide_symbol;
- elf_link_hash_traverse (elf_hash_table (info), elf_gc_sweep_symbol,
- &sweep_info);
-
- _bfd_elf_link_renumber_dynsyms (abfd, info, §ion_sym_count);
return TRUE;
}
&& ELF_ST_VISIBILITY (h->other) != STV_INTERNAL
&& ELF_ST_VISIBILITY (h->other) != STV_HIDDEN
&& (!bfd_link_executable (info)
+ || info->gc_keep_exported
|| info->export_dynamic
|| (h->dynamic
&& d != NULL
return FALSE;
/* Mark dynamically referenced symbols. */
- if (htab->dynamic_sections_created)
+ if (htab->dynamic_sections_created || info->gc_keep_exported)
elf_link_hash_traverse (htab, bed->gc_mark_dynamic_ref, info);
/* Grovel through relocs to find out who stays ... */