2001, 2002
Free Software Foundation, Inc.
-This file is part of GLD, the Gnu Linker.
+ This file is part of GLD, the Gnu Linker.
-GLD is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+ GLD is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
-GLD is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
+ GLD is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with GLD; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+ You should have received a copy of the GNU General Public License
+ along with GLD; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA. */
#include "bfd.h"
#include "sysdep.h"
#include "fnmatch.h"
#include "demangle.h"
+#ifndef offsetof
+#define offsetof(TYPE,MEMBER) ((size_t)&(((TYPE*)0)->MEMBER))
+#endif
+
/* FORWARDS */
static lang_statement_union_type *new_statement
PARAMS ((enum statement_enum, size_t, lang_statement_list_type *));
static void map_input_to_output_sections
PARAMS ((lang_statement_union_type *, const char *,
lang_output_section_statement_type *));
+static void strip_excluded_output_sections PARAMS ((void));
static void print_output_section_statement
PARAMS ((lang_output_section_statement_type *));
static void print_assignment
static void gc_section_callback
PARAMS ((lang_wild_statement_type *, struct wildcard_list *, asection *,
lang_input_statement_type *, PTR));
+static void lang_get_regions PARAMS ((struct memory_region_struct **,
+ struct memory_region_struct **,
+ const char *, const char *, int));
static void lang_record_phdrs PARAMS ((void));
static void lang_gc_wild PARAMS ((lang_wild_statement_type *));
static void lang_gc_sections_1 PARAMS ((lang_statement_union_type *));
lang_statement_list_type lang_output_section_statement;
lang_statement_list_type *stat_ptr = &statement_list;
lang_statement_list_type file_chain = { NULL, NULL };
-const char *entry_symbol = NULL;
+struct bfd_sym_chain entry_symbol = { NULL, NULL };
const char *entry_section = ".text";
boolean entry_from_cmdline;
boolean lang_has_input_file = false;
of having link once sections in the first place.
Also, not merging link once sections in a relocatable link
- causes trouble for MIPS ELF, which relies in link once semantics
+ causes trouble for MIPS ELF, which relies on link once semantics
to handle the .reginfo section correctly. */
name = bfd_get_section_name (abfd, sec);
section. */
sec->output_section = bfd_abs_section_ptr;
+ if (flags & SEC_GROUP)
+ bfd_discard_group (abfd, sec);
+
return;
}
}
flags &= ~ (SEC_MERGE | SEC_STRINGS);
}
+ /* For now make .tbss normal section. */
+ if ((flags & SEC_THREAD_LOCAL) && ! link_info.relocateable)
+ flags |= SEC_LOAD;
+
section->output_section->flags |= flags;
if (flags & SEC_MERGE)
return target;
}
+const char *
+lang_get_output_target ()
+{
+ const char *target;
+
+ /* Has the user told us which output format to use? */
+ if (output_target != (char *) NULL)
+ return output_target;
+
+ /* No - has the current target been set to something other than
+ the default? */
+ if (current_target != default_target)
+ return current_target;
+
+ /* No - can we determine the format of the first input file? */
+ target = get_first_input_target ();
+ if (target != NULL)
+ return target;
+
+ /* Failed - use the default output target. */
+ return default_target;
+}
+
/* Open the output file. */
static bfd *
{
bfd *output;
- /* Has the user told us which output format to use? */
- if (output_target == (char *) NULL)
- {
- /* No - has the current target been set to something other than
- the default? */
- if (current_target != default_target)
- output_target = current_target;
-
- /* No - can we determine the format of the first input file? */
- else
- {
- output_target = get_first_input_target ();
-
- /* Failed - use the default output target. */
- if (output_target == NULL)
- output_target = default_target;
- }
- }
+ output_target = lang_get_output_target ();
/* Has the user requested a particular endianness on the command
line? */
on a list, then, once the output file has been opened, transfer the
name to the symbol table. */
-typedef struct ldlang_undef_chain_list
-{
- struct ldlang_undef_chain_list *next;
- char *name;
-} ldlang_undef_chain_list_type;
+typedef struct bfd_sym_chain ldlang_undef_chain_list_type;
-static ldlang_undef_chain_list_type *ldlang_undef_chain_list_head;
+#define ldlang_undef_chain_list_head entry_symbol.next
void
ldlang_add_undef (name)
}
}
+/* An output section might have been removed after its statement was
+ added. For example, ldemul_before_allocation can remove dynamic
+ sections if they turn out to be not needed. Clean them up here. */
+
+static void
+strip_excluded_output_sections ()
+{
+ lang_statement_union_type *u;
+
+ for (u = lang_output_section_statement.head;
+ u != NULL;
+ u = u->output_section_statement.next)
+ {
+ lang_output_section_statement_type *os;
+ asection *s;
+
+ os = &u->output_section_statement;
+ s = os->bfd_section;
+ if (s != NULL && (s->flags & SEC_EXCLUDE) != 0)
+ {
+ asection **p;
+
+ os->bfd_section = NULL;
+
+ for (p = &output_bfd->sections; *p; p = &(*p)->next)
+ if (*p == s)
+ {
+ bfd_section_list_remove (output_bfd, p);
+ output_bfd->section_count--;
+ break;
+ }
+ }
+ }
+}
+
static void
print_output_section_statement (output_section_statement)
lang_output_section_statement_type *output_section_statement;
if (sec->spec.exclude_name_list != NULL)
{
name_list *tmp;
- minfo ("EXCLUDE_FILE ( %s", sec->spec.exclude_name_list->name);
+ minfo ("EXCLUDE_FILE(%s", sec->spec.exclude_name_list->name);
for (tmp = sec->spec.exclude_name_list->next; tmp; tmp = tmp->next)
- minfo (", %s", tmp->name);
- minfo (")");
+ minfo (" %s", tmp->name);
+ minfo (") ");
}
if (sec->spec.name != NULL)
minfo ("%s", sec->spec.name);
minfo ("*");
if (sec->spec.sorted)
minfo (")");
+ if (sec->next)
+ minfo (" ");
}
minfo (")");
/* Put the section within the requested block size, or
align at the block boundary. */
- after = ALIGN_N (os->bfd_section->vma
+ after = align_n (os->bfd_section->vma
+ os->bfd_section->_raw_size / opb,
- /* The coercion here is important, see ld.h. */
(bfd_vma) os->block_value);
if (bfd_is_abs_section (os->bfd_section))
ASSERT (after == os->bfd_section->vma);
+ else if ((os->bfd_section->flags & SEC_HAS_CONTENTS) == 0
+ && (os->bfd_section->flags & SEC_THREAD_LOCAL)
+ && ! link_info.relocateable)
+ os->bfd_section->_raw_size = 0;
else
os->bfd_section->_raw_size =
(after - os->bfd_section->vma) * opb;
if (os->lma_region == NULL && os->load_base == NULL)
os->lma_region = os->region;
- if (os->lma_region != NULL)
+ if (os->lma_region != NULL && os->lma_region != os->region)
{
- if (os->load_base != NULL)
- {
- einfo (_("%X%P: use an absolute load address or a load memory region, not both\n"));
- }
- else
- {
- /* Don't allocate twice. */
- if (os->lma_region != os->region)
- {
- /* Set load_base, which will be handled later. */
- os->load_base =
- exp_intop (os->lma_region->current);
- os->lma_region->current +=
- os->bfd_section->_raw_size / opb;
- os_region_check (os, os->lma_region, NULL,
- os->bfd_section->lma);
- }
- }
+ /* Set load_base, which will be handled later. */
+ os->load_base = exp_intop (os->lma_region->current);
+ os->lma_region->current +=
+ os->bfd_section->_raw_size / opb;
+ os_region_check (os, os->lma_region, NULL,
+ os->bfd_section->lma);
}
}
}
else
warn = true;
- if (entry_symbol == (char *) NULL)
+ if (entry_symbol.name == (const char *) NULL)
{
/* No entry has been specified. Look for start, but don't warn
if we don't find it. */
- entry_symbol = "start";
+ entry_symbol.name = "start";
warn = false;
}
- h = bfd_link_hash_lookup (link_info.hash, entry_symbol, false, false, true);
+ h = bfd_link_hash_lookup (link_info.hash, entry_symbol.name,
+ false, false, true);
if (h != (struct bfd_link_hash_entry *) NULL
&& (h->type == bfd_link_hash_defined
|| h->type == bfd_link_hash_defweak)
h->u.def.section->output_section)
+ h->u.def.section->output_offset);
if (! bfd_set_start_address (output_bfd, val))
- einfo (_("%P%F:%s: can't set start address\n"), entry_symbol);
+ einfo (_("%P%F:%s: can't set start address\n"), entry_symbol.name);
}
else
{
/* We couldn't find the entry symbol. Try parsing it as a
number. */
- val = bfd_scan_vma (entry_symbol, &send, 0);
+ val = bfd_scan_vma (entry_symbol.name, &send, 0);
if (*send == '\0')
{
if (! bfd_set_start_address (output_bfd, val))
{
if (warn)
einfo (_("%P: warning: cannot find entry symbol %s; defaulting to %V\n"),
- entry_symbol, bfd_get_section_vma (output_bfd, ts));
+ entry_symbol.name,
+ bfd_get_section_vma (output_bfd, ts));
if (! bfd_set_start_address (output_bfd,
bfd_get_section_vma (output_bfd,
ts)))
{
if (warn)
einfo (_("%P: warning: cannot find entry symbol %s; not setting start address\n"),
- entry_symbol);
+ entry_symbol.name);
}
}
}
section = h->u.c.p->section;
/* Increase the size of the section. */
- section->_cooked_size = ALIGN_N ((section->_cooked_size + opb - 1) / opb,
- (bfd_size_type) (1 << power_of_two)) * opb;
+ section->_cooked_size = align_n ((section->_cooked_size + opb - 1) / opb,
+ (bfd_vma) 1 << power_of_two) * opb;
/* Adjust the alignment if necessary. */
if (power_of_two > section->alignment_power)
lang_gc_sections ()
{
struct bfd_link_hash_entry *h;
- ldlang_undef_chain_list_type *ulist, fake_list_start;
+ ldlang_undef_chain_list_type *ulist;
/* Keep all sections so marked in the link script. */
lang_gc_sections_1 (statement_list.head);
- /* Keep all sections containing symbols undefined on the command-line.
- Handle the entry symbol at the same time. */
+ /* Keep all sections containing symbols undefined on the command-line,
+ and the section containing the entry symbol. */
- if (entry_symbol != NULL)
- {
- fake_list_start.next = ldlang_undef_chain_list_head;
- fake_list_start.name = (char *) entry_symbol;
- ulist = &fake_list_start;
- }
- else
- ulist = ldlang_undef_chain_list_head;
-
- for (; ulist; ulist = ulist->next)
+ for (ulist = link_info.gc_sym_list; ulist; ulist = ulist->next)
{
h = bfd_link_hash_lookup (link_info.hash, ulist->name,
false, false, false);
current_target = default_target;
open_input_bfds (statement_list.head, false);
+ link_info.gc_sym_list = &entry_symbol;
+ if (entry_symbol.name == NULL)
+ link_info.gc_sym_list = ldlang_undef_chain_list_head;
+
ldemul_after_open ();
already_linked_table_free ();
and other back-ends size dynamic sections. */
ldemul_before_allocation ();
+ if (!link_info.relocateable)
+ strip_excluded_output_sections ();
+
/* We must record the program headers before we try to fix the
section positions, since they will affect SIZEOF_HEADERS. */
lang_record_phdrs ();
(fill_type *) 0, (bfd_vma) 0);
/* Perform another relax pass - this time we know where the
- globals are, so can make better guess. */
+ globals are, so can make a better guess. */
lang_size_sections (statement_list.head,
abs_output_section,
- &(statement_list.head), 0, (bfd_vma) 0,
+ &statement_list.head, 0, (bfd_vma) 0,
&relax_again);
}
while (relax_again);
const char *name;
boolean cmdline;
{
- if (entry_symbol == NULL
+ if (entry_symbol.name == NULL
|| cmdline
|| ! entry_from_cmdline)
{
- entry_symbol = name;
+ entry_symbol.name = name;
entry_from_cmdline = cmdline;
}
}
lang_float_flag = maybe;
}
+
+/* Work out the load- and run-time regions from a script statement, and
+ store them in *LMA_REGION and *REGION respectively.
+
+ MEMSPEC is the name of the run-time region, or "*default*" if the
+ statement didn't specify one. LMA_MEMSPEC is the name of the
+ load-time region, or null if the statement didn't specify one.
+ HAVE_LMA_P is true if the statement had an explicit load address.
+
+ It is an error to specify both a load region and a load address. */
+
+static void
+lang_get_regions (region, lma_region, memspec, lma_memspec, have_lma_p)
+ struct memory_region_struct **region, **lma_region;
+ const char *memspec, *lma_memspec;
+ int have_lma_p;
+{
+ *lma_region = lang_memory_region_lookup (lma_memspec);
+
+ /* If no runtime region has been given, but the load region has
+ been, use the load region. */
+ if (lma_memspec != 0 && strcmp (memspec, "*default*") == 0)
+ *region = *lma_region;
+ else
+ *region = lang_memory_region_lookup (memspec);
+
+ if (have_lma_p && lma_memspec != 0)
+ einfo (_("%X%P:%S: section has both a load address and a load region\n"));
+}
+
void
lang_leave_output_section_statement (fill, memspec, phdrs, lma_memspec)
fill_type *fill;
struct lang_output_section_phdr_list *phdrs;
const char *lma_memspec;
{
+ lang_get_regions (¤t_section->region,
+ ¤t_section->lma_region,
+ memspec, lma_memspec,
+ current_section->load_base != 0);
current_section->fill = fill;
- current_section->region = lang_memory_region_lookup (memspec);
- current_section->lma_region = lang_memory_region_lookup (lma_memspec);
-
- /* If no runtime region has been given, but the load region has
- been, use the load region. */
- if (current_section->lma_region != 0 && strcmp (memspec, "*default*") == 0)
- current_section->region = current_section->lma_region;
-
current_section->phdrs = phdrs;
stat_ptr = &statement_list;
}
struct overlay_list *l;
struct lang_nocrossref *nocrossref;
- region = lang_memory_region_lookup (memspec);
- lma_region = lang_memory_region_lookup (lma_memspec);
+ lang_get_regions (®ion, &lma_region,
+ memspec, lma_memspec,
+ lma_expr != 0);
nocrossref = NULL;
ret = (struct bfd_elf_version_expr *) xmalloc (sizeof *ret);
ret->next = orig;
ret->pattern = new;
+ ret->symver = 0;
+ ret->script = 0;
if (lang == NULL || strcasecmp (lang, "C") == 0)
ret->match = lang_vers_match_lang_c;
ret->match = lang_vers_match_lang_c;
}
- return ret;
+ return ldemul_new_vers_pattern (ret);
}
/* This is called for each set of variable names and match