/* linker.c -- BFD linker routines
- Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
+ Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
Written by Steve Chamberlain and Ian Lance Taylor, Cygnus Support
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
-#include "bfd.h"
#include "sysdep.h"
+#include "bfd.h"
#include "libbfd.h"
#include "bfdlink.h"
#include "genlink.h"
Sometimes the <<_bfd_link_add_symbols>> function must store
some information in the hash table entry to be used by the
- <<_bfd_final_link>> function. In such a case the <<creator>>
- field of the hash table must be checked to make sure that the
- hash table was created by an object file of the same format.
+ <<_bfd_final_link>> function. In such a case the output bfd
+ xvec must be checked to make sure that the hash table was
+ created by an object file of the same format.
The <<_bfd_final_link>> routine must be prepared to handle a
hash entry without any extra information added by the
initialization function.
See <<ecoff_link_add_externals>> for an example of how to
- check the <<creator>> field before saving information (in this
+ check the output bfd before saving information (in this
case, the ECOFF external symbol debugging information) in a
hash table entry.
of the <<bfd>> structure.
Each section in the output file will have a list of
- <<link_order>> structures attached to the <<link_order_head>>
+ <<link_order>> structures attached to the <<map_head.link_order>>
field (the <<link_order>> structure is defined in
<<bfdlink.h>>). These structures describe how to create the
contents of the output section in terms of the contents of
subclass. */
if (entry == NULL)
{
- entry = bfd_hash_allocate (table, sizeof (struct bfd_link_hash_entry));
+ entry = (struct bfd_hash_entry *)
+ bfd_hash_allocate (table, sizeof (struct bfd_link_hash_entry));
if (entry == NULL)
return entry;
}
/* Initialize the local fields. */
h->type = bfd_link_hash_new;
- h->next = NULL;
+ memset (&h->u.undef.next, 0,
+ (sizeof (struct bfd_link_hash_entry)
+ - offsetof (struct bfd_link_hash_entry, u.undef.next)));
}
return entry;
bfd_boolean
_bfd_link_hash_table_init
(struct bfd_link_hash_table *table,
- bfd *abfd,
+ bfd *abfd ATTRIBUTE_UNUSED,
struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *,
struct bfd_hash_table *,
- const char *))
+ const char *),
+ unsigned int entsize)
{
- table->creator = abfd->xvec;
table->undefs = NULL;
table->undefs_tail = NULL;
table->type = bfd_link_generic_hash_table;
- return bfd_hash_table_init (&table->table, newfunc);
+ return bfd_hash_table_init (&table->table, newfunc, entsize);
}
/* Look up a symbol in a link hash table. If follow is TRUE, we
if (info->wrap_hash != NULL)
{
const char *l;
+ char prefix = '\0';
l = string;
- if (*l == bfd_get_symbol_leading_char (abfd))
- ++l;
+ if (*l == bfd_get_symbol_leading_char (abfd) || *l == info->wrap_char)
+ {
+ prefix = *l;
+ ++l;
+ }
#undef WRAP
#define WRAP "__wrap_"
references to SYM with references to __wrap_SYM. */
amt = strlen (l) + sizeof WRAP + 1;
- n = bfd_malloc (amt);
+ n = (char *) bfd_malloc (amt);
if (n == NULL)
return NULL;
- /* Note that symbol_leading_char may be '\0'. */
- n[0] = bfd_get_symbol_leading_char (abfd);
+ n[0] = prefix;
n[1] = '\0';
strcat (n, WRAP);
strcat (n, l);
#undef WRAP
-#undef REAL
+#undef REAL
#define REAL "__real_"
if (*l == '_'
- && strncmp (l, REAL, sizeof REAL - 1) == 0
+ && CONST_STRNEQ (l, REAL)
&& bfd_hash_lookup (info->wrap_hash, l + sizeof REAL - 1,
FALSE, FALSE) != NULL)
{
with references to SYM. */
amt = strlen (l + sizeof REAL - 1) + 2;
- n = bfd_malloc (amt);
+ n = (char *) bfd_malloc (amt);
if (n == NULL)
return NULL;
- /* Note that symbol_leading_char may be '\0'. */
- n[0] = bfd_get_symbol_leading_char (abfd);
+ n[0] = prefix;
n[1] = '\0';
strcat (n, l + sizeof REAL - 1);
h = bfd_link_hash_lookup (info->hash, n, create, TRUE, follow);
bfd_link_add_undef (struct bfd_link_hash_table *table,
struct bfd_link_hash_entry *h)
{
- BFD_ASSERT (h->next == NULL);
+ BFD_ASSERT (h->u.undef.next == NULL);
if (table->undefs_tail != NULL)
- table->undefs_tail->next = h;
+ table->undefs_tail->u.undef.next = h;
if (table->undefs == NULL)
table->undefs = h;
table->undefs_tail = h;
}
+
+/* The undefs list was designed so that in normal use we don't need to
+ remove entries. However, if symbols on the list are changed from
+ bfd_link_hash_undefined to either bfd_link_hash_undefweak or
+ bfd_link_hash_new for some reason, then they must be removed from the
+ list. Failure to do so might result in the linker attempting to add
+ the symbol to the list again at a later stage. */
+
+void
+bfd_link_repair_undef_list (struct bfd_link_hash_table *table)
+{
+ struct bfd_link_hash_entry **pun;
+
+ pun = &table->undefs;
+ while (*pun != NULL)
+ {
+ struct bfd_link_hash_entry *h = *pun;
+
+ if (h->type == bfd_link_hash_new
+ || h->type == bfd_link_hash_undefweak)
+ {
+ *pun = h->u.undef.next;
+ h->u.undef.next = NULL;
+ if (h == table->undefs_tail)
+ {
+ if (pun == &table->undefs)
+ table->undefs_tail = NULL;
+ else
+ /* pun points at an u.undef.next field. Go back to
+ the start of the link_hash_entry. */
+ table->undefs_tail = (struct bfd_link_hash_entry *)
+ ((char *) pun - ((char *) &h->u.undef.next - (char *) h));
+ break;
+ }
+ }
+ else
+ pun = &h->u.undef.next;
+ }
+}
\f
/* Routine to create an entry in a generic link hash table. */
subclass. */
if (entry == NULL)
{
- entry =
+ entry = (struct bfd_hash_entry *)
bfd_hash_allocate (table, sizeof (struct generic_link_hash_entry));
if (entry == NULL)
return entry;
struct generic_link_hash_table *ret;
bfd_size_type amt = sizeof (struct generic_link_hash_table);
- ret = bfd_malloc (amt);
+ ret = (struct generic_link_hash_table *) bfd_malloc (amt);
if (ret == NULL)
return NULL;
if (! _bfd_link_hash_table_init (&ret->root, abfd,
- _bfd_generic_link_hash_newfunc))
+ _bfd_generic_link_hash_newfunc,
+ sizeof (struct generic_link_hash_entry)))
{
free (ret);
return NULL;
the hash table pointing to different instances of the symbol
structure. */
-static bfd_boolean
-generic_link_read_symbols (bfd *abfd)
+bfd_boolean
+bfd_generic_link_read_symbols (bfd *abfd)
{
if (bfd_get_outsymbols (abfd) == NULL)
{
symsize = bfd_get_symtab_upper_bound (abfd);
if (symsize < 0)
return FALSE;
- bfd_get_outsymbols (abfd) = bfd_alloc (abfd, symsize);
+ bfd_get_outsymbols (abfd) = (struct bfd_symbol **) bfd_alloc (abfd,
+ symsize);
if (bfd_get_outsymbols (abfd) == NULL && symsize != 0)
return FALSE;
symcount = bfd_canonicalize_symtab (abfd, bfd_get_outsymbols (abfd));
sec->output_offset = sec->vma;
}
+/* Copy the type of a symbol assiciated with a linker hast table entry.
+ Override this so that symbols created in linker scripts get their
+ type from the RHS of the assignment.
+ The default implementation does nothing. */
+void
+_bfd_generic_copy_link_hash_symbol_type (bfd *abfd ATTRIBUTE_UNUSED,
+ struct bfd_link_hash_entry * hdest ATTRIBUTE_UNUSED,
+ struct bfd_link_hash_entry * hsrc ATTRIBUTE_UNUSED)
+{
+}
+
/* Add symbols from an object file to the global hash table. */
static bfd_boolean
bfd_size_type symcount;
struct bfd_symbol **outsyms;
- if (! generic_link_read_symbols (abfd))
+ if (!bfd_generic_link_read_symbols (abfd))
return FALSE;
symcount = _bfd_generic_link_get_symcount (abfd);
outsyms = _bfd_generic_link_get_symbols (abfd);
/* Allocate the structure if it has not already been allocated by a
subclass. */
if (ret == NULL)
- ret = bfd_hash_allocate (table, sizeof (struct archive_hash_entry));
+ ret = (struct archive_hash_entry *)
+ bfd_hash_allocate (table, sizeof (struct archive_hash_entry));
if (ret == NULL)
return NULL;
(struct archive_hash_table *table,
struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *,
struct bfd_hash_table *,
- const char *))
+ const char *),
+ unsigned int entsize)
{
- return bfd_hash_table_init (&table->table, newfunc);
+ return bfd_hash_table_init (&table->table, newfunc, entsize);
}
/* Look up an entry in an archive hash table. */
/* In order to quickly determine whether an symbol is defined in
this archive, we build a hash table of the symbols. */
- if (! archive_hash_table_init (&arsym_hash, archive_hash_newfunc))
+ if (! archive_hash_table_init (&arsym_hash, archive_hash_newfunc,
+ sizeof (struct archive_hash_entry)))
return FALSE;
for (arsym = arsyms, indx = 0; arsym < arsym_end; arsym++, indx++)
{
us to lose track of whether the symbol has been
referenced). */
if (*pundef != info->hash->undefs_tail)
- *pundef = (*pundef)->next;
+ *pundef = (*pundef)->u.undef.next;
else
- pundef = &(*pundef)->next;
+ pundef = &(*pundef)->u.undef.next;
continue;
}
if (info->pei386_auto_import)
{
bfd_size_type amt = strlen (h->root.string) + 10;
- char *buf = bfd_malloc (amt);
+ char *buf = (char *) bfd_malloc (amt);
if (buf == NULL)
return FALSE;
}
if (arh == NULL)
{
- pundef = &(*pundef)->next;
+ pundef = &(*pundef)->u.undef.next;
continue;
}
}
}
}
- pundef = &(*pundef)->next;
+ pundef = &(*pundef)->u.undef.next;
}
archive_hash_table_free (&arsym_hash);
*pneeded = FALSE;
- if (! generic_link_read_symbols (abfd))
+ if (!bfd_generic_link_read_symbols (abfd))
return FALSE;
pp = _bfd_generic_link_get_symbols (abfd);
attached to symbfd to ensure that it is in a BFD which
will be linked in. */
h->type = bfd_link_hash_common;
- h->u.c.p =
+ h->u.c.p = (struct bfd_link_hash_common_entry *)
bfd_hash_allocate (&info->hash->table,
sizeof (struct bfd_link_hash_common_entry));
if (h->u.c.p == NULL)
struct generic_link_hash_entry *h;
struct bfd_link_hash_entry *bh;
- name = bfd_asymbol_name (p);
+ string = name = bfd_asymbol_name (p);
if (((p->flags & BSF_INDIRECT) != 0
|| bfd_is_ind_section (p->section))
&& pp + 1 < ppend)
{
/* The name of P is actually the warning string, and the
next symbol is the one to warn about. */
- string = name;
pp++;
name = bfd_asymbol_name (*pp);
}
- else
- string = NULL;
bh = NULL;
if (! (_bfd_generic_link_add_one_symbol
hash table other than the generic hash table, so we only
do this if we are certain that the hash table is a
generic one. */
- if (info->hash->creator == abfd->xvec)
+ if (info->output_bfd->xvec == abfd->xvec)
{
if (h->sym == NULL
|| (! bfd_is_und_section (bfd_get_section (p))
/* Make a new weak undefined symbol. */
h->type = bfd_link_hash_undefweak;
h->u.undef.abfd = abfd;
+ h->u.undef.weak = abfd;
break;
case CDEF:
s = name + 1;
while (*s == '_')
++s;
- if (s[0] == 'G'
- && strncmp (s, CONS_PREFIX, CONS_PREFIX_LEN - 1) == 0)
+ if (s[0] == 'G' && CONST_STRNEQ (s, CONS_PREFIX))
{
char c;
if (h->type == bfd_link_hash_new)
bfd_link_add_undef (info->hash, h);
h->type = bfd_link_hash_common;
- h->u.c.p =
+ h->u.c.p = (struct bfd_link_hash_common_entry *)
bfd_hash_allocate (&info->hash->table,
sizeof (struct bfd_link_hash_common_entry));
if (h->u.c.p == NULL)
case REF:
/* A reference to a defined symbol. */
- if (h->next == NULL && info->hash->undefs_tail != h)
- h->next = h;
+ if (h->u.undef.next == NULL && info->hash->undefs_tail != h)
+ h->u.undef.next = h;
break;
case BIG:
&& inh->u.i.link == h)
{
(*_bfd_error_handler)
- (_("%s: indirect symbol `%s' to `%s' is a loop"),
- bfd_archive_filename (abfd), name, string);
+ (_("%B: indirect symbol `%s' to `%s' is a loop"),
+ abfd, name, string);
bfd_set_error (bfd_error_invalid_operation);
return FALSE;
}
case REFC:
/* A reference to an indirect symbol. */
- if (h->next == NULL && info->hash->undefs_tail != h)
- h->next = h;
+ if (h->u.undef.next == NULL && info->hash->undefs_tail != h)
+ h->u.undef.next = h;
h = h->u.i.link;
cycle = TRUE;
break;
case CWARN:
/* Warn if this symbol has been referenced already,
otherwise add a warning. A symbol has been referenced if
- the next field is not NULL, or it is the tail of the
+ the u.undef.next field is not NULL, or it is the tail of the
undefined symbol list. The REF case above helps to
ensure this. */
- if (h->next != NULL || info->hash->undefs_tail == h)
+ if (h->u.undef.next != NULL || info->hash->undefs_tail == h)
{
if (! (*info->callbacks->warning) (info, string, h->root.string,
hash_entry_bfd (h), NULL, 0))
char *w;
size_t len = strlen (string) + 1;
- w = bfd_hash_allocate (&info->hash->table, len);
+ w = (char *) bfd_hash_allocate (&info->hash->table, len);
if (w == NULL)
return FALSE;
memcpy (w, string, len);
/* Mark all sections which will be included in the output file. */
for (o = abfd->sections; o != NULL; o = o->next)
- for (p = o->link_order_head; p != NULL; p = p->next)
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
if (p->type == bfd_indirect_link_order)
p->u.indirect.section->linker_mark = TRUE;
for (o = abfd->sections; o != NULL; o = o->next)
{
o->reloc_count = 0;
- for (p = o->link_order_head; p != NULL; p = p->next)
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
{
if (p->type == bfd_section_reloc_link_order
|| p->type == bfd_symbol_reloc_link_order)
input_section);
if (relsize < 0)
return FALSE;
- relocs = bfd_malloc (relsize);
+ relocs = (arelent **) bfd_malloc (relsize);
if (!relocs && relsize != 0)
return FALSE;
symbols = _bfd_generic_link_get_symbols (input_bfd);
amt = o->reloc_count;
amt *= sizeof (arelent *);
- o->orelocation = bfd_alloc (abfd, amt);
+ o->orelocation = (struct reloc_cache_entry **) bfd_alloc (abfd, amt);
if (!o->orelocation)
return FALSE;
o->flags |= SEC_RELOC;
/* Handle all the link order information for the sections. */
for (o = abfd->sections; o != NULL; o = o->next)
{
- for (p = o->link_order_head; p != NULL; p = p->next)
+ for (p = o->map_head.link_order; p != NULL; p = p->next)
{
switch (p->type)
{
*psymalloc *= 2;
amt = *psymalloc;
amt *= sizeof (asymbol *);
- newsyms = bfd_realloc (bfd_get_outsymbols (output_bfd), amt);
+ newsyms = (asymbol **) bfd_realloc (bfd_get_outsymbols (output_bfd), amt);
if (newsyms == NULL)
return FALSE;
bfd_get_outsymbols (output_bfd) = newsyms;
asymbol **sym_ptr;
asymbol **sym_end;
- if (! generic_link_read_symbols (input_bfd))
+ if (!bfd_generic_link_read_symbols (input_bfd))
return FALSE;
/* Create a filename symbol if we are supposed to. */
|| bfd_is_ind_section (bfd_get_section (sym)))
{
if (sym->udata.p != NULL)
- h = sym->udata.p;
+ h = (struct generic_link_hash_entry *) sym->udata.p;
else if ((sym->flags & BSF_CONSTRUCTOR) != 0)
{
/* This case normally means that the main linker code
this routine will be called with a hash table
other than a generic hash table, so we double
check that. */
- if (info->hash->creator == input_bfd->xvec)
+ if (info->output_bfd->xvec == input_bfd->xvec)
{
if (h->sym != NULL)
*sym_ptr = sym = h->sym;
abort ();
/* If this symbol is in a section which is not being included
- in the output file, then we don't want to output the symbol.
-
- Gross. .bss and similar sections won't have the linker_mark
- field set. */
- if ((sym->section->flags & SEC_HAS_CONTENTS) != 0
- && ! sym->section->linker_mark)
+ in the output file, then we don't want to output the
+ symbol. */
+ if (!bfd_is_abs_section (sym->section)
+ && bfd_section_removed_from_list (output_bfd,
+ sym->section->output_section))
output = FALSE;
if (output)
_bfd_generic_link_write_global_symbol (struct generic_link_hash_entry *h,
void *data)
{
- struct generic_write_global_symbol_info *wginfo = data;
+ struct generic_write_global_symbol_info *wginfo =
+ (struct generic_write_global_symbol_info *) data;
asymbol *sym;
if (h->root.type == bfd_link_hash_warning)
if (sec->orelocation == NULL)
abort ();
- r = bfd_alloc (abfd, sizeof (arelent));
+ r = (arelent *) bfd_alloc (abfd, sizeof (arelent));
if (r == NULL)
return FALSE;
file_ptr loc;
size = bfd_get_reloc_size (r->howto);
- buf = bfd_zmalloc (size);
+ buf = (bfd_byte *) bfd_zmalloc (size);
if (buf == NULL)
return FALSE;
rstat = _bfd_relocate_contents (r->howto, abfd,
abort ();
case bfd_reloc_overflow:
if (! ((*info->callbacks->reloc_overflow)
- (info,
+ (info, NULL,
(link_order->type == bfd_section_reloc_link_order
? bfd_section_name (abfd, link_order->u.reloc.p->u.section)
: link_order->u.reloc.p->u.name),
bfd_new_link_order (bfd *abfd, asection *section)
{
bfd_size_type amt = sizeof (struct bfd_link_order);
- struct bfd_link_order *new;
+ struct bfd_link_order *new_lo;
- new = bfd_zalloc (abfd, amt);
- if (!new)
+ new_lo = (struct bfd_link_order *) bfd_zalloc (abfd, amt);
+ if (!new_lo)
return NULL;
- new->type = bfd_undefined_link_order;
+ new_lo->type = bfd_undefined_link_order;
- if (section->link_order_tail != NULL)
- section->link_order_tail->next = new;
+ if (section->map_tail.link_order != NULL)
+ section->map_tail.link_order->next = new_lo;
else
- section->link_order_head = new;
- section->link_order_tail = new;
+ section->map_head.link_order = new_lo;
+ section->map_tail.link_order = new_lo;
- return new;
+ return new_lo;
}
/* Default link order processing routine. Note that we can not handle
if (fill_size != 0 && fill_size < size)
{
bfd_byte *p;
- fill = bfd_malloc (size);
+ fill = (bfd_byte *) bfd_malloc (size);
if (fill == NULL)
return FALSE;
p = fill;
BFD_ASSERT ((output_section->flags & SEC_HAS_CONTENTS) != 0);
- if (link_order->size == 0)
- return TRUE;
-
input_section = link_order->u.indirect.section;
input_bfd = input_section->owner;
+ if (input_section->size == 0)
+ return TRUE;
BFD_ASSERT (input_section->output_section == output_section);
BFD_ASSERT (input_section->output_offset == link_order->offset);
- BFD_ASSERT (input_section->_cooked_size == link_order->size);
+ BFD_ASSERT (input_section->size == link_order->size);
if (info->relocatable
&& input_section->reloc_count > 0
have retrieved them by this point, but we are being called by
a specific linker, presumably because we are linking
different types of object files together. */
- if (! generic_link_read_symbols (input_bfd))
+ if (!bfd_generic_link_read_symbols (input_bfd))
return FALSE;
/* Since we have been called by a specific linker, rather than
/* sym->udata may have been set by
generic_link_add_symbol_list. */
if (sym->udata.p != NULL)
- h = sym->udata.p;
+ h = (struct bfd_link_hash_entry *) sym->udata.p;
else if (bfd_is_und_section (bfd_get_section (sym)))
h = bfd_wrapped_link_hash_lookup (output_bfd, info,
bfd_asymbol_name (sym),
}
}
- /* Get and relocate the section contents. */
- sec_size = bfd_section_size (input_bfd, input_section);
- contents = bfd_malloc (sec_size);
- if (contents == NULL && sec_size != 0)
- goto error_return;
- new_contents = (bfd_get_relocated_section_contents
- (output_bfd, info, link_order, contents, info->relocatable,
- _bfd_generic_link_get_symbols (input_bfd)));
- if (!new_contents)
- goto error_return;
+ if ((output_section->flags & (SEC_GROUP | SEC_LINKER_CREATED)) == SEC_GROUP
+ && input_section->size != 0)
+ {
+ /* Group section contents are set by bfd_elf_set_group_contents. */
+ if (!output_bfd->output_has_begun)
+ {
+ /* FIXME: This hack ensures bfd_elf_set_group_contents is called. */
+ if (!bfd_set_section_contents (output_bfd, output_section, "", 0, 1))
+ goto error_return;
+ }
+ new_contents = output_section->contents;
+ BFD_ASSERT (new_contents != NULL);
+ BFD_ASSERT (input_section->output_offset == 0);
+ }
+ else
+ {
+ /* Get and relocate the section contents. */
+ sec_size = (input_section->rawsize > input_section->size
+ ? input_section->rawsize
+ : input_section->size);
+ contents = (bfd_byte *) bfd_malloc (sec_size);
+ if (contents == NULL && sec_size != 0)
+ goto error_return;
+ new_contents = (bfd_get_relocated_section_contents
+ (output_bfd, info, link_order, contents,
+ info->relocatable,
+ _bfd_generic_link_get_symbols (input_bfd)));
+ if (!new_contents)
+ goto error_return;
+ }
/* Output the section contents. */
- loc = link_order->offset * bfd_octets_per_byte (output_bfd);
+ loc = input_section->output_offset * bfd_octets_per_byte (output_bfd);
if (! bfd_set_section_contents (output_bfd, output_section,
- new_contents, loc, link_order->size))
+ new_contents, loc, input_section->size))
goto error_return;
if (contents != NULL)
{
return FALSE;
}
+
+/*
+FUNCTION
+ bfd_section_already_linked
+
+SYNOPSIS
+ void bfd_section_already_linked (bfd *abfd, asection *sec,
+ struct bfd_link_info *info);
+
+DESCRIPTION
+ Check if @var{sec} has been already linked during a reloceatable
+ or final link.
+
+.#define bfd_section_already_linked(abfd, sec, info) \
+. BFD_SEND (abfd, _section_already_linked, (abfd, sec, info))
+.
+
+*/
+
+/* Sections marked with the SEC_LINK_ONCE flag should only be linked
+ once into the output. This routine checks each section, and
+ arrange to discard it if a section of the same name has already
+ been linked. This code assumes that all relevant sections have the
+ SEC_LINK_ONCE flag set; that is, it does not depend solely upon the
+ section name. bfd_section_already_linked is called via
+ bfd_map_over_sections. */
+
+/* The hash table. */
+
+static struct bfd_hash_table _bfd_section_already_linked_table;
+
+/* Support routines for the hash table used by section_already_linked,
+ initialize the table, traverse, lookup, fill in an entry and remove
+ the table. */
+
+void
+bfd_section_already_linked_table_traverse
+ (bfd_boolean (*func) (struct bfd_section_already_linked_hash_entry *,
+ void *), void *info)
+{
+ bfd_hash_traverse (&_bfd_section_already_linked_table,
+ (bfd_boolean (*) (struct bfd_hash_entry *,
+ void *)) func,
+ info);
+}
+
+struct bfd_section_already_linked_hash_entry *
+bfd_section_already_linked_table_lookup (const char *name)
+{
+ return ((struct bfd_section_already_linked_hash_entry *)
+ bfd_hash_lookup (&_bfd_section_already_linked_table, name,
+ TRUE, FALSE));
+}
+
+bfd_boolean
+bfd_section_already_linked_table_insert
+ (struct bfd_section_already_linked_hash_entry *already_linked_list,
+ asection *sec)
+{
+ struct bfd_section_already_linked *l;
+
+ /* Allocate the memory from the same obstack as the hash table is
+ kept in. */
+ l = (struct bfd_section_already_linked *)
+ bfd_hash_allocate (&_bfd_section_already_linked_table, sizeof *l);
+ if (l == NULL)
+ return FALSE;
+ l->sec = sec;
+ l->next = already_linked_list->entry;
+ already_linked_list->entry = l;
+ return TRUE;
+}
+
+static struct bfd_hash_entry *
+already_linked_newfunc (struct bfd_hash_entry *entry ATTRIBUTE_UNUSED,
+ struct bfd_hash_table *table,
+ const char *string ATTRIBUTE_UNUSED)
+{
+ struct bfd_section_already_linked_hash_entry *ret =
+ (struct bfd_section_already_linked_hash_entry *)
+ bfd_hash_allocate (table, sizeof *ret);
+
+ if (ret == NULL)
+ return NULL;
+
+ ret->entry = NULL;
+
+ return &ret->root;
+}
+
+bfd_boolean
+bfd_section_already_linked_table_init (void)
+{
+ return bfd_hash_table_init_n (&_bfd_section_already_linked_table,
+ already_linked_newfunc,
+ sizeof (struct bfd_section_already_linked_hash_entry),
+ 42);
+}
+
+void
+bfd_section_already_linked_table_free (void)
+{
+ bfd_hash_table_free (&_bfd_section_already_linked_table);
+}
+
+/* This is used on non-ELF inputs. */
+
+void
+_bfd_generic_section_already_linked (bfd *abfd, asection *sec,
+ struct bfd_link_info *info)
+{
+ flagword flags;
+ const char *name;
+ struct bfd_section_already_linked *l;
+ struct bfd_section_already_linked_hash_entry *already_linked_list;
+
+ flags = sec->flags;
+ if ((flags & SEC_LINK_ONCE) == 0)
+ return;
+
+ /* FIXME: When doing a relocatable link, we may have trouble
+ copying relocations in other sections that refer to local symbols
+ in the section being discarded. Those relocations will have to
+ be converted somehow; as of this writing I'm not sure that any of
+ the backends handle that correctly.
+
+ It is tempting to instead not discard link once sections when
+ doing a relocatable link (technically, they should be discarded
+ whenever we are building constructors). However, that fails,
+ because the linker winds up combining all the link once sections
+ into a single large link once section, which defeats the purpose
+ of having link once sections in the first place. */
+
+ name = bfd_get_section_name (abfd, sec);
+
+ already_linked_list = bfd_section_already_linked_table_lookup (name);
+
+ for (l = already_linked_list->entry; l != NULL; l = l->next)
+ {
+ bfd_boolean skip = FALSE;
+ struct coff_comdat_info *s_comdat
+ = bfd_coff_get_comdat_section (abfd, sec);
+ struct coff_comdat_info *l_comdat
+ = bfd_coff_get_comdat_section (l->sec->owner, l->sec);
+
+ /* We may have 3 different sections on the list: group section,
+ comdat section and linkonce section. SEC may be a linkonce or
+ comdat section. We always ignore group section. For non-COFF
+ inputs, we also ignore comdat section.
+
+ FIXME: Is that safe to match a linkonce section with a comdat
+ section for COFF inputs? */
+ if ((l->sec->flags & SEC_GROUP) != 0)
+ skip = TRUE;
+ else if (bfd_get_flavour (abfd) == bfd_target_coff_flavour)
+ {
+ if (s_comdat != NULL
+ && l_comdat != NULL
+ && strcmp (s_comdat->name, l_comdat->name) != 0)
+ skip = TRUE;
+ }
+ else if (l_comdat != NULL)
+ skip = TRUE;
+
+ if (!skip)
+ {
+ /* The section has already been linked. See if we should
+ issue a warning. */
+ switch (flags & SEC_LINK_DUPLICATES)
+ {
+ default:
+ abort ();
+
+ case SEC_LINK_DUPLICATES_DISCARD:
+ break;
+
+ case SEC_LINK_DUPLICATES_ONE_ONLY:
+ (*_bfd_error_handler)
+ (_("%B: warning: ignoring duplicate section `%A'\n"),
+ abfd, sec);
+ break;
+
+ case SEC_LINK_DUPLICATES_SAME_CONTENTS:
+ /* FIXME: We should really dig out the contents of both
+ sections and memcmp them. The COFF/PE spec says that
+ the Microsoft linker does not implement this
+ correctly, so I'm not going to bother doing it
+ either. */
+ /* Fall through. */
+ case SEC_LINK_DUPLICATES_SAME_SIZE:
+ if (sec->size != l->sec->size)
+ (*_bfd_error_handler)
+ (_("%B: warning: duplicate section `%A' has different size\n"),
+ abfd, sec);
+ break;
+ }
+
+ /* Set the output_section field so that lang_add_section
+ does not create a lang_input_section structure for this
+ section. Since there might be a symbol in the section
+ being discarded, we must retain a pointer to the section
+ which we are really going to use. */
+ sec->output_section = bfd_abs_section_ptr;
+ sec->kept_section = l->sec;
+
+ return;
+ }
+ }
+
+ /* This is the first section with this name. Record it. */
+ if (! bfd_section_already_linked_table_insert (already_linked_list, sec))
+ info->callbacks->einfo (_("%F%P: already_linked_table: %E\n"));
+}
+
+/* Convert symbols in excluded output sections to use a kept section. */
+
+static bfd_boolean
+fix_syms (struct bfd_link_hash_entry *h, void *data)
+{
+ bfd *obfd = (bfd *) data;
+
+ if (h->type == bfd_link_hash_warning)
+ h = h->u.i.link;
+
+ if (h->type == bfd_link_hash_defined
+ || h->type == bfd_link_hash_defweak)
+ {
+ asection *s = h->u.def.section;
+ if (s != NULL
+ && s->output_section != NULL
+ && (s->output_section->flags & SEC_EXCLUDE) != 0
+ && bfd_section_removed_from_list (obfd, s->output_section))
+ {
+ asection *op, *op1;
+
+ h->u.def.value += s->output_offset + s->output_section->vma;
+
+ /* Find preceding kept section. */
+ for (op1 = s->output_section->prev; op1 != NULL; op1 = op1->prev)
+ if ((op1->flags & SEC_EXCLUDE) == 0
+ && !bfd_section_removed_from_list (obfd, op1))
+ break;
+
+ /* Find following kept section. Start at prev->next because
+ other sections may have been added after S was removed. */
+ if (s->output_section->prev != NULL)
+ op = s->output_section->prev->next;
+ else
+ op = s->output_section->owner->sections;
+ for (; op != NULL; op = op->next)
+ if ((op->flags & SEC_EXCLUDE) == 0
+ && !bfd_section_removed_from_list (obfd, op))
+ break;
+
+ /* Choose better of two sections, based on flags. The idea
+ is to choose a section that will be in the same segment
+ as S would have been if it was kept. */
+ if (op1 == NULL)
+ {
+ if (op == NULL)
+ op = bfd_abs_section_ptr;
+ }
+ else if (op == NULL)
+ op = op1;
+ else if (((op1->flags ^ op->flags)
+ & (SEC_ALLOC | SEC_THREAD_LOCAL | SEC_LOAD)) != 0)
+ {
+ if (((op->flags ^ s->flags)
+ & (SEC_ALLOC | SEC_THREAD_LOCAL)) != 0
+ /* We prefer to choose a loaded section. Section S
+ doesn't have SEC_LOAD set (it being excluded, that
+ part of the flag processing didn't happen) so we
+ can't compare that flag to those of OP and OP1. */
+ || ((op1->flags & SEC_LOAD) != 0
+ && (op->flags & SEC_LOAD) == 0))
+ op = op1;
+ }
+ else if (((op1->flags ^ op->flags) & SEC_READONLY) != 0)
+ {
+ if (((op->flags ^ s->flags) & SEC_READONLY) != 0)
+ op = op1;
+ }
+ else if (((op1->flags ^ op->flags) & SEC_CODE) != 0)
+ {
+ if (((op->flags ^ s->flags) & SEC_CODE) != 0)
+ op = op1;
+ }
+ else
+ {
+ /* Flags we care about are the same. Prefer the following
+ section if that will result in a positive valued sym. */
+ if (h->u.def.value < op->vma)
+ op = op1;
+ }
+
+ h->u.def.value -= op->vma;
+ h->u.def.section = op;
+ }
+ }
+
+ return TRUE;
+}
+
+void
+_bfd_fix_excluded_sec_syms (bfd *obfd, struct bfd_link_info *info)
+{
+ bfd_link_hash_traverse (info->hash, fix_syms, obfd);
+}
+
+/*
+FUNCTION
+ bfd_generic_define_common_symbol
+
+SYNOPSIS
+ bfd_boolean bfd_generic_define_common_symbol
+ (bfd *output_bfd, struct bfd_link_info *info,
+ struct bfd_link_hash_entry *h);
+
+DESCRIPTION
+ Convert common symbol @var{h} into a defined symbol.
+ Return TRUE on success and FALSE on failure.
+
+.#define bfd_define_common_symbol(output_bfd, info, h) \
+. BFD_SEND (output_bfd, _bfd_define_common_symbol, (output_bfd, info, h))
+.
+*/
+
+bfd_boolean
+bfd_generic_define_common_symbol (bfd *output_bfd,
+ struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ struct bfd_link_hash_entry *h)
+{
+ unsigned int power_of_two;
+ bfd_vma alignment, size;
+ asection *section;
+
+ BFD_ASSERT (h != NULL && h->type == bfd_link_hash_common);
+
+ size = h->u.c.size;
+ power_of_two = h->u.c.p->alignment_power;
+ section = h->u.c.p->section;
+
+ /* Increase the size of the section to align the common symbol.
+ The alignment must be a power of two. */
+ alignment = bfd_octets_per_byte (output_bfd) << power_of_two;
+ BFD_ASSERT (alignment != 0 && (alignment & -alignment) == alignment);
+ section->size += alignment - 1;
+ section->size &= -alignment;
+
+ /* Adjust the section's overall alignment if necessary. */
+ if (power_of_two > section->alignment_power)
+ section->alignment_power = power_of_two;
+
+ /* Change the symbol from common to defined. */
+ h->type = bfd_link_hash_defined;
+ h->u.def.section = section;
+ h->u.def.value = section->size;
+
+ /* Increase the size of the section. */
+ section->size += size;
+
+ /* Make sure the section is allocated in memory, and make sure that
+ it is no longer a common section. */
+ section->flags |= SEC_ALLOC;
+ section->flags &= ~SEC_IS_COMMON;
+ return TRUE;
+}
+
+/*
+FUNCTION
+ bfd_find_version_for_sym
+
+SYNOPSIS
+ struct bfd_elf_version_tree * bfd_find_version_for_sym
+ (struct bfd_elf_version_tree *verdefs,
+ const char *sym_name, bfd_boolean *hide);
+
+DESCRIPTION
+ Search an elf version script tree for symbol versioning
+ info and export / don't-export status for a given symbol.
+ Return non-NULL on success and NULL on failure; also sets
+ the output @samp{hide} boolean parameter.
+
+*/
+
+struct bfd_elf_version_tree *
+bfd_find_version_for_sym (struct bfd_elf_version_tree *verdefs,
+ const char *sym_name,
+ bfd_boolean *hide)
+{
+ struct bfd_elf_version_tree *t;
+ struct bfd_elf_version_tree *local_ver, *global_ver, *exist_ver;
+ struct bfd_elf_version_tree *star_local_ver, *star_global_ver;
+
+ local_ver = NULL;
+ global_ver = NULL;
+ star_local_ver = NULL;
+ star_global_ver = NULL;
+ exist_ver = NULL;
+ for (t = verdefs; t != NULL; t = t->next)
+ {
+ if (t->globals.list != NULL)
+ {
+ struct bfd_elf_version_expr *d = NULL;
+
+ while ((d = (*t->match) (&t->globals, d, sym_name)) != NULL)
+ {
+ if (d->literal || strcmp (d->pattern, "*") != 0)
+ global_ver = t;
+ else
+ star_global_ver = t;
+ if (d->symver)
+ exist_ver = t;
+ d->script = 1;
+ /* If the match is a wildcard pattern, keep looking for
+ a more explicit, perhaps even local, match. */
+ if (d->literal)
+ break;
+ }
+
+ if (d != NULL)
+ break;
+ }
+
+ if (t->locals.list != NULL)
+ {
+ struct bfd_elf_version_expr *d = NULL;
+
+ while ((d = (*t->match) (&t->locals, d, sym_name)) != NULL)
+ {
+ if (d->literal || strcmp (d->pattern, "*") != 0)
+ local_ver = t;
+ else
+ star_local_ver = t;
+ /* If the match is a wildcard pattern, keep looking for
+ a more explicit, perhaps even global, match. */
+ if (d->literal)
+ {
+ /* An exact match overrides a global wildcard. */
+ global_ver = NULL;
+ star_global_ver = NULL;
+ break;
+ }
+ }
+
+ if (d != NULL)
+ break;
+ }
+ }
+
+ if (global_ver == NULL && local_ver == NULL)
+ global_ver = star_global_ver;
+
+ if (global_ver != NULL)
+ {
+ /* If we already have a versioned symbol that matches the
+ node for this symbol, then we don't want to create a
+ duplicate from the unversioned symbol. Instead hide the
+ unversioned symbol. */
+ *hide = exist_ver == global_ver;
+ return global_ver;
+ }
+
+ if (local_ver == NULL)
+ local_ver = star_local_ver;
+
+ if (local_ver != NULL)
+ {
+ *hide = TRUE;
+ return local_ver;
+ }
+
+ return NULL;
+}