#include "ldemul.h"
#include "fnmatch.h"
#include "demangle.h"
+#include "hashtab.h"
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
static const char *output_target;
static lang_statement_list_type statement_list;
static struct lang_phdr *lang_phdr_list;
+static struct bfd_hash_table lang_definedness_table;
/* Forward declarations. */
static void exp_init_os (etree_type *);
static lang_input_statement_type *lookup_name (const char *);
static bfd_boolean load_symbols (lang_input_statement_type *,
lang_statement_list_type *);
+static struct bfd_hash_entry *lang_definedness_newfunc
+ (struct bfd_hash_entry *, struct bfd_hash_table *, const char *);
static void insert_undefined (const char *);
static void print_statement (lang_statement_union_type *,
lang_output_section_statement_type *);
struct lang_nocrossrefs *nocrossref_list;
struct unique_sections *unique_section_list;
static bfd_boolean ldlang_sysrooted_script = FALSE;
+int lang_statement_iteration = 0;
etree_type *base; /* Relocation base - or null */
lang_output_section_statement_lookup (BFD_ABS_SECTION_NAME);
abs_output_section->bfd_section = bfd_abs_section_ptr;
+
+ /* The value "3" is ad-hoc, somewhat related to the expected number of
+ DEFINED expressions in a linker script. For most default linker
+ scripts, there are none. Why a hash table then? Well, it's somewhat
+ simpler to re-use working machinery than using a linked list in terms
+ of code-complexity here in ld, besides the initialization which just
+ looks like other code here. */
+ if (bfd_hash_table_init_n (&lang_definedness_table,
+ lang_definedness_newfunc, 3) != TRUE)
+ einfo (_("%P%F: out of memory during initialization"));
+
+ /* Callers of exp_fold_tree need to increment this. */
+ lang_statement_iteration = 0;
}
/*----------------------------------------------------------------------
We maintain a list of all the regions here.
If no regions are specified in the script, then the default is used
- which is created when looked up to be the entire data space. */
+ which is created when looked up to be the entire data space.
+
+ If create is true we are creating a region inside a MEMORY block.
+ In this case it is probably an error to create a region that has
+ already been created. If we are not inside a MEMORY block it is
+ dubious to use an undeclared region name (except DEFAULT_MEMORY_REGION)
+ and so we issue a warning. */
static lang_memory_region_type *lang_memory_region_list;
static lang_memory_region_type **lang_memory_region_list_tail = &lang_memory_region_list;
lang_memory_region_type *
-lang_memory_region_lookup (const char *const name)
+lang_memory_region_lookup (const char *const name, bfd_boolean create)
{
lang_memory_region_type *p;
lang_memory_region_type *new;
return NULL;
for (p = lang_memory_region_list; p != NULL; p = p->next)
- {
- if (strcmp (p->name, name) == 0)
+ if (strcmp (p->name, name) == 0)
+ {
+ if (create)
+ einfo (_("%P:%S: warning: redeclaration of memory region '%s'\n"), name);
return p;
- }
+ }
#if 0
/* This code used to always use the first region in the list as the
NOLOAD sections to work reasonably without requiring a region.
People should specify what region they mean, if they really want
a region. */
- if (strcmp (name, "*default*") == 0)
+ if (strcmp (name, DEFAULT_MEMORY_REGION) == 0)
{
if (lang_memory_region_list != NULL)
return lang_memory_region_list;
}
#endif
+ if (!create && strcmp (name, DEFAULT_MEMORY_REGION))
+ einfo (_("%P:%S: warning: memory region %s not declared\n"), name);
+
new = stat_alloc (sizeof (lang_memory_region_type));
new->name = xstrdup (name);
return p;
}
}
- return lang_memory_region_lookup ("*default*");
+ return lang_memory_region_lookup (DEFAULT_MEMORY_REGION, FALSE);
}
lang_output_section_statement_type *
#endif
}
+/* Add a symbol to a hash of symbols used in DEFINED (NAME) expressions. */
+
+void
+lang_track_definedness (const char *name)
+{
+ if (bfd_hash_lookup (&lang_definedness_table, name, TRUE, FALSE) == NULL)
+ einfo (_("%P%F: bfd_hash_lookup failed creating symbol %s\n"), name);
+}
+
+/* New-function for the definedness hash table. */
+
+static struct bfd_hash_entry *
+lang_definedness_newfunc (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table ATTRIBUTE_UNUSED,
+ const char *name ATTRIBUTE_UNUSED)
+{
+ struct lang_definedness_hash_entry *ret
+ = (struct lang_definedness_hash_entry *) entry;
+
+ if (ret == NULL)
+ ret = (struct lang_definedness_hash_entry *)
+ bfd_hash_allocate (table, sizeof (struct lang_definedness_hash_entry));
+
+ if (ret == NULL)
+ einfo (_("%P%F: bfd_hash_allocate failed creating symbol %s\n"), name);
+
+ ret->iteration = -1;
+ return &ret->root;
+}
+
+/* Return the iteration when the definition of NAME was last updated. A
+ value of -1 means that the symbol is not defined in the linker script
+ or the command line, but may be defined in the linker symbol table. */
+
+int
+lang_symbol_definition_iteration (const char *name)
+{
+ struct lang_definedness_hash_entry *defentry
+ = (struct lang_definedness_hash_entry *)
+ bfd_hash_lookup (&lang_definedness_table, name, FALSE, FALSE);
+
+ /* We've already created this one on the presence of DEFINED in the
+ script, so it can't be NULL unless something is borked elsewhere in
+ the code. */
+ if (defentry == NULL)
+ FAIL ();
+
+ return defentry->iteration;
+}
+
+/* Update the definedness state of NAME. */
+
+void
+lang_update_definedness (const char *name, struct bfd_link_hash_entry *h)
+{
+ struct lang_definedness_hash_entry *defentry
+ = (struct lang_definedness_hash_entry *)
+ bfd_hash_lookup (&lang_definedness_table, name, FALSE, FALSE);
+
+ /* We don't keep track of symbols not tested with DEFINED. */
+ if (defentry == NULL)
+ return;
+
+ /* If the symbol was already defined, and not from an earlier statement
+ iteration, don't update the definedness iteration, because that'd
+ make the symbol seem defined in the linker script at this point, and
+ it wasn't; it was defined in some object. If we do anyway, DEFINED
+ would start to yield false before this point and the construct "sym =
+ DEFINED (sym) ? sym : X;" would change sym to X despite being defined
+ in an object. */
+ if (h->type != bfd_link_hash_undefined
+ && h->type != bfd_link_hash_common
+ && h->type != bfd_link_hash_new
+ && defentry->iteration == -1)
+ return;
+
+ defentry->iteration = lang_statement_iteration;
+}
+
/* Add the supplied name to the symbol table as an undefined reference.
This is a two step process as the symbol table doesn't even exist at
the time the ld command line is processed. First we put the name
|| (((bfd_get_section_flags (output_bfd, os->bfd_section)
& (SEC_ALLOC | SEC_LOAD)) != 0)
&& os->region->name[0] == '*'
- && strcmp (os->region->name, "*default*") == 0))
+ && strcmp (os->region->name, DEFAULT_MEMORY_REGION) == 0))
{
os->region = lang_memory_default (os->bfd_section);
}
& SEC_NEVER_LOAD) == 0
&& ! link_info.relocatable
&& check_regions
- && strcmp (os->region->name, "*default*") == 0
+ && strcmp (os->region->name, DEFAULT_MEMORY_REGION) == 0
&& lang_memory_region_list != NULL
&& (strcmp (lang_memory_region_list->name,
- "*default*") != 0
+ DEFAULT_MEMORY_REGION) != 0
|| lang_memory_region_list->next != NULL))
{
/* By default this is an error rather than just a
{
/* If we don't have an output section, then just adjust
the default memory address. */
- lang_memory_region_lookup ("*default*")->current = newdot;
+ lang_memory_region_lookup (DEFAULT_MEMORY_REGION, FALSE)->current = newdot;
}
else
{
bfd_boolean check_regions)
{
bfd_vma result;
+ asection *o;
+
+ /* Callers of exp_fold_tree need to increment this. */
+ lang_statement_iteration++;
exp_data_seg.phase = exp_dataseg_none;
result = lang_size_sections_1 (s, output_section_statement, prev, fill,
}
}
+ /* Some backend relaxers want to refer to the output section size. Give
+ them a section size that does not change on the next call while they
+ relax. We can't set this at top because lang_reset_memory_regions
+ which is called before we get here, sets _raw_size to 0 on relaxing
+ rounds. */
+ for (o = output_bfd->sections; o != NULL; o = o->next)
+ o->_cooked_size = o->_raw_size;
+
return result;
}
-bfd_vma
-lang_do_assignments
+/* Worker function for lang_do_assignments. Recursiveness goes here. */
+
+static bfd_vma
+lang_do_assignments_1
(lang_statement_union_type *s,
lang_output_section_statement_type *output_section_statement,
fill_type *fill,
switch (s->header.type)
{
case lang_constructors_statement_enum:
- dot = lang_do_assignments (constructor_list.head,
- output_section_statement,
- fill,
- dot);
+ dot = lang_do_assignments_1 (constructor_list.head,
+ output_section_statement,
+ fill,
+ dot);
break;
case lang_output_section_statement_enum:
if (os->bfd_section != NULL)
{
dot = os->bfd_section->vma;
- (void) lang_do_assignments (os->children.head, os,
- os->fill, dot);
+ (void) lang_do_assignments_1 (os->children.head, os,
+ os->fill, dot);
dot = os->bfd_section->vma + os->bfd_section->_raw_size / opb;
}
break;
case lang_wild_statement_enum:
- dot = lang_do_assignments (s->wild_statement.children.head,
- output_section_statement,
- fill, dot);
+ dot = lang_do_assignments_1 (s->wild_statement.children.head,
+ output_section_statement,
+ fill, dot);
break;
break;
case lang_group_statement_enum:
- dot = lang_do_assignments (s->group_statement.children.head,
- output_section_statement,
- fill, dot);
+ dot = lang_do_assignments_1 (s->group_statement.children.head,
+ output_section_statement,
+ fill, dot);
break;
return dot;
}
+bfd_vma
+lang_do_assignments (lang_statement_union_type *s,
+ lang_output_section_statement_type
+ *output_section_statement,
+ fill_type *fill,
+ bfd_vma dot)
+{
+ /* Callers of exp_fold_tree need to increment this. */
+ lang_statement_iteration++;
+ lang_do_assignments_1 (s, output_section_statement, fill, dot);
+}
+
/* Fix any .startof. or .sizeof. symbols. When the assemblers see the
operator .startof. (section_name), it produces an undefined symbol
.startof.section_name. Similarly, when it sees
}
}
}
+
+ bfd_hash_table_free (&lang_definedness_table);
}
/* This is a small function used when we want to ignore errors from
/* 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.
+ MEMSPEC is the name of the run-time region, or the value of
+ DEFAULT_MEMORY_REGION 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. */
const char *lma_memspec,
int have_lma_p)
{
- *lma_region = lang_memory_region_lookup (lma_memspec);
+ *lma_region = lang_memory_region_lookup (lma_memspec, FALSE);
/* 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)
+ if (lma_memspec != 0 && strcmp (memspec, DEFAULT_MEMORY_REGION) == 0)
*region = *lma_region;
else
- *region = lang_memory_region_lookup (memspec);
+ *region = lang_memory_region_lookup (memspec, FALSE);
if (have_lma_p && lma_memspec != 0)
einfo (_("%X%P:%S: section has both a load address and a load region\n"));
name = current_section->name;
- /* For now, assume that "*default*" is the run-time memory region and
- that no load-time region has been specified. It doesn't really
- matter what we say here, since lang_leave_overlay will override it. */
- lang_leave_output_section_statement (fill, "*default*", phdrs, 0);
+ /* For now, assume that DEFAULT_MEMORY_REGION is the run-time memory
+ region and that no load-time region has been specified. It doesn't
+ really matter what we say here, since lang_leave_overlay will
+ override it. */
+ lang_leave_output_section_statement (fill, DEFAULT_MEMORY_REGION, phdrs, 0);
/* Define the magic symbols. */
struct bfd_elf_version_tree *lang_elf_version_info;
-static int
-lang_vers_match_lang_c (struct bfd_elf_version_expr *expr,
- const char *sym)
-{
- if (expr->pattern[0] == '*' && expr->pattern[1] == '\0')
- return 1;
- return fnmatch (expr->pattern, sym, 0) == 0;
-}
+/* If PREV is NULL, return first version pattern matching particular symbol.
+ If PREV is non-NULL, return first version pattern matching particular
+ symbol after PREV (previously returned by lang_vers_match). */
-static int
-lang_vers_match_lang_cplusplus (struct bfd_elf_version_expr *expr,
- const char *sym)
+static struct bfd_elf_version_expr *
+lang_vers_match (struct bfd_elf_version_expr_head *head,
+ struct bfd_elf_version_expr *prev,
+ const char *sym)
{
- char *alt_sym;
- int result;
-
- if (expr->pattern[0] == '*' && expr->pattern[1] == '\0')
- return 1;
+ const char *cxx_sym = sym;
+ const char *java_sym = sym;
+ struct bfd_elf_version_expr *expr = NULL;
- alt_sym = cplus_demangle (sym, /* DMGL_NO_TPARAMS */ 0);
- if (!alt_sym)
+ if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
{
- /* cplus_demangle (also) returns NULL when it is not a C++ symbol.
- Should we early out FALSE in this case? */
- result = fnmatch (expr->pattern, sym, 0) == 0;
+ cxx_sym = cplus_demangle (sym, /* DMGL_NO_TPARAMS */ 0);
+ if (!cxx_sym)
+ cxx_sym = sym;
}
- else
+ if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
{
- result = fnmatch (expr->pattern, alt_sym, 0) == 0;
- free (alt_sym);
+ java_sym = cplus_demangle (sym, DMGL_JAVA);
+ if (!java_sym)
+ java_sym = sym;
}
- return result;
-}
-
-static int
-lang_vers_match_lang_java (struct bfd_elf_version_expr *expr,
- const char *sym)
-{
- char *alt_sym;
- int result;
-
- if (expr->pattern[0] == '*' && expr->pattern[1] == '\0')
- return 1;
-
- alt_sym = cplus_demangle (sym, DMGL_JAVA);
- if (!alt_sym)
+ if (head->htab && (prev == NULL || prev->wildcard == 0))
{
- /* cplus_demangle (also) returns NULL when it is not a Java symbol.
- Should we early out FALSE in this case? */
- result = fnmatch (expr->pattern, sym, 0) == 0;
+ struct bfd_elf_version_expr e;
+
+ switch (prev ? prev->mask : 0)
+ {
+ case 0:
+ if (head->mask & BFD_ELF_VERSION_C_TYPE)
+ {
+ e.pattern = sym;
+ expr = htab_find (head->htab, &e);
+ while (expr && strcmp (expr->pattern, sym) == 0)
+ if (expr->mask == BFD_ELF_VERSION_C_TYPE)
+ goto out_ret;
+ else
+ expr = expr->next;
+ }
+ /* Fallthrough */
+ case BFD_ELF_VERSION_C_TYPE:
+ if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
+ {
+ e.pattern = cxx_sym;
+ expr = htab_find (head->htab, &e);
+ while (expr && strcmp (expr->pattern, sym) == 0)
+ if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
+ goto out_ret;
+ else
+ expr = expr->next;
+ }
+ /* Fallthrough */
+ case BFD_ELF_VERSION_CXX_TYPE:
+ if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
+ {
+ e.pattern = java_sym;
+ expr = htab_find (head->htab, &e);
+ while (expr && strcmp (expr->pattern, sym) == 0)
+ if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
+ goto out_ret;
+ else
+ expr = expr->next;
+ }
+ /* Fallthrough */
+ default:
+ break;
+ }
}
+
+ /* Finally, try the wildcards. */
+ if (prev == NULL || prev->wildcard == 0)
+ expr = head->remaining;
else
+ expr = prev->next;
+ while (expr)
{
- result = fnmatch (expr->pattern, alt_sym, 0) == 0;
- free (alt_sym);
+ const char *s;
+
+ if (expr->pattern[0] == '*' && expr->pattern[1] == '\0')
+ break;
+
+ if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
+ s = java_sym;
+ else if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
+ s = cxx_sym;
+ else
+ s = sym;
+ if (fnmatch (expr->pattern, sym, 0) == 0)
+ break;
+ expr = expr->next;
}
- return result;
+out_ret:
+ if (cxx_sym != sym)
+ free ((char *) cxx_sym);
+ if (java_sym != sym)
+ free ((char *) java_sym);
+ return expr;
}
/* This is called for each variable name or match expression. */
ret->pattern = new;
ret->symver = 0;
ret->script = 0;
+ ret->wildcard = wildcardp (new);
if (lang == NULL || strcasecmp (lang, "C") == 0)
- ret->match = lang_vers_match_lang_c;
+ ret->mask = BFD_ELF_VERSION_C_TYPE;
else if (strcasecmp (lang, "C++") == 0)
- ret->match = lang_vers_match_lang_cplusplus;
+ ret->mask = BFD_ELF_VERSION_CXX_TYPE;
else if (strcasecmp (lang, "Java") == 0)
- ret->match = lang_vers_match_lang_java;
+ ret->mask = BFD_ELF_VERSION_JAVA_TYPE;
else
{
einfo (_("%X%P: unknown language `%s' in version information\n"),
lang);
- ret->match = lang_vers_match_lang_c;
+ ret->mask = BFD_ELF_VERSION_C_TYPE;
}
return ldemul_new_vers_pattern (ret);
{
struct bfd_elf_version_tree *ret;
- ret = xmalloc (sizeof *ret);
- ret->next = NULL;
- ret->name = NULL;
- ret->vernum = 0;
- ret->globals = globals;
- ret->locals = locals;
- ret->deps = NULL;
+ ret = xcalloc (1, sizeof *ret);
+ ret->globals.list = globals;
+ ret->locals.list = locals;
+ ret->match = lang_vers_match;
ret->name_indx = (unsigned int) -1;
- ret->used = 0;
return ret;
}
static int version_index;
+static hashval_t
+version_expr_head_hash (const void *p)
+{
+ const struct bfd_elf_version_expr *e = p;
+
+ return htab_hash_string (e->pattern);
+}
+
+static int
+version_expr_head_eq (const void *p1, const void *p2)
+{
+ const struct bfd_elf_version_expr *e1 = p1;
+ const struct bfd_elf_version_expr *e2 = p2;
+
+ return strcmp (e1->pattern, e2->pattern) == 0;
+}
+
+static void
+lang_finalize_version_expr_head (struct bfd_elf_version_expr_head *head)
+{
+ size_t count = 0;
+ struct bfd_elf_version_expr *e, *next;
+ struct bfd_elf_version_expr **list_loc, **remaining_loc;
+
+ for (e = head->list; e; e = e->next)
+ {
+ if (!e->wildcard)
+ count++;
+ head->mask |= e->mask;
+ }
+
+ if (count)
+ {
+ head->htab = htab_create (count * 2, version_expr_head_hash,
+ version_expr_head_eq, NULL);
+ list_loc = &head->list;
+ remaining_loc = &head->remaining;
+ for (e = head->list; e; e = next)
+ {
+ next = e->next;
+ if (e->wildcard)
+ {
+ *remaining_loc = e;
+ remaining_loc = &e->next;
+ }
+ else
+ {
+ void **loc = htab_find_slot (head->htab, e, INSERT);
+
+ if (*loc)
+ {
+ struct bfd_elf_version_expr *e1, *last;
+
+ e1 = *loc;
+ last = NULL;
+ do
+ {
+ if (e1->mask == e->mask)
+ {
+ last = NULL;
+ break;
+ }
+ last = e1;
+ e1 = e1->next;
+ }
+ while (e1 && strcmp (e1->pattern, e->pattern) == 0);
+
+ if (last == NULL)
+ {
+ /* This is a duplicate. */
+ /* FIXME: Memory leak. Sometimes pattern is not
+ xmalloced alone, but in larger chunk of memory. */
+ /* free (e->pattern); */
+ free (e);
+ }
+ else
+ {
+ e->next = last->next;
+ last->next = e;
+ }
+ }
+ else
+ {
+ *loc = e;
+ *list_loc = e;
+ list_loc = &e->next;
+ }
+ }
+ }
+ *remaining_loc = NULL;
+ *list_loc = head->remaining;
+ }
+ else
+ head->remaining = head->list;
+}
+
/* This is called when we know the name and dependencies of the
version. */
if (strcmp (t->name, name) == 0)
einfo (_("%X%P: duplicate version tag `%s'\n"), name);
+ lang_finalize_version_expr_head (&version->globals);
+ lang_finalize_version_expr_head (&version->locals);
+
/* Check the global and local match names, and make sure there
aren't any duplicates. */
- for (e1 = version->globals; e1 != NULL; e1 = e1->next)
+ for (e1 = version->globals.list; e1 != NULL; e1 = e1->next)
{
for (t = lang_elf_version_info; t != NULL; t = t->next)
{
struct bfd_elf_version_expr *e2;
- for (e2 = t->locals; e2 != NULL; e2 = e2->next)
- if (strcmp (e1->pattern, e2->pattern) == 0)
- einfo (_("%X%P: duplicate expression `%s' in version information\n"),
- e1->pattern);
+ if (t->locals.htab && e1->wildcard == 0)
+ {
+ e2 = htab_find (t->locals.htab, e1);
+ while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
+ {
+ if (e1->mask == e2->mask)
+ einfo (_("%X%P: duplicate expression `%s' in version information\n"),
+ e1->pattern);
+ e2 = e2->next;
+ }
+ }
+ else if (e1->wildcard)
+ for (e2 = t->locals.remaining; e2 != NULL; e2 = e2->next)
+ if (strcmp (e1->pattern, e2->pattern) == 0 && e1->mask == e2->mask)
+ einfo (_("%X%P: duplicate expression `%s' in version information\n"),
+ e1->pattern);
}
}
- for (e1 = version->locals; e1 != NULL; e1 = e1->next)
+ for (e1 = version->locals.list; e1 != NULL; e1 = e1->next)
{
for (t = lang_elf_version_info; t != NULL; t = t->next)
{
struct bfd_elf_version_expr *e2;
- for (e2 = t->globals; e2 != NULL; e2 = e2->next)
- if (strcmp (e1->pattern, e2->pattern) == 0)
- einfo (_("%X%P: duplicate expression `%s' in version information\n"),
- e1->pattern);
+ if (t->globals.htab && e1->wildcard == 0)
+ {
+ e2 = htab_find (t->globals.htab, e1);
+ while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
+ {
+ if (e1->mask == e2->mask)
+ einfo (_("%X%P: duplicate expression `%s' in version information\n"),
+ e1->pattern);
+ e2 = e2->next;
+ }
+ }
+ else if (e1->wildcard)
+ for (e2 = t->globals.remaining; e2 != NULL; e2 = e2->next)
+ if (strcmp (e1->pattern, e2->pattern) == 0 && e1->mask == e2->mask)
+ einfo (_("%X%P: duplicate expression `%s' in version information\n"),
+ e1->pattern);
}
}