#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "bfdlink.h"
#include "genlink.h"
static bfd_boolean generic_link_read_symbols
PARAMS ((bfd *));
static bfd_boolean generic_link_add_symbols
PARAMS ((bfd *, struct bfd_link_info *, bfd_boolean collect));
static bfd_boolean generic_link_add_object_symbols
PARAMS ((bfd *, struct bfd_link_info *, bfd_boolean collect));
static bfd_boolean generic_link_check_archive_element_no_collect
PARAMS ((bfd *, struct bfd_link_info *, bfd_boolean *pneeded));
static bfd_boolean generic_link_check_archive_element_collect
PARAMS ((bfd *, struct bfd_link_info *, bfd_boolean *pneeded));
static bfd_boolean generic_link_check_archive_element
PARAMS ((bfd *, struct bfd_link_info *, bfd_boolean *pneeded,
bfd_boolean collect));
static bfd_boolean generic_link_add_symbol_list
PARAMS ((bfd *, struct bfd_link_info *, bfd_size_type count, asymbol **,
bfd_boolean collect));
static bfd *hash_entry_bfd
PARAMS ((struct bfd_link_hash_entry *));
static void set_symbol_from_hash
PARAMS ((asymbol *, struct bfd_link_hash_entry *));
static bfd_boolean generic_add_output_symbol
PARAMS ((bfd *, size_t *psymalloc, asymbol *));
static bfd_boolean default_data_link_order
PARAMS ((bfd *, struct bfd_link_info *, asection *,
struct bfd_link_order *));
static bfd_boolean default_indirect_link_order
PARAMS ((bfd *, struct bfd_link_info *, asection *,
struct bfd_link_order *, bfd_boolean));
struct bfd_hash_entry *
_bfd_link_hash_newfunc (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
const char *string;
{
if (entry == NULL)
{
entry = (struct bfd_hash_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;
h->next = NULL;
}
return entry;
}
bfd_boolean
_bfd_link_hash_table_init (table, abfd, newfunc)
struct bfd_link_hash_table *table;
bfd *abfd;
struct bfd_hash_entry *(*newfunc) PARAMS ((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 (table, string, create, copy, follow)
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 != (struct bfd_link_hash_entry *) 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 (abfd, info, string, create, copy, follow)
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;
l = string;
if (*l == bfd_get_symbol_leading_char (abfd))
++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 = (char *) bfd_malloc (amt);
if (n == NULL)
return NULL;
n[0] = bfd_get_symbol_leading_char (abfd);
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 = (char *) bfd_malloc (amt);
if (n == NULL)
return NULL;
n[0] = bfd_get_symbol_leading_char (abfd);
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 (table, func, info)
struct bfd_link_hash_table *table;
bfd_boolean (*func) PARAMS ((struct bfd_link_hash_entry *, PTR));
PTR info;
{
bfd_hash_traverse (&table->table,
((bfd_boolean (*) PARAMS ((struct bfd_hash_entry *, PTR)))
func),
info);
}
INLINE void
bfd_link_add_undef (table, h)
struct bfd_link_hash_table *table;
struct bfd_link_hash_entry *h;
{
BFD_ASSERT (h->next == NULL);
if (table->undefs_tail != (struct bfd_link_hash_entry *) NULL)
table->undefs_tail->next = h;
if (table->undefs == (struct bfd_link_hash_entry *) NULL)
table->undefs = h;
table->undefs_tail = h;
}
struct bfd_hash_entry *
_bfd_generic_link_hash_newfunc (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
const char *string;
{
if (entry == NULL)
{
entry = (struct bfd_hash_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 (abfd)
bfd *abfd;
{
struct generic_link_hash_table *ret;
bfd_size_type amt = sizeof (struct generic_link_hash_table);
ret = (struct generic_link_hash_table *) bfd_malloc (amt);
if (ret == NULL)
return (struct bfd_link_hash_table *) NULL;
if (! _bfd_link_hash_table_init (&ret->root, abfd,
_bfd_generic_link_hash_newfunc))
{
free (ret);
return (struct bfd_link_hash_table *) NULL;
}
return &ret->root;
}
void
_bfd_generic_link_hash_table_free (hash)
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 (abfd)
bfd *abfd;
{
if (bfd_get_outsymbols (abfd) == (asymbol **) NULL)
{
long symsize;
long symcount;
symsize = bfd_get_symtab_upper_bound (abfd);
if (symsize < 0)
return FALSE;
bfd_get_outsymbols (abfd) =
(asymbol **) bfd_alloc (abfd, (bfd_size_type) 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 (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
return generic_link_add_symbols (abfd, info, FALSE);
}
bfd_boolean
_bfd_generic_link_add_symbols_collect (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
return generic_link_add_symbols (abfd, info, TRUE);
}
void
_bfd_generic_link_just_syms (sec, info)
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 (abfd, info, collect)
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 (abfd, info, collect)
bfd *abfd;
struct bfd_link_info *info;
bfd_boolean collect;
{
bfd_size_type symcount;
struct symbol_cache_entry **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
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static bfd_boolean archive_hash_table_init
PARAMS ((struct archive_hash_table *,
struct bfd_hash_entry *(*) (struct bfd_hash_entry *,
struct bfd_hash_table *,
const char *)));
static struct bfd_hash_entry *
archive_hash_newfunc (entry, table, string)
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 == (struct archive_hash_entry *) NULL)
ret = ((struct archive_hash_entry *)
bfd_hash_allocate (table, sizeof (struct archive_hash_entry)));
if (ret == (struct archive_hash_entry *) NULL)
return NULL;
ret = ((struct archive_hash_entry *)
bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
if (ret)
{
ret->defs = (struct archive_list *) NULL;
}
return (struct bfd_hash_entry *) ret;
}
static bfd_boolean
archive_hash_table_init (table, newfunc)
struct archive_hash_table *table;
struct bfd_hash_entry *(*newfunc) PARAMS ((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 (abfd, info, checkfn)
bfd *abfd;
struct bfd_link_info *info;
bfd_boolean (*checkfn)
PARAMS ((bfd *, struct bfd_link_info *, bfd_boolean *pneeded));
{
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, (bfd *) 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 == (struct archive_hash_entry *) 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 != (struct archive_list *) NULL;
pp = &(*pp)->next)
;
*pp = l;
l->next = NULL;
}
pass = abfd->archive_pass + 1;
pundef = &info->hash->undefs;
while (*pundef != (struct bfd_link_hash_entry *) 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)->next;
else
pundef = &(*pundef)->next;
continue;
}
arh = archive_hash_lookup (&arsym_hash, h->root.string, FALSE, FALSE);
if (arh == (struct archive_hash_entry *) NULL)
{
if (info->pei386_auto_import)
{
bfd_size_type amt = strlen (h->root.string) + 10;
char *buf = (char *) 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 == (struct archive_hash_entry *) NULL)
{
pundef = &(*pundef)->next;
continue;
}
}
for (l = arh->defs; l != (struct archive_list *) 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 == (bfd *) 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)->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 (abfd, info, pneeded)
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 (abfd, info, pneeded)
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 (abfd, info, pneeded, collect)
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 == (struct bfd_link_hash_entry *) 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 == (bfd *) 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 =
((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)
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 (abfd, info, symbol_count, symbols, collect)
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 == (asymbol *) 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 = (PTR) 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 (h)
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 (info, abfd, name, flags, section, value,
string, copy, collect, hashp)
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 != (struct bfd_hash_table *) NULL
&& (bfd_hash_lookup (info->notice_hash, name, FALSE, FALSE)
!= (struct bfd_hash_entry *) NULL)))
{
if (! (*info->callbacks->notice) (info, h->root.string, abfd, section,
value))
return FALSE;
}
if (hashp != (struct bfd_link_hash_entry **) 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;
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, (bfd_vma) 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 =
((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)
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->next == NULL && info->hash->undefs_tail != h)
h->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, (bfd_vma) 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, (bfd_vma) 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 == (struct bfd_link_hash_entry *) NULL)
return FALSE;
if (inh->type == bfd_link_hash_indirect
&& inh->u.i.link == h)
{
(*_bfd_error_handler)
(_("%s: indirect symbol `%s' to `%s' is a loop"),
bfd_archive_filename (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,
(asection *) NULL,
(bfd_vma) 0))
return FALSE;
h->u.i.warning = NULL;
}
case CYCLE:
h = h->u.i.link;
cycle = TRUE;
break;
case REFC:
if (h->next == NULL && info->hash->undefs_tail != h)
h->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),
(asection *) NULL, (bfd_vma) 0))
return FALSE;
break;
case CWARN:
if (h->next != NULL || info->hash->undefs_tail == h)
{
if (! (*info->callbacks->warning) (info, string, h->root.string,
hash_entry_bfd (h),
(asection *) NULL,
(bfd_vma) 0))
return FALSE;
break;
}
case MWARN:
{
struct bfd_link_hash_entry *sub;
sub = ((struct bfd_link_hash_entry *)
((*info->hash->table.newfunc)
((struct bfd_hash_entry *) 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 (abfd, info)
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) = (asymbol **) NULL;
bfd_get_symcount (abfd) = 0;
outsymalloc = 0;
for (o = abfd->sections; o != NULL; o = o->next)
for (p = o->link_order_head; 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 != (bfd *) 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,
(PTR) &wginfo);
if (! generic_add_output_symbol (abfd, &outsymalloc, NULL))
return FALSE;
if (info->relocateable)
{
for (o = abfd->sections;
o != (asection *) NULL;
o = o->next)
{
o->reloc_count = 0;
for (p = o->link_order_head;
p != (struct bfd_link_order *) 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 = (arelent **) bfd_malloc ((bfd_size_type) 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);
if (reloc_count < 0)
return FALSE;
BFD_ASSERT ((unsigned long) reloc_count
== input_section->reloc_count);
o->reloc_count += reloc_count;
free (relocs);
}
}
if (o->reloc_count > 0)
{
bfd_size_type amt;
amt = o->reloc_count;
amt *= sizeof (arelent *);
o->orelocation = (arelent **) bfd_alloc (abfd, amt);
if (!o->orelocation)
return FALSE;
o->flags |= SEC_RELOC;
o->reloc_count = 0;
}
}
}
for (o = abfd->sections;
o != (asection *) NULL;
o = o->next)
{
for (p = o->link_order_head;
p != (struct bfd_link_order *) 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 (output_bfd, psymalloc, sym)
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 = (asymbol **) bfd_realloc (bfd_get_outsymbols (output_bfd), amt);
if (newsyms == (asymbol **) 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 (output_bfd, input_bfd, info, psymalloc)
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 != (asection *) NULL)
{
asection *sec;
for (sec = input_bfd->sections;
sec != (asection *) 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 = (struct generic_link_hash_entry *) 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 = (struct generic_link_hash_entry *) 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 != (struct generic_link_hash_entry *) NULL)
{
if (info->hash->creator == input_bfd->xvec)
{
if (h->sym != (asymbol *) 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)
== (struct bfd_hash_entry *) 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->relocateable
|| ! (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)
output = FALSE;
if (output)
{
if (! generic_add_output_symbol (output_bfd, psymalloc, sym))
return FALSE;
if (h != (struct generic_link_hash_entry *) NULL)
h->written = TRUE;
}
}
return TRUE;
}
static void
set_symbol_from_hash (sym, h)
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 (h, data)
struct generic_link_hash_entry *h;
PTR 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)
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 != (asymbol *) 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 (abfd, info, sec, link_order)
bfd *abfd;
struct bfd_link_info *info;
asection *sec;
struct bfd_link_order *link_order;
{
arelent *r;
if (! info->relocateable)
abort ();
if (sec->orelocation == (arelent **) NULL)
abort ();
r = (arelent *) bfd_alloc (abfd, (bfd_size_type) sizeof (arelent));
if (r == (arelent *) 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 == (struct generic_link_hash_entry *) NULL
|| ! h->written)
{
if (! ((*info->callbacks->unattached_reloc)
(info, link_order->u.reloc.p->u.name,
(bfd *) NULL, (asection *) NULL, (bfd_vma) 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_byte *) bfd_zmalloc (size);
if (buf == (bfd_byte *) 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,
(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,
(bfd *) NULL, (asection *) NULL, (bfd_vma) 0)))
{
free (buf);
return FALSE;
}
break;
}
loc = link_order->offset * bfd_octets_per_byte (abfd);
ok = bfd_set_section_contents (abfd, sec, (PTR) buf, loc,
(bfd_size_type) 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 (abfd, section)
bfd *abfd;
asection *section;
{
bfd_size_type amt = sizeof (struct bfd_link_order);
struct bfd_link_order *new;
new = (struct bfd_link_order *) bfd_zalloc (abfd, amt);
if (!new)
return NULL;
new->type = bfd_undefined_link_order;
if (section->link_order_tail != (struct bfd_link_order *) NULL)
section->link_order_tail->next = new;
else
section->link_order_head = new;
section->link_order_tail = new;
return new;
}
bfd_boolean
_bfd_default_link_order (abfd, info, sec, 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 (abfd, info, sec, 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_byte *) 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 (output_bfd, info, output_section, link_order,
generic_linker)
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->_cooked_size == link_order->size);
if (info->relocateable
&& input_section->reloc_count > 0
&& output_section->orelocation == (arelent **) NULL)
{
(*_bfd_error_handler)
(_("Attempt to do relocateable 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 = (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),
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 = bfd_section_size (input_bfd, input_section);
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->relocateable,
_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,
(PTR) 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 (link_order)
struct bfd_link_order *link_order;
{
register unsigned int c;
register struct bfd_link_order *l;
c = 0;
for (l = link_order; l != (struct bfd_link_order *) 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 (abfd, sec)
bfd *abfd ATTRIBUTE_UNUSED;
asection *sec ATTRIBUTE_UNUSED;
{
return FALSE;
}