aboutsummaryrefslogtreecommitdiff
path: root/bfd/linker.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/linker.c')
-rw-r--r--bfd/linker.c261
1 files changed, 202 insertions, 59 deletions
diff --git a/bfd/linker.c b/bfd/linker.c
index 3c7f823..7606534 100644
--- a/bfd/linker.c
+++ b/bfd/linker.c
@@ -1,5 +1,5 @@
/* linker.c -- BFD linker routines
- Copyright (C) 1993, 94 Free Software Foundation, Inc.
+ Copyright (C) 1993, 1994 Free Software Foundation, Inc.
Written by Steve Chamberlain and Ian Lance Taylor, Cygnus Support
This file is part of BFD, the Binary File Descriptor library.
@@ -427,6 +427,8 @@ static boolean generic_link_check_archive_element
static boolean generic_link_add_symbol_list
PARAMS ((bfd *, struct bfd_link_info *, bfd_size_type count, asymbol **,
boolean collect));
+static void set_symbol_from_hash
+ PARAMS ((asymbol *, struct bfd_link_hash_entry *));
static boolean generic_add_output_symbol
PARAMS ((bfd *, size_t *psymalloc, asymbol *));
static boolean default_fill_link_order
@@ -434,7 +436,7 @@ static boolean default_fill_link_order
struct bfd_link_order *));
static boolean default_indirect_link_order
PARAMS ((bfd *, struct bfd_link_info *, asection *,
- struct bfd_link_order *));
+ struct bfd_link_order *, boolean));
/* The link hash table structure is defined in bfdlink.h. It provides
a base hash table which the backend specific hash tables are built
@@ -897,7 +899,10 @@ _bfd_generic_link_add_archive_symbols (abfd, info, checkfn)
l->next = NULL;
}
- pass = 1;
+ /* The archive_pass field in the archive itself is used to
+ initialize PASS, sine we may search the same archive multiple
+ times. */
+ pass = abfd->archive_pass + 1;
/* New undefined symbols are added to the end of the list, so we
only need to look through it once. */
@@ -987,6 +992,9 @@ _bfd_generic_link_add_archive_symbols (abfd, info, checkfn)
archive_hash_table_free (&arsym_hash);
+ /* Save PASS in case we are called again. */
+ abfd->archive_pass = pass;
+
return true;
error_return:
@@ -1089,6 +1097,8 @@ generic_link_check_archive_element (abfd, info, pneeded, collect)
if (h->type == bfd_link_hash_undefined)
{
bfd *symbfd;
+ bfd_vma size;
+ unsigned int power;
symbfd = h->u.undef.abfd;
if (symbfd == (bfd *) NULL)
@@ -1111,7 +1121,21 @@ generic_link_check_archive_element (abfd, info, pneeded, collect)
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.size = bfd_asymbol_value (p);
+
+ size = bfd_asymbol_value (p);
+ h->u.c.size = size;
+ if (h->u.c.size != size)
+ {
+ /* The size did not fit in the bitfield. */
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ power = bfd_log2 (size);
+ if (power > 4)
+ power = 4;
+ h->u.c.alignment_power = power;
+
if (p->section == bfd_com_section_ptr)
h->u.c.section = bfd_make_section_old_way (symbfd, "COMMON");
else
@@ -1218,8 +1242,10 @@ generic_link_add_symbol_list (abfd, info, symbol_count, symbols, collect)
/* Store a back pointer from the symbol to the hash
table entry for the benefit of relaxation code until
- it gets rewritten to not use asymbol structures. */
- p->udata = (PTR) h;
+ it gets rewritten to not use asymbol structures.
+ Setting this is also used to check whether these
+ symbols were set up by the generic linker. */
+ p->udata.p = (PTR) h;
}
}
}
@@ -1267,6 +1293,7 @@ enum link_action
MDEF, /* Multiple definition error. */
MIND, /* Multiple indirect symbols. */
IND, /* Make indirect symbol. */
+ CIND, /* Make indirect symbol from existing common symbol. */
SET, /* Add value to set. */
MWARN, /* Make warning symbol. */
WARN, /* Issue warning. */
@@ -1286,8 +1313,8 @@ static const enum link_action link_action[8][7] =
/* UNDEFW_ROW */ {WEAK, WEAK, NOACT, REF, NOACT, REFC, WARNC },
/* DEF_ROW */ {DEF, DEF, DEF, MDEF, CDEF, MDEF, CYCLE },
/* DEFW_ROW */ {DEF, DEF, DEF, NOACT, NOACT, NOACT, CYCLE },
- /* COMMON_ROW */ {COM, COM, COM, CREF, BIG, MDEF, WARNC },
- /* INDR_ROW */ {IND, IND, IND, MDEF, MDEF, MIND, CYCLE },
+ /* COMMON_ROW */ {COM, COM, COM, CREF, BIG, CREF, WARNC },
+ /* INDR_ROW */ {IND, IND, IND, MDEF, CIND, MIND, CYCLE },
/* WARN_ROW */ {MWARN, WARN, WARN, CWARN, WARN, CWARN, CYCLE },
/* SET_ROW */ {SET, SET, SET, SET, SET, CYCLE, CYCLE }
};
@@ -1491,6 +1518,34 @@ _bfd_generic_link_add_one_symbol (info, abfd, name, flags, section, value,
bfd_link_add_undef (info->hash, h);
h->type = bfd_link_hash_common;
h->u.c.size = value;
+ if (h->u.c.size != value)
+ {
+ /* The size did not fit in the bitfield. */
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ /* Select a default alignment based on the size. This may
+ be overridden by the caller. */
+ {
+ unsigned int power;
+
+ power = bfd_log2 (value);
+ if (power > 4)
+ power = 4;
+ h->u.c.alignment_power = power;
+ }
+
+ /* The section of a common symbol is only used if the common
+ symbol is actually allocated. It basically provides a
+ hook for the linker script to decide which output section
+ the common symbols should be put in. In most cases, the
+ section of a common symbol will be bfd_com_section_ptr,
+ the code here will choose a common symbol section named
+ "COMMON", and the linker script will contain *(COMMON) in
+ the appropriate place. A few targets use separate common
+ sections for small symbols, and they require special
+ handling. */
if (section == bfd_com_section_ptr)
{
h->u.c.section = bfd_make_section_old_way (abfd, "COMMON");
@@ -1522,18 +1577,37 @@ _bfd_generic_link_add_one_symbol (info, abfd, name, flags, section, value,
abfd, bfd_link_hash_common, value)))
return false;
if (value > h->u.c.size)
- h->u.c.size = value;
+ {
+ unsigned int power;
+
+ h->u.c.size = value;
+
+ /* Select a default alignment based on the size. This may
+ be overridden by the caller. */
+ power = bfd_log2 (value);
+ if (power > 4)
+ power = 4;
+ h->u.c.alignment_power = power;
+ }
break;
case CREF:
- /* We have found a common definition for a symbol which was
- already defined. */
- BFD_ASSERT (h->type == bfd_link_hash_defined);
- if (! ((*info->callbacks->multiple_common)
- (info, name,
- h->u.def.section->owner, bfd_link_hash_defined, (bfd_vma) 0,
- abfd, bfd_link_hash_common, value)))
- return false;
+ {
+ bfd *obfd;
+
+ /* We have found a common definition for a symbol which
+ was already defined. FIXME: It would nice if we could
+ report the BFD which defined an indirect symbol, but we
+ don't have anywhere to store the information. */
+ if (h->type == bfd_link_hash_defined)
+ obfd = h->u.def.section->owner;
+ else
+ obfd = NULL;
+ if (! ((*info->callbacks->multiple_common)
+ (info, name, obfd, h->type, (bfd_vma) 0,
+ abfd, bfd_link_hash_common, value)))
+ return false;
+ }
break;
case MIND:
@@ -1573,6 +1647,15 @@ _bfd_generic_link_add_one_symbol (info, abfd, name, flags, section, value,
}
break;
+ case CIND:
+ /* Create an indirect symbol from an existing common symbol. */
+ BFD_ASSERT (h->type == bfd_link_hash_common);
+ if (! ((*info->callbacks->multiple_common)
+ (info, name,
+ h->u.c.section->owner, bfd_link_hash_common, h->u.c.size,
+ abfd, bfd_link_hash_indirect, (bfd_vma) 0)))
+ return false;
+ /* Fall through. */
case IND:
/* Create an indirect symbol. */
{
@@ -1805,6 +1888,10 @@ _bfd_generic_final_link (abfd, info)
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;
@@ -2045,6 +2132,50 @@ _bfd_generic_link_output_symbols (output_bfd, input_bfd, info, psymalloc)
return true;
}
+/* Set the section and value of a generic BFD symbol based on a linker
+ hash table entry. */
+
+static void
+set_symbol_from_hash (sym, h)
+ asymbol *sym;
+ struct bfd_link_hash_entry *h;
+{
+ switch (h->type)
+ {
+ default:
+ case bfd_link_hash_new:
+ abort ();
+ case bfd_link_hash_undefined:
+ sym->section = bfd_und_section_ptr;
+ sym->value = 0;
+ break;
+ case bfd_link_hash_weak:
+ 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_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;
+ }
+ /* Do not set the section; see _bfd_generic_link_output_symbols. */
+ break;
+ case bfd_link_hash_indirect:
+ case bfd_link_hash_warning:
+ /* FIXME: What should we do here? */
+ break;
+ }
+}
+
/* Write out a global symbol, if it hasn't already been written out.
This is called for each symbol in the hash table. */
@@ -2082,40 +2213,7 @@ _bfd_generic_link_write_global_symbol (h, data)
sym->flags = 0;
}
- switch (h->root.type)
- {
- default:
- case bfd_link_hash_new:
- abort ();
- case bfd_link_hash_undefined:
- sym->section = bfd_und_section_ptr;
- sym->value = 0;
- break;
- case bfd_link_hash_weak:
- sym->section = bfd_und_section_ptr;
- sym->value = 0;
- sym->flags |= BSF_WEAK;
- break;
- case bfd_link_hash_defined:
- sym->section = h->root.u.def.section;
- sym->value = h->root.u.def.value;
- break;
- case bfd_link_hash_common:
- sym->value = h->root.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;
- }
- /* Do not set the section; see _bfd_generic_link_output_symbols. */
- break;
- case bfd_link_hash_indirect:
- case bfd_link_hash_warning:
- /* FIXME: What should we do here? */
- break;
- }
+ set_symbol_from_hash (sym, &h->root);
sym->flags |= BSF_GLOBAL;
@@ -2289,7 +2387,8 @@ _bfd_default_link_order (abfd, info, sec, link_order)
default:
abort ();
case bfd_indirect_link_order:
- return default_indirect_link_order (abfd, info, sec, link_order);
+ return default_indirect_link_order (abfd, info, sec, link_order,
+ false);
case bfd_fill_link_order:
return default_fill_link_order (abfd, info, sec, link_order);
case bfd_data_link_order:
@@ -2341,11 +2440,13 @@ default_fill_link_order (abfd, info, sec, link_order)
/* Default routine to handle a bfd_indirect_link_order. */
static boolean
-default_indirect_link_order (output_bfd, info, output_section, link_order)
+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;
+ boolean generic_linker;
{
asection *input_section;
bfd *input_bfd;
@@ -2376,12 +2477,54 @@ default_indirect_link_order (output_bfd, info, output_section, link_order)
abort ();
}
- /* Get the canonical symbols. The generic linker will always have
- retrieved them by this point, but we may be being called by a
- specific linker when linking different types of object files
- together. */
- if (! generic_link_read_symbols (input_bfd))
- return false;
+ if (! generic_linker)
+ {
+ asymbol **sympp;
+ asymbol **symppend;
+
+ /* Get the canonical symbols. The generic linker will always
+ 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))
+ return false;
+
+ /* Since we have been called by a specific linker, rather than
+ the generic linker, the values of the symbols will not be
+ right. They will be the values as seen in the input file,
+ not the values of the final link. We need to fix them up
+ before we can relocate the section. */
+ 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)))
+ {
+ /* sym->udata may have been set by
+ generic_link_add_symbol_list. */
+ if (sym->udata.p != NULL)
+ h = (struct bfd_link_hash_entry *) sym->udata.p;
+ else
+ h = bfd_link_hash_lookup (info->hash,
+ bfd_asymbol_name (sym),
+ false, false, true);
+ if (h != NULL)
+ set_symbol_from_hash (sym, h);
+ }
+ }
+ }
/* Get and relocate the section contents. */
contents = (bfd_byte *) malloc (bfd_section_size (input_bfd, input_section));