aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ld/ChangeLog29
-rw-r--r--ld/ld.texinfo3
-rw-r--r--ld/ldexp.c12
-rw-r--r--ld/ldlang.c143
-rw-r--r--ld/ldlang.h14
5 files changed, 184 insertions, 17 deletions
diff --git a/ld/ChangeLog b/ld/ChangeLog
index 00840ad..7e70ed4 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,32 @@
+2003-10-11 Hans-Peter Nilsson <hp@bitrange.com>
+
+ * ld.texinfo (Builtin Functions) <DEFINED>: Say that only symbols
+ defined before the statement using DEFINED yield 1.
+ * ldexp.c (fold_name) <case DEFINED>: In lang_first_phase_enum,
+ call lang_track_definedness on symbol. In subsequent phases, use
+ lang_symbol_definition_iteration and lang_statement_iteration to
+ check whether the symbol was defined before the current statement.
+ (exp_fold_tree) <case etree_assign et al>: Call
+ lang_update_definedness before updating symbol type when setting
+ symbol.
+ * ldlang.c (lang_definedness_table): New variable.
+ (lang_definedness_newfunc, lang_track_definedness)
+ (lang_symbol_definition_iteration, lang_update_definedness): New
+ functions.
+ (lang_init): Initialize lang_definedness_table and
+ lang_statement_iteration.
+ (lang_finish): Destroy bfd_hash_table_free.
+ (lang_size_sections): Increment lang_statement_iteration.
+ (lang_do_assignments_1): New function with former
+ lang_do_assignments contents. Change recursive calls to call this
+ function.
+ (lang_do_assignments): Evacuate contents. Increment
+ lang_statement_iteration, then just call lang_do_assignments_1.
+ * ldlang.h (struct lang_definedness_hash_entry)
+ (lang_statement_iteration, lang_track_definedness)
+ (lang_symbol_definition_iteration, lang_update_definedness):
+ Declare.
+
2003-10-09 H.J. Lu <hongjiu.lu@intel.com>
* scripttempl/elf.sc (__executable_start): Provide.
diff --git a/ld/ld.texinfo b/ld/ld.texinfo
index b01a105..1713294 100644
--- a/ld/ld.texinfo
+++ b/ld/ld.texinfo
@@ -4452,7 +4452,8 @@ evaluation purposes.
@kindex DEFINED(@var{symbol})
@cindex symbol defaults
Return 1 if @var{symbol} is in the linker global symbol table and is
-defined, otherwise return 0. You can use this function to provide
+defined before the statement using DEFINED in the script, otherwise
+return 0. You can use this function to provide
default values for symbols. For example, the following script fragment
shows how to set a global symbol @samp{begin} to the first location in
the @samp{.text} section---but if a symbol called @samp{begin} already
diff --git a/ld/ldexp.c b/ld/ldexp.c
index cce0cdf..80d79cf 100644
--- a/ld/ldexp.c
+++ b/ld/ldexp.c
@@ -485,10 +485,15 @@ fold_name (etree_type *tree,
break;
case DEFINED:
if (allocation_done == lang_first_phase_enum)
- result.valid_p = FALSE;
+ {
+ lang_track_definedness (tree->name.name);
+ result.valid_p = FALSE;
+ }
else
{
struct bfd_link_hash_entry *h;
+ int def_iteration
+ = lang_symbol_definition_iteration (tree->name.name);
h = bfd_wrapped_link_hash_lookup (output_bfd, &link_info,
tree->name.name,
@@ -496,7 +501,9 @@ fold_name (etree_type *tree,
result.value = (h != NULL
&& (h->type == bfd_link_hash_defined
|| h->type == bfd_link_hash_defweak
- || h->type == bfd_link_hash_common));
+ || h->type == bfd_link_hash_common)
+ && (def_iteration == lang_statement_iteration
+ || def_iteration == -1));
result.section = abs_output_section;
result.valid_p = TRUE;
}
@@ -738,6 +745,7 @@ exp_fold_tree (etree_type *tree,
{
/* FIXME: Should we worry if the symbol is already
defined? */
+ lang_update_definedness (tree->assign.dst, h);
h->type = bfd_link_hash_defined;
h->u.def.value = result.value;
h->u.def.section = result.section->bfd_section;
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 2d1f5d5..039c7cb 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -60,6 +60,7 @@ static const char *current_target;
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 *);
@@ -67,6 +68,8 @@ static bfd_boolean wildcardp (const char *);
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 *);
@@ -95,6 +98,7 @@ bfd_boolean delete_output_file_on_failure = FALSE;
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 */
@@ -477,6 +481,19 @@ lang_init (void)
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;
}
/*----------------------------------------------------------------------
@@ -1867,6 +1884,85 @@ lang_reasonable_defaults (void)
#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
@@ -3132,6 +3228,9 @@ lang_size_sections
{
bfd_vma result;
+ /* 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,
dot, relax, check_regions);
@@ -3157,8 +3256,10 @@ lang_size_sections
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,
@@ -3172,10 +3273,10 @@ lang_do_assignments
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:
@@ -3186,8 +3287,8 @@ lang_do_assignments
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;
}
@@ -3206,9 +3307,9 @@ lang_do_assignments
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;
@@ -3301,9 +3402,9 @@ lang_do_assignments
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;
@@ -3318,6 +3419,18 @@ lang_do_assignments
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
@@ -3445,6 +3558,8 @@ lang_finish (void)
}
}
}
+
+ bfd_hash_table_free (&lang_definedness_table);
}
/* This is a small function used when we want to ignore errors from
diff --git a/ld/ldlang.h b/ld/ldlang.h
index d3e87b1..d0195d0 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -361,6 +361,14 @@ struct unique_sections {
const char *name;
};
+/* This structure records symbols for which we need to keep track of
+ definedness for use in the DEFINED () test. */
+
+struct lang_definedness_hash_entry {
+ struct bfd_hash_entry root;
+ int iteration;
+};
+
extern struct unique_sections *unique_section_list;
extern lang_output_section_statement_type *abs_output_section;
@@ -375,6 +383,8 @@ extern const char *entry_section;
extern bfd_boolean entry_from_cmdline;
extern lang_statement_list_type file_chain;
+extern int lang_statement_iteration;
+
extern void lang_init
(void);
extern struct memory_region_struct *lang_memory_region_lookup
@@ -517,5 +527,9 @@ extern void lang_add_unique
(const char *);
extern const char *lang_get_output_target
(void);
+extern void lang_track_definedness (const char *);
+extern int lang_symbol_definition_iteration (const char *);
+extern void lang_update_definedness
+ (const char *, struct bfd_link_hash_entry *);
#endif