#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "bfdlink.h"
#include "genlink.h"
static bfd_boolean generic_link_add_object_symbols
(bfd *, struct bfd_link_info *, bfd_boolean collect);
static bfd_boolean generic_link_add_symbols
(bfd *, struct bfd_link_info *, bfd_boolean);
static bfd_boolean generic_link_check_archive_element_no_collect
(bfd *, struct bfd_link_info *, bfd_boolean *);
static bfd_boolean generic_link_check_archive_element_collect
(bfd *, struct bfd_link_info *, bfd_boolean *);
static bfd_boolean generic_link_check_archive_element
(bfd *, struct bfd_link_info *, bfd_boolean *, bfd_boolean);
static bfd_boolean generic_link_add_symbol_list
(bfd *, struct bfd_link_info *, bfd_size_type count, asymbol **,
bfd_boolean);
static bfd_boolean generic_add_output_symbol
(bfd *, size_t *psymalloc, asymbol *);
static bfd_boolean default_data_link_order
(bfd *, struct bfd_link_info *, asection *, struct bfd_link_order *);
static bfd_boolean default_indirect_link_order
(bfd *, struct bfd_link_info *, asection *, struct bfd_link_order *,
bfd_boolean);
struct bfd_hash_entry *
_bfd_link_hash_newfunc (struct bfd_hash_entry *entry,
struct bfd_hash_table *table,
const char *string)
{
if (entry == NULL)
{
entry = bfd_hash_allocate (table, sizeof (struct bfd_link_hash_entry));
if (entry == NULL)
return entry;
}
entry = bfd_hash_newfunc (entry, table, string);
if (entry)
{
struct bfd_link_hash_entry *h = (struct bfd_link_hash_entry *) entry;
h->type = bfd_link_hash_new;
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,
struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *,
struct bfd_hash_table *,
const char *))
{
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);
}
struct bfd_link_hash_entry *
bfd_link_hash_lookup (struct bfd_link_hash_table *table,
const char *string,
bfd_boolean create,
bfd_boolean copy,
bfd_boolean follow)
{
struct bfd_link_hash_entry *ret;
ret = ((struct bfd_link_hash_entry *)
bfd_hash_lookup (&table->table, string, create, copy));
if (follow && ret != NULL)
{
while (ret->type == bfd_link_hash_indirect
|| ret->type == bfd_link_hash_warning)
ret = ret->u.i.link;
}
return ret;
}
struct bfd_link_hash_entry *
bfd_wrapped_link_hash_lookup (bfd *abfd,
struct bfd_link_info *info,
const char *string,
bfd_boolean create,
bfd_boolean copy,
bfd_boolean follow)
{
bfd_size_type amt;
if (info->wrap_hash != NULL)
{
const char *l;
char prefix = '\0';
l = string;
if (*l == bfd_get_symbol_leading_char (abfd) || *l == info->wrap_char)
{
prefix = *l;
++l;
}
#undef WRAP
#define WRAP "__wrap_"
if (bfd_hash_lookup (info->wrap_hash, l, FALSE, FALSE) != NULL)
{
char *n;
struct bfd_link_hash_entry *h;
amt = strlen (l) + sizeof WRAP + 1;
n = bfd_malloc (amt);
if (n == NULL)
return NULL;
n[0] = prefix;
n[1] = '\0';
strcat (n, WRAP);
strcat (n, l);
h = bfd_link_hash_lookup (info->hash, n, create, TRUE, follow);
free (n);
return h;
}
#undef WRAP
#undef REAL
#define REAL "__real_"
if (*l == '_'
&& strncmp (l, REAL, sizeof REAL - 1) == 0
&& bfd_hash_lookup (info->wrap_hash, l + sizeof REAL - 1,
FALSE, FALSE) != NULL)
{
char *n;
struct bfd_link_hash_entry *h;
amt = strlen (l + sizeof REAL - 1) + 2;
n = bfd_malloc (amt);
if (n == NULL)
return NULL;
n[0] = prefix;
n[1] = '\0';
strcat (n, l + sizeof REAL - 1);
h = bfd_link_hash_lookup (info->hash, n, create, TRUE, follow);
free (n);
return h;
}
#undef REAL
}
return bfd_link_hash_lookup (info->hash, string, create, copy, follow);
}
void
bfd_link_hash_traverse
(struct bfd_link_hash_table *table,
bfd_boolean (*func) (struct bfd_link_hash_entry *, void *),
void *info)
{
bfd_hash_traverse (&table->table,
(bfd_boolean (*) (struct bfd_hash_entry *, void *)) func,
info);
}
void
bfd_link_add_undef (struct bfd_link_hash_table *table,
struct bfd_link_hash_entry *h)
{
BFD_ASSERT (h->u.undef.next == NULL);
if (table->undefs_tail != NULL)
table->undefs_tail->u.undef.next = h;
if (table->undefs == NULL)
table->undefs = h;
table->undefs_tail = h;
}
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
table->undefs_tail = (struct bfd_link_hash_entry *)
((char *) pun - ((char *) &h->u.undef.next - (char *) h));
break;
}
}
else
pun = &h->u.undef.next;
}
}
struct bfd_hash_entry *
_bfd_generic_link_hash_newfunc (struct bfd_hash_entry *entry,
struct bfd_hash_table *table,
const char *string)
{
if (entry == NULL)
{
entry =
bfd_hash_allocate (table, sizeof (struct generic_link_hash_entry));
if (entry == NULL)
return entry;
}
entry = _bfd_link_hash_newfunc (entry, table, string);
if (entry)
{
struct generic_link_hash_entry *ret;
ret = (struct generic_link_hash_entry *) entry;
ret->written = FALSE;
ret->sym = NULL;
}
return entry;
}
struct bfd_link_hash_table *
_bfd_generic_link_hash_table_create (bfd *abfd)
{
struct generic_link_hash_table *ret;
bfd_size_type amt = sizeof (struct generic_link_hash_table);
ret = bfd_malloc (amt);
if (ret == NULL)
return NULL;
if (! _bfd_link_hash_table_init (&ret->root, abfd,
_bfd_generic_link_hash_newfunc))
{
free (ret);
return NULL;
}
return &ret->root;
}
void
_bfd_generic_link_hash_table_free (struct bfd_link_hash_table *hash)
{
struct generic_link_hash_table *ret
= (struct generic_link_hash_table *) hash;
bfd_hash_table_free (&ret->root.table);
free (ret);
}
static bfd_boolean
generic_link_read_symbols (bfd *abfd)
{
if (bfd_get_outsymbols (abfd) == NULL)
{
long symsize;
long symcount;
symsize = bfd_get_symtab_upper_bound (abfd);
if (symsize < 0)
return FALSE;
bfd_get_outsymbols (abfd) = bfd_alloc (abfd, symsize);
if (bfd_get_outsymbols (abfd) == NULL && symsize != 0)
return FALSE;
symcount = bfd_canonicalize_symtab (abfd, bfd_get_outsymbols (abfd));
if (symcount < 0)
return FALSE;
bfd_get_symcount (abfd) = symcount;
}
return TRUE;
}
bfd_boolean
_bfd_generic_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
{
return generic_link_add_symbols (abfd, info, FALSE);
}
bfd_boolean
_bfd_generic_link_add_symbols_collect (bfd *abfd, struct bfd_link_info *info)
{
return generic_link_add_symbols (abfd, info, TRUE);
}
void
_bfd_generic_link_just_syms (asection *sec,
struct bfd_link_info *info ATTRIBUTE_UNUSED)
{
sec->output_section = bfd_abs_section_ptr;
sec->output_offset = sec->vma;
}
static bfd_boolean
generic_link_add_symbols (bfd *abfd,
struct bfd_link_info *info,
bfd_boolean collect)
{
bfd_boolean ret;
switch (bfd_get_format (abfd))
{
case bfd_object:
ret = generic_link_add_object_symbols (abfd, info, collect);
break;
case bfd_archive:
ret = (_bfd_generic_link_add_archive_symbols
(abfd, info,
(collect
? generic_link_check_archive_element_collect
: generic_link_check_archive_element_no_collect)));
break;
default:
bfd_set_error (bfd_error_wrong_format);
ret = FALSE;
}
return ret;
}
static bfd_boolean
generic_link_add_object_symbols (bfd *abfd,
struct bfd_link_info *info,
bfd_boolean collect)
{
bfd_size_type symcount;
struct bfd_symbol **outsyms;
if (! generic_link_read_symbols (abfd))
return FALSE;
symcount = _bfd_generic_link_get_symcount (abfd);
outsyms = _bfd_generic_link_get_symbols (abfd);
return generic_link_add_symbol_list (abfd, info, symcount, outsyms, collect);
}
struct archive_list
{
struct archive_list *next;
unsigned int indx;
};
struct archive_hash_entry
{
struct bfd_hash_entry root;
struct archive_list *defs;
};
struct archive_hash_table
{
struct bfd_hash_table table;
};
static struct bfd_hash_entry *
archive_hash_newfunc (struct bfd_hash_entry *entry,
struct bfd_hash_table *table,
const char *string)
{
struct archive_hash_entry *ret = (struct archive_hash_entry *) entry;
if (ret == NULL)
ret = bfd_hash_allocate (table, sizeof (struct archive_hash_entry));
if (ret == NULL)
return NULL;
ret = ((struct archive_hash_entry *)
bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
if (ret)
{
ret->defs = NULL;
}
return &ret->root;
}
static bfd_boolean
archive_hash_table_init
(struct archive_hash_table *table,
struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *,
struct bfd_hash_table *,
const char *))
{
return bfd_hash_table_init (&table->table, newfunc);
}
#define archive_hash_lookup(t, string, create, copy) \
((struct archive_hash_entry *) \
bfd_hash_lookup (&(t)->table, (string), (create), (copy)))
#define archive_hash_allocate(t, size) bfd_hash_allocate (&(t)->table, (size))
#define archive_hash_table_free(t) bfd_hash_table_free (&(t)->table)
bfd_boolean
_bfd_generic_link_add_archive_symbols
(bfd *abfd,
struct bfd_link_info *info,
bfd_boolean (*checkfn) (bfd *, struct bfd_link_info *, bfd_boolean *))
{
carsym *arsyms;
carsym *arsym_end;
register carsym *arsym;
int pass;
struct archive_hash_table arsym_hash;
unsigned int indx;
struct bfd_link_hash_entry **pundef;
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;
}
arsyms = bfd_ardata (abfd)->symdefs;
arsym_end = arsyms + bfd_ardata (abfd)->symdef_count;
if (! archive_hash_table_init (&arsym_hash, archive_hash_newfunc))
return FALSE;
for (arsym = arsyms, indx = 0; arsym < arsym_end; arsym++, indx++)
{
struct archive_hash_entry *arh;
struct archive_list *l, **pp;
arh = archive_hash_lookup (&arsym_hash, arsym->name, TRUE, FALSE);
if (arh == NULL)
goto error_return;
l = ((struct archive_list *)
archive_hash_allocate (&arsym_hash, sizeof (struct archive_list)));
if (l == NULL)
goto error_return;
l->indx = indx;
for (pp = &arh->defs; *pp != NULL; pp = &(*pp)->next)
;
*pp = l;
l->next = NULL;
}
pass = abfd->archive_pass + 1;
pundef = &info->hash->undefs;
while (*pundef != NULL)
{
struct bfd_link_hash_entry *h;
struct archive_hash_entry *arh;
struct archive_list *l;
h = *pundef;
if (h->type != bfd_link_hash_undefined
&& h->type != bfd_link_hash_common)
{
if (*pundef != info->hash->undefs_tail)
*pundef = (*pundef)->u.undef.next;
else
pundef = &(*pundef)->u.undef.next;
continue;
}
arh = archive_hash_lookup (&arsym_hash, h->root.string, FALSE, FALSE);
if (arh == NULL)
{
if (info->pei386_auto_import)
{
bfd_size_type amt = strlen (h->root.string) + 10;
char *buf = bfd_malloc (amt);
if (buf == NULL)
return FALSE;
sprintf (buf, "__imp_%s", h->root.string);
arh = archive_hash_lookup (&arsym_hash, buf, FALSE, FALSE);
free(buf);
}
if (arh == NULL)
{
pundef = &(*pundef)->u.undef.next;
continue;
}
}
for (l = arh->defs; l != NULL; l = l->next)
{
bfd *element;
bfd_boolean needed;
if (h->type != bfd_link_hash_undefined
&& h->type != bfd_link_hash_common)
break;
element = bfd_get_elt_at_index (abfd, l->indx);
if (element == NULL)
goto error_return;
if (element->archive_pass == -1
|| element->archive_pass == pass)
continue;
if (! bfd_check_format (element, bfd_object))
{
element->archive_pass = -1;
continue;
}
if (! (*checkfn) (element, info, &needed))
goto error_return;
if (! needed)
element->archive_pass = pass;
else
{
element->archive_pass = -1;
++pass;
}
}
pundef = &(*pundef)->u.undef.next;
}
archive_hash_table_free (&arsym_hash);
abfd->archive_pass = pass;
return TRUE;
error_return:
archive_hash_table_free (&arsym_hash);
return FALSE;
}
static bfd_boolean
generic_link_check_archive_element_no_collect (
bfd *abfd,
struct bfd_link_info *info,
bfd_boolean *pneeded)
{
return generic_link_check_archive_element (abfd, info, pneeded, FALSE);
}
static bfd_boolean
generic_link_check_archive_element_collect (bfd *abfd,
struct bfd_link_info *info,
bfd_boolean *pneeded)
{
return generic_link_check_archive_element (abfd, info, pneeded, TRUE);
}
static bfd_boolean
generic_link_check_archive_element (bfd *abfd,
struct bfd_link_info *info,
bfd_boolean *pneeded,
bfd_boolean collect)
{
asymbol **pp, **ppend;
*pneeded = FALSE;
if (! generic_link_read_symbols (abfd))
return FALSE;
pp = _bfd_generic_link_get_symbols (abfd);
ppend = pp + _bfd_generic_link_get_symcount (abfd);
for (; pp < ppend; pp++)
{
asymbol *p;
struct bfd_link_hash_entry *h;
p = *pp;
if (! bfd_is_com_section (p->section)
&& (p->flags & (BSF_GLOBAL | BSF_INDIRECT | BSF_WEAK)) == 0)
continue;
h = bfd_link_hash_lookup (info->hash, bfd_asymbol_name (p), FALSE,
FALSE, TRUE);
if (h == NULL
|| (h->type != bfd_link_hash_undefined
&& h->type != bfd_link_hash_common))
continue;
if (! bfd_is_com_section (p->section))
{
bfd_size_type symcount;
asymbol **symbols;
if (! (*info->callbacks->add_archive_element) (info, abfd,
bfd_asymbol_name (p)))
return FALSE;
symcount = _bfd_generic_link_get_symcount (abfd);
symbols = _bfd_generic_link_get_symbols (abfd);
if (! generic_link_add_symbol_list (abfd, info, symcount,
symbols, collect))
return FALSE;
*pneeded = TRUE;
return TRUE;
}
if (h->type == bfd_link_hash_undefined)
{
bfd *symbfd;
bfd_vma size;
unsigned int power;
symbfd = h->u.undef.abfd;
if (symbfd == NULL)
{
if (! (*info->callbacks->add_archive_element)
(info, abfd, bfd_asymbol_name (p)))
return FALSE;
*pneeded = TRUE;
return TRUE;
}
h->type = bfd_link_hash_common;
h->u.c.p =
bfd_hash_allocate (&info->hash->table,
sizeof (struct bfd_link_hash_common_entry));
if (h->u.c.p == NULL)
return FALSE;
size = bfd_asymbol_value (p);
h->u.c.size = size;
power = bfd_log2 (size);
if (power > 4)
power = 4;
h->u.c.p->alignment_power = power;
if (p->section == bfd_com_section_ptr)
h->u.c.p->section = bfd_make_section_old_way (symbfd, "COMMON");
else
h->u.c.p->section = bfd_make_section_old_way (symbfd,
p->section->name);
h->u.c.p->section->flags = SEC_ALLOC;
}
else
{
if (bfd_asymbol_value (p) > h->u.c.size)
h->u.c.size = bfd_asymbol_value (p);
}
}
return TRUE;
}
static bfd_boolean
generic_link_add_symbol_list (bfd *abfd,
struct bfd_link_info *info,
bfd_size_type symbol_count,
asymbol **symbols,
bfd_boolean collect)
{
asymbol **pp, **ppend;
pp = symbols;
ppend = symbols + symbol_count;
for (; pp < ppend; pp++)
{
asymbol *p;
p = *pp;
if ((p->flags & (BSF_INDIRECT
| BSF_WARNING
| BSF_GLOBAL
| BSF_CONSTRUCTOR
| BSF_WEAK)) != 0
|| bfd_is_und_section (bfd_get_section (p))
|| bfd_is_com_section (bfd_get_section (p))
|| bfd_is_ind_section (bfd_get_section (p)))
{
const char *name;
const char *string;
struct generic_link_hash_entry *h;
struct bfd_link_hash_entry *bh;
name = bfd_asymbol_name (p);
if (((p->flags & BSF_INDIRECT) != 0
|| bfd_is_ind_section (p->section))
&& pp + 1 < ppend)
{
pp++;
string = bfd_asymbol_name (*pp);
}
else if ((p->flags & BSF_WARNING) != 0
&& pp + 1 < ppend)
{
string = name;
pp++;
name = bfd_asymbol_name (*pp);
}
else
string = NULL;
bh = NULL;
if (! (_bfd_generic_link_add_one_symbol
(info, abfd, name, p->flags, bfd_get_section (p),
p->value, string, FALSE, collect, &bh)))
return FALSE;
h = (struct generic_link_hash_entry *) bh;
if ((p->flags & BSF_CONSTRUCTOR) != 0
&& (h == NULL || h->root.type == bfd_link_hash_new))
{
p->udata.p = NULL;
continue;
}
if (info->hash->creator == abfd->xvec)
{
if (h->sym == NULL
|| (! bfd_is_und_section (bfd_get_section (p))
&& (! bfd_is_com_section (bfd_get_section (p))
|| bfd_is_und_section (bfd_get_section (h->sym)))))
{
h->sym = p;
if (bfd_is_com_section (bfd_get_section (p)))
p->flags |= BSF_OLD_COMMON;
}
}
p->udata.p = h;
}
}
return TRUE;
}
enum link_row
{
UNDEF_ROW,
UNDEFW_ROW,
DEF_ROW,
DEFW_ROW,
COMMON_ROW,
INDR_ROW,
WARN_ROW,
SET_ROW
};
#undef FAIL
enum link_action
{
FAIL,
UND,
WEAK,
DEF,
DEFW,
COM,
REF,
CREF,
CDEF,
NOACT,
BIG,
MDEF,
MIND,
IND,
CIND,
SET,
MWARN,
WARN,
CWARN,
CYCLE,
REFC,
WARNC
};
static const enum link_action link_action[8][8] =
{
{UND, NOACT, UND, REF, REF, NOACT, REFC, WARNC },
{WEAK, NOACT, NOACT, REF, REF, NOACT, REFC, WARNC },
{DEF, DEF, DEF, MDEF, DEF, CDEF, MDEF, CYCLE },
{DEFW, DEFW, DEFW, NOACT, NOACT, NOACT, NOACT, CYCLE },
{COM, COM, COM, CREF, COM, BIG, REFC, WARNC },
{IND, IND, IND, MDEF, IND, CIND, MIND, CYCLE },
{MWARN, WARN, WARN, CWARN, CWARN, WARN, CWARN, NOACT },
{SET, SET, SET, SET, SET, SET, CYCLE, CYCLE }
};
static bfd *
hash_entry_bfd (struct bfd_link_hash_entry *h)
{
while (h->type == bfd_link_hash_warning)
h = h->u.i.link;
switch (h->type)
{
default:
return NULL;
case bfd_link_hash_undefined:
case bfd_link_hash_undefweak:
return h->u.undef.abfd;
case bfd_link_hash_defined:
case bfd_link_hash_defweak:
return h->u.def.section->owner;
case bfd_link_hash_common:
return h->u.c.p->section->owner;
}
}
bfd_boolean
_bfd_generic_link_add_one_symbol (struct bfd_link_info *info,
bfd *abfd,
const char *name,
flagword flags,
asection *section,
bfd_vma value,
const char *string,
bfd_boolean copy,
bfd_boolean collect,
struct bfd_link_hash_entry **hashp)
{
enum link_row row;
struct bfd_link_hash_entry *h;
bfd_boolean cycle;
if (bfd_is_ind_section (section)
|| (flags & BSF_INDIRECT) != 0)
row = INDR_ROW;
else if ((flags & BSF_WARNING) != 0)
row = WARN_ROW;
else if ((flags & BSF_CONSTRUCTOR) != 0)
row = SET_ROW;
else if (bfd_is_und_section (section))
{
if ((flags & BSF_WEAK) != 0)
row = UNDEFW_ROW;
else
row = UNDEF_ROW;
}
else if ((flags & BSF_WEAK) != 0)
row = DEFW_ROW;
else if (bfd_is_com_section (section))
row = COMMON_ROW;
else
row = DEF_ROW;
if (hashp != NULL && *hashp != NULL)
h = *hashp;
else
{
if (row == UNDEF_ROW || row == UNDEFW_ROW)
h = bfd_wrapped_link_hash_lookup (abfd, info, name, TRUE, copy, FALSE);
else
h = bfd_link_hash_lookup (info->hash, name, TRUE, copy, FALSE);
if (h == NULL)
{
if (hashp != NULL)
*hashp = NULL;
return FALSE;
}
}
if (info->notice_all
|| (info->notice_hash != NULL
&& bfd_hash_lookup (info->notice_hash, name, FALSE, FALSE) != NULL))
{
if (! (*info->callbacks->notice) (info, h->root.string, abfd, section,
value))
return FALSE;
}
if (hashp != NULL)
*hashp = h;
do
{
enum link_action action;
cycle = FALSE;
action = link_action[(int) row][(int) h->type];
switch (action)
{
case FAIL:
abort ();
case NOACT:
break;
case UND:
h->type = bfd_link_hash_undefined;
h->u.undef.abfd = abfd;
bfd_link_add_undef (info->hash, h);
break;
case WEAK:
h->type = bfd_link_hash_undefweak;
h->u.undef.abfd = abfd;
h->u.undef.weak = abfd;
break;
case CDEF:
BFD_ASSERT (h->type == bfd_link_hash_common);
if (! ((*info->callbacks->multiple_common)
(info, h->root.string,
h->u.c.p->section->owner, bfd_link_hash_common, h->u.c.size,
abfd, bfd_link_hash_defined, 0)))
return FALSE;
case DEF:
case DEFW:
{
enum bfd_link_hash_type oldtype;
oldtype = h->type;
if (action == DEFW)
h->type = bfd_link_hash_defweak;
else
h->type = bfd_link_hash_defined;
h->u.def.section = section;
h->u.def.value = value;
if (collect && name[0] == '_')
{
const char *s;
#define CONS_PREFIX "GLOBAL_"
#define CONS_PREFIX_LEN (sizeof CONS_PREFIX - 1)
s = name + 1;
while (*s == '_')
++s;
if (s[0] == 'G'
&& strncmp (s, CONS_PREFIX, CONS_PREFIX_LEN - 1) == 0)
{
char c;
c = s[CONS_PREFIX_LEN + 1];
if ((c == 'I' || c == 'D')
&& s[CONS_PREFIX_LEN] == s[CONS_PREFIX_LEN + 2])
{
if (oldtype == bfd_link_hash_defweak)
abort ();
if (! ((*info->callbacks->constructor)
(info, c == 'I',
h->root.string, abfd, section, value)))
return FALSE;
}
}
}
}
break;
case COM:
if (h->type == bfd_link_hash_new)
bfd_link_add_undef (info->hash, h);
h->type = bfd_link_hash_common;
h->u.c.p =
bfd_hash_allocate (&info->hash->table,
sizeof (struct bfd_link_hash_common_entry));
if (h->u.c.p == NULL)
return FALSE;
h->u.c.size = value;
{
unsigned int power;
power = bfd_log2 (value);
if (power > 4)
power = 4;
h->u.c.p->alignment_power = power;
}
if (section == bfd_com_section_ptr)
{
h->u.c.p->section = bfd_make_section_old_way (abfd, "COMMON");
h->u.c.p->section->flags = SEC_ALLOC;
}
else if (section->owner != abfd)
{
h->u.c.p->section = bfd_make_section_old_way (abfd,
section->name);
h->u.c.p->section->flags = SEC_ALLOC;
}
else
h->u.c.p->section = section;
break;
case REF:
if (h->u.undef.next == NULL && info->hash->undefs_tail != h)
h->u.undef.next = h;
break;
case BIG:
BFD_ASSERT (h->type == bfd_link_hash_common);
if (! ((*info->callbacks->multiple_common)
(info, h->root.string,
h->u.c.p->section->owner, bfd_link_hash_common, h->u.c.size,
abfd, bfd_link_hash_common, value)))
return FALSE;
if (value > h->u.c.size)
{
unsigned int power;
h->u.c.size = value;
power = bfd_log2 (value);
if (power > 4)
power = 4;
h->u.c.p->alignment_power = power;
if (section == bfd_com_section_ptr)
{
h->u.c.p->section
= bfd_make_section_old_way (abfd, "COMMON");
h->u.c.p->section->flags = SEC_ALLOC;
}
else if (section->owner != abfd)
{
h->u.c.p->section
= bfd_make_section_old_way (abfd, section->name);
h->u.c.p->section->flags = SEC_ALLOC;
}
else
h->u.c.p->section = section;
}
break;
case CREF:
{
bfd *obfd;
if (h->type == bfd_link_hash_defined
|| h->type == bfd_link_hash_defweak)
obfd = h->u.def.section->owner;
else
obfd = NULL;
if (! ((*info->callbacks->multiple_common)
(info, h->root.string, obfd, h->type, 0,
abfd, bfd_link_hash_common, value)))
return FALSE;
}
break;
case MIND:
if (strcmp (h->u.i.link->root.string, string) == 0)
break;
case MDEF:
if (!info->allow_multiple_definition)
{
asection *msec = NULL;
bfd_vma mval = 0;
switch (h->type)
{
case bfd_link_hash_defined:
msec = h->u.def.section;
mval = h->u.def.value;
break;
case bfd_link_hash_indirect:
msec = bfd_ind_section_ptr;
mval = 0;
break;
default:
abort ();
}
if (h->type == bfd_link_hash_defined
&& bfd_is_abs_section (msec)
&& bfd_is_abs_section (section)
&& value == mval)
break;
if (! ((*info->callbacks->multiple_definition)
(info, h->root.string, msec->owner, msec, mval,
abfd, section, value)))
return FALSE;
}
break;
case CIND:
BFD_ASSERT (h->type == bfd_link_hash_common);
if (! ((*info->callbacks->multiple_common)
(info, h->root.string,
h->u.c.p->section->owner, bfd_link_hash_common, h->u.c.size,
abfd, bfd_link_hash_indirect, 0)))
return FALSE;
case IND:
{
struct bfd_link_hash_entry *inh;
inh = bfd_wrapped_link_hash_lookup (abfd, info, string, TRUE,
copy, FALSE);
if (inh == NULL)
return FALSE;
if (inh->type == bfd_link_hash_indirect
&& inh->u.i.link == h)
{
(*_bfd_error_handler)
(_("%B: indirect symbol `%s' to `%s' is a loop"),
abfd, name, string);
bfd_set_error (bfd_error_invalid_operation);
return FALSE;
}
if (inh->type == bfd_link_hash_new)
{
inh->type = bfd_link_hash_undefined;
inh->u.undef.abfd = abfd;
bfd_link_add_undef (info->hash, inh);
}
if (h->type != bfd_link_hash_new)
{
row = UNDEF_ROW;
cycle = TRUE;
}
h->type = bfd_link_hash_indirect;
h->u.i.link = inh;
}
break;
case SET:
if (! (*info->callbacks->add_to_set) (info, h, BFD_RELOC_CTOR,
abfd, section, value))
return FALSE;
break;
case WARNC:
if (h->u.i.warning != NULL)
{
if (! (*info->callbacks->warning) (info, h->u.i.warning,
h->root.string, abfd,
NULL, 0))
return FALSE;
h->u.i.warning = NULL;
}
case CYCLE:
h = h->u.i.link;
cycle = TRUE;
break;
case REFC:
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 WARN:
if (! (*info->callbacks->warning) (info, string, h->root.string,
hash_entry_bfd (h), NULL, 0))
return FALSE;
break;
case CWARN:
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))
return FALSE;
break;
}
case MWARN:
{
struct bfd_link_hash_entry *sub;
sub = ((struct bfd_link_hash_entry *)
((*info->hash->table.newfunc)
(NULL, &info->hash->table, h->root.string)));
if (sub == NULL)
return FALSE;
*sub = *h;
sub->type = bfd_link_hash_warning;
sub->u.i.link = h;
if (! copy)
sub->u.i.warning = string;
else
{
char *w;
size_t len = strlen (string) + 1;
w = bfd_hash_allocate (&info->hash->table, len);
if (w == NULL)
return FALSE;
memcpy (w, string, len);
sub->u.i.warning = w;
}
bfd_hash_replace (&info->hash->table,
(struct bfd_hash_entry *) h,
(struct bfd_hash_entry *) sub);
if (hashp != NULL)
*hashp = sub;
}
break;
}
}
while (cycle);
return TRUE;
}
bfd_boolean
_bfd_generic_final_link (bfd *abfd, struct bfd_link_info *info)
{
bfd *sub;
asection *o;
struct bfd_link_order *p;
size_t outsymalloc;
struct generic_write_global_symbol_info wginfo;
bfd_get_outsymbols (abfd) = NULL;
bfd_get_symcount (abfd) = 0;
outsymalloc = 0;
for (o = abfd->sections; o != NULL; o = o->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 (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
if (! _bfd_generic_link_output_symbols (abfd, sub, info, &outsymalloc))
return FALSE;
wginfo.info = info;
wginfo.output_bfd = abfd;
wginfo.psymalloc = &outsymalloc;
_bfd_generic_link_hash_traverse (_bfd_generic_hash_table (info),
_bfd_generic_link_write_global_symbol,
&wginfo);
if (! generic_add_output_symbol (abfd, &outsymalloc, NULL))
return FALSE;
if (info->relocatable)
{
for (o = abfd->sections; o != NULL; o = o->next)
{
o->reloc_count = 0;
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)
++o->reloc_count;
else if (p->type == bfd_indirect_link_order)
{
asection *input_section;
bfd *input_bfd;
long relsize;
arelent **relocs;
asymbol **symbols;
long reloc_count;
input_section = p->u.indirect.section;
input_bfd = input_section->owner;
relsize = bfd_get_reloc_upper_bound (input_bfd,
input_section);
if (relsize < 0)
return FALSE;
relocs = bfd_malloc (relsize);
if (!relocs && relsize != 0)
return FALSE;
symbols = _bfd_generic_link_get_symbols (input_bfd);
reloc_count = bfd_canonicalize_reloc (input_bfd,
input_section,
relocs,
symbols);
free (relocs);
if (reloc_count < 0)
return FALSE;
BFD_ASSERT ((unsigned long) reloc_count
== input_section->reloc_count);
o->reloc_count += reloc_count;
}
}
if (o->reloc_count > 0)
{
bfd_size_type amt;
amt = o->reloc_count;
amt *= sizeof (arelent *);
o->orelocation = bfd_alloc (abfd, amt);
if (!o->orelocation)
return FALSE;
o->flags |= SEC_RELOC;
o->reloc_count = 0;
}
}
}
for (o = abfd->sections; o != NULL; o = o->next)
{
for (p = o->map_head.link_order; p != NULL; p = p->next)
{
switch (p->type)
{
case bfd_section_reloc_link_order:
case bfd_symbol_reloc_link_order:
if (! _bfd_generic_reloc_link_order (abfd, info, o, p))
return FALSE;
break;
case bfd_indirect_link_order:
if (! default_indirect_link_order (abfd, info, o, p, TRUE))
return FALSE;
break;
default:
if (! _bfd_default_link_order (abfd, info, o, p))
return FALSE;
break;
}
}
}
return TRUE;
}
static bfd_boolean
generic_add_output_symbol (bfd *output_bfd, size_t *psymalloc, asymbol *sym)
{
if (bfd_get_symcount (output_bfd) >= *psymalloc)
{
asymbol **newsyms;
bfd_size_type amt;
if (*psymalloc == 0)
*psymalloc = 124;
else
*psymalloc *= 2;
amt = *psymalloc;
amt *= sizeof (asymbol *);
newsyms = bfd_realloc (bfd_get_outsymbols (output_bfd), amt);
if (newsyms == NULL)
return FALSE;
bfd_get_outsymbols (output_bfd) = newsyms;
}
bfd_get_outsymbols (output_bfd) [bfd_get_symcount (output_bfd)] = sym;
if (sym != NULL)
++ bfd_get_symcount (output_bfd);
return TRUE;
}
bfd_boolean
_bfd_generic_link_output_symbols (bfd *output_bfd,
bfd *input_bfd,
struct bfd_link_info *info,
size_t *psymalloc)
{
asymbol **sym_ptr;
asymbol **sym_end;
if (! generic_link_read_symbols (input_bfd))
return FALSE;
if (info->create_object_symbols_section != NULL)
{
asection *sec;
for (sec = input_bfd->sections; sec != NULL; sec = sec->next)
{
if (sec->output_section == info->create_object_symbols_section)
{
asymbol *newsym;
newsym = bfd_make_empty_symbol (input_bfd);
if (!newsym)
return FALSE;
newsym->name = input_bfd->filename;
newsym->value = 0;
newsym->flags = BSF_LOCAL | BSF_FILE;
newsym->section = sec;
if (! generic_add_output_symbol (output_bfd, psymalloc,
newsym))
return FALSE;
break;
}
}
}
sym_ptr = _bfd_generic_link_get_symbols (input_bfd);
sym_end = sym_ptr + _bfd_generic_link_get_symcount (input_bfd);
for (; sym_ptr < sym_end; sym_ptr++)
{
asymbol *sym;
struct generic_link_hash_entry *h;
bfd_boolean output;
h = NULL;
sym = *sym_ptr;
if ((sym->flags & (BSF_INDIRECT
| BSF_WARNING
| BSF_GLOBAL
| BSF_CONSTRUCTOR
| BSF_WEAK)) != 0
|| bfd_is_und_section (bfd_get_section (sym))
|| bfd_is_com_section (bfd_get_section (sym))
|| bfd_is_ind_section (bfd_get_section (sym)))
{
if (sym->udata.p != NULL)
h = sym->udata.p;
else if ((sym->flags & BSF_CONSTRUCTOR) != 0)
{
h = NULL;
}
else if (bfd_is_und_section (bfd_get_section (sym)))
h = ((struct generic_link_hash_entry *)
bfd_wrapped_link_hash_lookup (output_bfd, info,
bfd_asymbol_name (sym),
FALSE, FALSE, TRUE));
else
h = _bfd_generic_link_hash_lookup (_bfd_generic_hash_table (info),
bfd_asymbol_name (sym),
FALSE, FALSE, TRUE);
if (h != NULL)
{
if (info->hash->creator == input_bfd->xvec)
{
if (h->sym != NULL)
*sym_ptr = sym = h->sym;
}
switch (h->root.type)
{
default:
case bfd_link_hash_new:
abort ();
case bfd_link_hash_undefined:
break;
case bfd_link_hash_undefweak:
sym->flags |= BSF_WEAK;
break;
case bfd_link_hash_indirect:
h = (struct generic_link_hash_entry *) h->root.u.i.link;
case bfd_link_hash_defined:
sym->flags |= BSF_GLOBAL;
sym->flags &=~ BSF_CONSTRUCTOR;
sym->value = h->root.u.def.value;
sym->section = h->root.u.def.section;
break;
case bfd_link_hash_defweak:
sym->flags |= BSF_WEAK;
sym->flags &=~ BSF_CONSTRUCTOR;
sym->value = h->root.u.def.value;
sym->section = h->root.u.def.section;
break;
case bfd_link_hash_common:
sym->value = h->root.u.c.size;
sym->flags |= BSF_GLOBAL;
if (! bfd_is_com_section (sym->section))
{
BFD_ASSERT (bfd_is_und_section (sym->section));
sym->section = bfd_com_section_ptr;
}
break;
}
}
}
if (info->strip == strip_all
|| (info->strip == strip_some
&& bfd_hash_lookup (info->keep_hash, bfd_asymbol_name (sym),
FALSE, FALSE) == NULL))
output = FALSE;
else if ((sym->flags & (BSF_GLOBAL | BSF_WEAK)) != 0)
{
if (bfd_asymbol_bfd (sym) == input_bfd
&& (sym->flags & BSF_NOT_AT_END) != 0)
output = TRUE;
else
output = FALSE;
}
else if (bfd_is_ind_section (sym->section))
output = FALSE;
else if ((sym->flags & BSF_DEBUGGING) != 0)
{
if (info->strip == strip_none)
output = TRUE;
else
output = FALSE;
}
else if (bfd_is_und_section (sym->section)
|| bfd_is_com_section (sym->section))
output = FALSE;
else if ((sym->flags & BSF_LOCAL) != 0)
{
if ((sym->flags & BSF_WARNING) != 0)
output = FALSE;
else
{
switch (info->discard)
{
default:
case discard_all:
output = FALSE;
break;
case discard_sec_merge:
output = TRUE;
if (info->relocatable
|| ! (sym->section->flags & SEC_MERGE))
break;
case discard_l:
if (bfd_is_local_label (input_bfd, sym))
output = FALSE;
else
output = TRUE;
break;
case discard_none:
output = TRUE;
break;
}
}
}
else if ((sym->flags & BSF_CONSTRUCTOR))
{
if (info->strip != strip_all)
output = TRUE;
else
output = FALSE;
}
else
abort ();
if (((sym->section->flags & SEC_HAS_CONTENTS) != 0
&& ! sym->section->linker_mark)
|| bfd_section_removed_from_list (output_bfd,
sym->section->output_section))
output = FALSE;
if (output)
{
if (! generic_add_output_symbol (output_bfd, psymalloc, sym))
return FALSE;
if (h != NULL)
h->written = TRUE;
}
}
return TRUE;
}
static void
set_symbol_from_hash (asymbol *sym, struct bfd_link_hash_entry *h)
{
switch (h->type)
{
default:
abort ();
break;
case bfd_link_hash_new:
if (sym->section != NULL)
{
BFD_ASSERT ((sym->flags & BSF_CONSTRUCTOR) != 0);
}
else
{
sym->flags |= BSF_CONSTRUCTOR;
sym->section = bfd_abs_section_ptr;
sym->value = 0;
}
break;
case bfd_link_hash_undefined:
sym->section = bfd_und_section_ptr;
sym->value = 0;
break;
case bfd_link_hash_undefweak:
sym->section = bfd_und_section_ptr;
sym->value = 0;
sym->flags |= BSF_WEAK;
break;
case bfd_link_hash_defined:
sym->section = h->u.def.section;
sym->value = h->u.def.value;
break;
case bfd_link_hash_defweak:
sym->flags |= BSF_WEAK;
sym->section = h->u.def.section;
sym->value = h->u.def.value;
break;
case bfd_link_hash_common:
sym->value = h->u.c.size;
if (sym->section == NULL)
sym->section = bfd_com_section_ptr;
else if (! bfd_is_com_section (sym->section))
{
BFD_ASSERT (bfd_is_und_section (sym->section));
sym->section = bfd_com_section_ptr;
}
break;
case bfd_link_hash_indirect:
case bfd_link_hash_warning:
break;
}
}
bfd_boolean
_bfd_generic_link_write_global_symbol (struct generic_link_hash_entry *h,
void *data)
{
struct generic_write_global_symbol_info *wginfo = data;
asymbol *sym;
if (h->root.type == bfd_link_hash_warning)
h = (struct generic_link_hash_entry *) h->root.u.i.link;
if (h->written)
return TRUE;
h->written = TRUE;
if (wginfo->info->strip == strip_all
|| (wginfo->info->strip == strip_some
&& bfd_hash_lookup (wginfo->info->keep_hash, h->root.root.string,
FALSE, FALSE) == NULL))
return TRUE;
if (h->sym != NULL)
sym = h->sym;
else
{
sym = bfd_make_empty_symbol (wginfo->output_bfd);
if (!sym)
return FALSE;
sym->name = h->root.root.string;
sym->flags = 0;
}
set_symbol_from_hash (sym, &h->root);
sym->flags |= BSF_GLOBAL;
if (! generic_add_output_symbol (wginfo->output_bfd, wginfo->psymalloc,
sym))
{
abort ();
}
return TRUE;
}
bfd_boolean
_bfd_generic_reloc_link_order (bfd *abfd,
struct bfd_link_info *info,
asection *sec,
struct bfd_link_order *link_order)
{
arelent *r;
if (! info->relocatable)
abort ();
if (sec->orelocation == NULL)
abort ();
r = bfd_alloc (abfd, sizeof (arelent));
if (r == NULL)
return FALSE;
r->address = link_order->offset;
r->howto = bfd_reloc_type_lookup (abfd, link_order->u.reloc.p->reloc);
if (r->howto == 0)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
if (link_order->type == bfd_section_reloc_link_order)
r->sym_ptr_ptr = link_order->u.reloc.p->u.section->symbol_ptr_ptr;
else
{
struct generic_link_hash_entry *h;
h = ((struct generic_link_hash_entry *)
bfd_wrapped_link_hash_lookup (abfd, info,
link_order->u.reloc.p->u.name,
FALSE, FALSE, TRUE));
if (h == NULL
|| ! h->written)
{
if (! ((*info->callbacks->unattached_reloc)
(info, link_order->u.reloc.p->u.name, NULL, NULL, 0)))
return FALSE;
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
r->sym_ptr_ptr = &h->sym;
}
if (! r->howto->partial_inplace)
r->addend = link_order->u.reloc.p->addend;
else
{
bfd_size_type size;
bfd_reloc_status_type rstat;
bfd_byte *buf;
bfd_boolean ok;
file_ptr loc;
size = bfd_get_reloc_size (r->howto);
buf = bfd_zmalloc (size);
if (buf == NULL)
return FALSE;
rstat = _bfd_relocate_contents (r->howto, abfd,
(bfd_vma) link_order->u.reloc.p->addend,
buf);
switch (rstat)
{
case bfd_reloc_ok:
break;
default:
case bfd_reloc_outofrange:
abort ();
case bfd_reloc_overflow:
if (! ((*info->callbacks->reloc_overflow)
(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),
r->howto->name, link_order->u.reloc.p->addend,
NULL, NULL, 0)))
{
free (buf);
return FALSE;
}
break;
}
loc = link_order->offset * bfd_octets_per_byte (abfd);
ok = bfd_set_section_contents (abfd, sec, buf, loc, size);
free (buf);
if (! ok)
return FALSE;
r->addend = 0;
}
sec->orelocation[sec->reloc_count] = r;
++sec->reloc_count;
return TRUE;
}
struct bfd_link_order *
bfd_new_link_order (bfd *abfd, asection *section)
{
bfd_size_type amt = sizeof (struct bfd_link_order);
struct bfd_link_order *new;
new = bfd_zalloc (abfd, amt);
if (!new)
return NULL;
new->type = bfd_undefined_link_order;
if (section->map_tail.link_order != NULL)
section->map_tail.link_order->next = new;
else
section->map_head.link_order = new;
section->map_tail.link_order = new;
return new;
}
bfd_boolean
_bfd_default_link_order (bfd *abfd,
struct bfd_link_info *info,
asection *sec,
struct bfd_link_order *link_order)
{
switch (link_order->type)
{
case bfd_undefined_link_order:
case bfd_section_reloc_link_order:
case bfd_symbol_reloc_link_order:
default:
abort ();
case bfd_indirect_link_order:
return default_indirect_link_order (abfd, info, sec, link_order,
FALSE);
case bfd_data_link_order:
return default_data_link_order (abfd, info, sec, link_order);
}
}
static bfd_boolean
default_data_link_order (bfd *abfd,
struct bfd_link_info *info ATTRIBUTE_UNUSED,
asection *sec,
struct bfd_link_order *link_order)
{
bfd_size_type size;
size_t fill_size;
bfd_byte *fill;
file_ptr loc;
bfd_boolean result;
BFD_ASSERT ((sec->flags & SEC_HAS_CONTENTS) != 0);
size = link_order->size;
if (size == 0)
return TRUE;
fill = link_order->u.data.contents;
fill_size = link_order->u.data.size;
if (fill_size != 0 && fill_size < size)
{
bfd_byte *p;
fill = bfd_malloc (size);
if (fill == NULL)
return FALSE;
p = fill;
if (fill_size == 1)
memset (p, (int) link_order->u.data.contents[0], (size_t) size);
else
{
do
{
memcpy (p, link_order->u.data.contents, fill_size);
p += fill_size;
size -= fill_size;
}
while (size >= fill_size);
if (size != 0)
memcpy (p, link_order->u.data.contents, (size_t) size);
size = link_order->size;
}
}
loc = link_order->offset * bfd_octets_per_byte (abfd);
result = bfd_set_section_contents (abfd, sec, fill, loc, size);
if (fill != link_order->u.data.contents)
free (fill);
return result;
}
static bfd_boolean
default_indirect_link_order (bfd *output_bfd,
struct bfd_link_info *info,
asection *output_section,
struct bfd_link_order *link_order,
bfd_boolean generic_linker)
{
asection *input_section;
bfd *input_bfd;
bfd_byte *contents = NULL;
bfd_byte *new_contents;
bfd_size_type sec_size;
file_ptr loc;
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;
BFD_ASSERT (input_section->output_section == output_section);
BFD_ASSERT (input_section->output_offset == link_order->offset);
BFD_ASSERT (input_section->size == link_order->size);
if (info->relocatable
&& input_section->reloc_count > 0
&& output_section->orelocation == NULL)
{
(*_bfd_error_handler)
(_("Attempt to do relocatable link with %s input and %s output"),
bfd_get_target (input_bfd), bfd_get_target (output_bfd));
bfd_set_error (bfd_error_wrong_format);
return FALSE;
}
if (! generic_linker)
{
asymbol **sympp;
asymbol **symppend;
if (! generic_link_read_symbols (input_bfd))
return FALSE;
sympp = _bfd_generic_link_get_symbols (input_bfd);
symppend = sympp + _bfd_generic_link_get_symcount (input_bfd);
for (; sympp < symppend; sympp++)
{
asymbol *sym;
struct bfd_link_hash_entry *h;
sym = *sympp;
if ((sym->flags & (BSF_INDIRECT
| BSF_WARNING
| BSF_GLOBAL
| BSF_CONSTRUCTOR
| BSF_WEAK)) != 0
|| bfd_is_und_section (bfd_get_section (sym))
|| bfd_is_com_section (bfd_get_section (sym))
|| bfd_is_ind_section (bfd_get_section (sym)))
{
if (sym->udata.p != NULL)
h = 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),
FALSE, FALSE, TRUE);
else
h = bfd_link_hash_lookup (info->hash,
bfd_asymbol_name (sym),
FALSE, FALSE, TRUE);
if (h != NULL)
set_symbol_from_hash (sym, h);
}
}
}
sec_size = (input_section->rawsize > input_section->size
? input_section->rawsize
: input_section->size);
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;
loc = link_order->offset * bfd_octets_per_byte (output_bfd);
if (! bfd_set_section_contents (output_bfd, output_section,
new_contents, loc, link_order->size))
goto error_return;
if (contents != NULL)
free (contents);
return TRUE;
error_return:
if (contents != NULL)
free (contents);
return FALSE;
}
unsigned int
_bfd_count_link_order_relocs (struct bfd_link_order *link_order)
{
register unsigned int c;
register struct bfd_link_order *l;
c = 0;
for (l = link_order; l != NULL; l = l->next)
{
if (l->type == bfd_section_reloc_link_order
|| l->type == bfd_symbol_reloc_link_order)
++c;
}
return c;
}
bfd_boolean
_bfd_generic_link_split_section (bfd *abfd ATTRIBUTE_UNUSED,
asection *sec ATTRIBUTE_UNUSED)
{
return FALSE;
}
static struct bfd_hash_table _bfd_section_already_linked_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));
}
void
bfd_section_already_linked_table_insert
(struct bfd_section_already_linked_hash_entry *already_linked_list,
asection *sec)
{
struct bfd_section_already_linked *l;
l = bfd_hash_allocate (&_bfd_section_already_linked_table, sizeof *l);
l->sec = sec;
l->next = already_linked_list->entry;
already_linked_list->entry = l;
}
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 =
bfd_hash_allocate (table, sizeof *ret);
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, 42);
}
void
bfd_section_already_linked_table_free (void)
{
bfd_hash_table_free (&_bfd_section_already_linked_table);
}
void
_bfd_generic_section_already_linked (bfd *abfd, asection *sec)
{
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;
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);
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)
{
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:
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;
}
sec->output_section = bfd_abs_section_ptr;
sec->kept_section = l->sec;
return;
}
}
bfd_section_already_linked_table_insert (already_linked_list, sec);
}
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))
{
h->u.def.value += s->output_offset + s->output_section->vma;
h->u.def.section = bfd_abs_section_ptr;
}
}
return TRUE;
}
void
_bfd_fix_excluded_sec_syms (bfd *obfd, struct bfd_link_info *info)
{
bfd_link_hash_traverse (info->hash, fix_syms, obfd);
}