From 7542af2ae81ecd2b97dcaa7c66d0b0d85e359d88 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 19 Aug 2010 05:51:50 +0000 Subject: [PATCH] binutils/ * NEWS: Mention change in linker script expression evaluation. ld/ * ld.texinfo (Expression Section): Detail expression evaluation. (Builtin Functions ): Correct. (Builtin Functions ): Don't mention LOADADDR normally the same as ADDR. (Builtin Functions ): Typo fix. * ldexp.c (new_number): New function. (make_abs, exp_get_abs_int): Cope with NULL expld.result.section. (fold_unary <'~', '!', '-'>): Don't make_abs. (fold_binary): Simplify result section logic. Return NULL section for logical ops. (fold_binary ): Use new_rel_from_abs to set value to a consistent result. (fold_name ): Return new_number, not new_abs. (fold_name ): Likewise. (fold_name ): No need to handle absolute symbols differently from relative ones. (fold_name ): Don't return valid result when lang_first_phase_enum. Return new_rel_from_abs, not new_abs. (exp_fold_tree_1 ): Return new_number, not new_rel. (exp_fold_tree_1): Ajust for NULL expld.result.section. When assigning a plain number to dot, assume the value is relative to expld.section. Make terms not in an output section, absolute. * ldlang.c (print_assignment): Fix style nit. (lang_size_sections_1): Cope with NULL expld.result.section. (lang_do_assignments_1): Likewise. ld/testsuite/ * ld-scripts/memory.t: Remove ORIGIN fudge. --- binutils/ChangeLog | 4 + binutils/NEWS | 3 + ld/ChangeLog | 28 +++++ ld/ld.texinfo | 104 ++++++++++++++---- ld/ldexp.c | 177 ++++++++++++++++--------------- ld/ldlang.c | 17 ++- ld/testsuite/ChangeLog | 4 + ld/testsuite/ld-scripts/memory.t | 14 +-- 8 files changed, 227 insertions(+), 124 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 4ec74dae1c..624bab1d0d 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,7 @@ +2010-08-19 Alan Modra + + * NEWS: Mention change in linker script expression evaluation. + 2010-08-13 Dan Rosenberg PR binutils/11889 diff --git a/binutils/NEWS b/binutils/NEWS index 92f894dc07..a1ba9ac518 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -1,5 +1,8 @@ -*- text -*- +* Linker script expression evaluation is somewhat more sane. This may + break scripts that depend on quirks of the old expression evaluation. + * Add support for the TMS320C6000 (TI C6X) processor family. * Readelf can now display ARM unwind tables (.ARM.exidx / .ARM.extab) using diff --git a/ld/ChangeLog b/ld/ChangeLog index 30cb4ebe6c..e0873aecfb 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,31 @@ +2010-08-19 Alan Modra + + * ld.texinfo (Expression Section): Detail expression evaluation. + (Builtin Functions ): Correct. + (Builtin Functions ): Don't mention LOADADDR normally + the same as ADDR. + (Builtin Functions ): Typo fix. + * ldexp.c (new_number): New function. + (make_abs, exp_get_abs_int): Cope with NULL expld.result.section. + (fold_unary <'~', '!', '-'>): Don't make_abs. + (fold_binary): Simplify result section logic. Return NULL section + for logical ops. + (fold_binary ): Use new_rel_from_abs to set value to + a consistent result. + (fold_name ): Return new_number, not new_abs. + (fold_name ): Likewise. + (fold_name ): No need to handle absolute symbols differently + from relative ones. + (fold_name ): Don't return valid result when + lang_first_phase_enum. Return new_rel_from_abs, not new_abs. + (exp_fold_tree_1 ): Return new_number, not new_rel. + (exp_fold_tree_1): Ajust for NULL expld.result.section. When assigning + a plain number to dot, assume the value is relative to expld.section. + Make terms not in an output section, absolute. + * ldlang.c (print_assignment): Fix style nit. + (lang_size_sections_1): Cope with NULL expld.result.section. + (lang_do_assignments_1): Likewise. + 2010-08-12 Alan Modra * ldexp.c (new_rel): Remove "str". Update all call sites. diff --git a/ld/ld.texinfo b/ld/ld.texinfo index 8928350ea4..7470c538a6 100644 --- a/ld/ld.texinfo +++ b/ld/ld.texinfo @@ -5447,23 +5447,82 @@ address}. @cindex absolute and relocatable symbols @cindex relocatable and absolute symbols @cindex symbols, relocatable and absolute -When the linker evaluates an expression, the result is either absolute -or relative to some section. A relative expression is expressed as a -fixed offset from the base of a section. +Addresses and symbols may be section relative, or absolute. A section +relative symbol is relocatable. If you request relocatable output +using the @samp{-r} option, a further link operation may change the +value of a section relative symbol. On the other hand, an absolute +symbol will retain the same value throughout any further link +operations. + +Some terms in linker expressions are addresses. This is true of all +symbols and for builtin functions that return an address, such as +@code{ADDR}, @code{LOADADDR}, @code{ORIGIN} and @code{SEGMENT_START}. +Other terms are simply numbers, or are builtin functions that return a +non-address value, such as @code{LENGTH}. + +When the linker evaluates an expression, the result depends on where +the expression is located in a linker script. Expressions appearing +outside an output section definitions are evaluated with all terms +first being converted to absolute addresses before applying operators, +and evaluate to an absolute address result. Expressions appearing +inside an output section definition are evaluated with more complex +rules, but the aim is to treat terms as relative addresses and produce +a relative address result. In particular, an assignment of a number +to a symbol results in a symbol relative to the output section with an +offset given by the number. So, in the following simple example, -The position of the expression within the linker script determines -whether it is absolute or relative. An expression which appears within -an output section definition is relative to the base of the output -section. An expression which appears elsewhere will be absolute. +@smallexample +@group +SECTIONS + @{ + . = 0x100; + __executable_start = 0x100; + .data : + @{ + . = 0x10; + __data_start = 0x10; + *(.data) + @} + @dots{} + @} +@end group +@end smallexample -A symbol set to a relative expression will be relocatable if you request -relocatable output using the @samp{-r} option. That means that a -further link operation may change the value of the symbol. The symbol's -section will be the section of the relative expression. +both @code{.} and @code{__executable_start} are set to the absolute +address 0x100 in the first two assignments, then both @code{.} and +@code{__data_start} are set to 0x10 relative to the @code{.data} +section in the second two assignments. -A symbol set to an absolute expression will retain the same value -through any further link operation. The symbol will be absolute, and -will not have any particular associated section. +For expressions appearing inside an output section definition +involving numbers, relative addresses and absolute addresses, ld +follows these rules to evaluate terms: + +@itemize @bullet +@item +Unary operations on a relative address, and binary operations on two +relative addresses in the same section or between one relative address +and a number, apply the operator to the offset part of the address(es). +@item +Unary operations on an absolute address, and binary operations on one +or more absolute addresses or on two relative addresses not in the +same section, first convert any non-absolute term to an absolute +address before applying the operator. +@end itemize + +The result section of each sub-expression is as follows: + +@itemize @bullet +@item +An operation involving only numbers results in a number. +@item +The result of comparisons, @samp{&&} and @samp{||} is also a number. +@item +The result of other operations on relative addresses (after above +conversions) is a relative address in the same section as the operand(s). +@item +The result of other operations on absolute addresses (after above +conversions) is an absolute address. +@end itemize You can use the builtin function @code{ABSOLUTE} to force an expression to be absolute when it would otherwise be relative. For example, to @@ -5479,6 +5538,9 @@ SECTIONS If @samp{ABSOLUTE} were not used, @samp{_edata} would be relative to the @samp{.data} section. +Using @code{LOADADDR} also forces an expression absolute, since this +particular builtin function returns an absolute address. + @node Builtin Functions @subsection Builtin Functions @cindex functions in expressions @@ -5497,10 +5559,12 @@ normally section relative. @xref{Expression Section}. @item ADDR(@var{section}) @kindex ADDR(@var{section}) @cindex section address in expression -Return the absolute address (the VMA) of the named @var{section}. Your +Return the address (VMA) of the named @var{section}. Your script must previously have defined the location of that section. In -the following example, @code{symbol_1} and @code{symbol_2} are assigned -identical values: +the following example, @code{start_of_output_1}, @code{symbol_1} and +@code{symbol_2} are assigned equivalent values, except that +@code{symbol_1} will be relative to the @code{.output1} section while +the other two will be absolute: @smallexample @group SECTIONS @{ @dots{} @@ -5664,9 +5728,7 @@ Return the length of the memory region named @var{memory}. @item LOADADDR(@var{section}) @kindex LOADADDR(@var{section}) @cindex section load address in expression -Return the absolute LMA of the named @var{section}. This is normally -the same as @code{ADDR}, but it may be different if the @code{AT} -attribute is used in the output section definition (@pxref{Output +Return the absolute LMA of the named @var{section}. (@pxref{Output Section LMA}). @kindex MAX @@ -5696,7 +5758,7 @@ value has been given for this segment (with a command-line @samp{-T} option) that value will be returned; otherwise the value will be @var{default}. At present, the @samp{-T} command-line option can only be used to set the base address for the ``text'', ``data'', and -``bss'' sections, but you use @code{SEGMENT_START} with any segment +``bss'' sections, but you can use @code{SEGMENT_START} with any segment name. @item SIZEOF(@var{section}) diff --git a/ld/ldexp.c b/ld/ldexp.c index 050227d25b..0549f19575 100644 --- a/ld/ldexp.c +++ b/ld/ldexp.c @@ -138,7 +138,8 @@ exp_print_token (token_code_type code, int infix_p) static void make_abs (void) { - expld.result.value += expld.result.section->vma; + if (expld.result.section != NULL) + expld.result.value += expld.result.section->vma; expld.result.section = bfd_abs_section_ptr; } @@ -189,6 +190,15 @@ exp_relop (asection *section, bfd_vma value) return new_e; } +static void +new_number (bfd_vma value) +{ + expld.result.valid_p = TRUE; + expld.result.value = value; + expld.result.str = NULL; + expld.result.section = NULL; +} + static void new_rel (bfd_vma value, asection *section) { @@ -227,17 +237,14 @@ fold_unary (etree_type *tree) break; case '~': - make_abs (); expld.result.value = ~expld.result.value; break; case '!': - make_abs (); expld.result.value = !expld.result.value; break; case '-': - make_abs (); expld.result.value = -expld.result.value; break; @@ -293,6 +300,7 @@ fold_binary (etree_type *tree) { const char *segment_name; segment_type *seg; + /* Check to see if the user has overridden the default value. */ segment_name = tree->binary.rhs->name.name; @@ -305,9 +313,7 @@ fold_binary (etree_type *tree) einfo (_("%P: warning: address of `%s' isn't multiple of maximum page size\n"), segment_name); seg->used = TRUE; - expld.result.value = seg->value; - expld.result.str = NULL; - expld.result.section = expld.section; + new_rel_from_abs (seg->value); break; } return; @@ -319,32 +325,20 @@ fold_binary (etree_type *tree) if (expld.result.valid_p) { - /* If the values are from different sections, or this is an - absolute expression, make both the source arguments - absolute. However, adding or subtracting an absolute - value from a relative value is meaningful, and is an - exception. */ - if (expld.section != bfd_abs_section_ptr - && lhs.section == bfd_abs_section_ptr - && tree->type.node_code == '+') + if (lhs.section != expld.result.section) { - /* Keep the section of the rhs term. */ - expld.result.value = lhs.value + expld.result.value; - return; - } - else if (expld.section != bfd_abs_section_ptr - && expld.result.section == bfd_abs_section_ptr - && (tree->type.node_code == '+' - || tree->type.node_code == '-')) - { - /* Keep the section of the lhs term. */ - expld.result.section = lhs.section; - } - else if (expld.result.section != lhs.section - || expld.section == bfd_abs_section_ptr) - { - make_abs (); - lhs.value += lhs.section->vma; + /* If the values are from different sections, and neither is + just a number, make both the source arguments absolute. */ + if (expld.result.section != NULL + && lhs.section != NULL) + { + make_abs (); + lhs.value += lhs.section->vma; + } + + /* If the rhs is just a number, keep the lhs section. */ + else if (expld.result.section == NULL) + expld.result.section = lhs.section; } switch (tree->type.node_code) @@ -366,26 +360,32 @@ fold_binary (etree_type *tree) break; #define BOP(x, y) \ - case x: \ - expld.result.value = lhs.value y expld.result.value; \ - break; + case x: \ + expld.result.value = lhs.value y expld.result.value; \ + break; + +#define BOPN(x, y) \ + case x: \ + expld.result.value = lhs.value y expld.result.value; \ + expld.result.section = NULL; \ + break; BOP ('+', +); BOP ('*', *); BOP ('-', -); BOP (LSHIFT, <<); BOP (RSHIFT, >>); - BOP (EQ, ==); - BOP (NE, !=); - BOP ('<', <); - BOP ('>', >); - BOP (LE, <=); - BOP (GE, >=); BOP ('&', &); BOP ('^', ^); BOP ('|', |); - BOP (ANDAND, &&); - BOP (OROR, ||); + BOPN (EQ, ==); + BOPN (NE, !=); + BOPN ('<', <); + BOPN ('>', >); + BOPN (LE, <=); + BOPN (GE, >=); + BOPN (ANDAND, &&); + BOPN (OROR, ||); case MAX_K: if (lhs.value > expld.result.value) @@ -499,7 +499,7 @@ fold_name (etree_type *tree) The bfd function may cache incorrect data. */ if (expld.phase != lang_mark_phase_enum) hdr_size = bfd_sizeof_headers (link_info.output_bfd, &link_info); - new_abs (hdr_size); + new_number (hdr_size); } break; @@ -516,14 +516,12 @@ fold_name (etree_type *tree) &link_info, tree->name.name, FALSE, FALSE, TRUE); - expld.result.value = (h != NULL - && (h->type == bfd_link_hash_defined - || h->type == bfd_link_hash_defweak - || h->type == bfd_link_hash_common) - && (def_iteration == lang_statement_iteration - || def_iteration == -1)); - expld.result.section = expld.section; - expld.result.valid_p = TRUE; + new_number (h != NULL + && (h->type == bfd_link_hash_defined + || h->type == bfd_link_hash_defweak + || h->type == bfd_link_hash_common) + && (def_iteration == lang_statement_iteration + || def_iteration == -1)); } break; @@ -545,24 +543,19 @@ fold_name (etree_type *tree) else if (h->type == bfd_link_hash_defined || h->type == bfd_link_hash_defweak) { - if (bfd_is_abs_section (h->u.def.section)) - new_abs (h->u.def.value); - else - { - asection *output_section; + asection *output_section; - output_section = h->u.def.section->output_section; - if (output_section == NULL) - { - if (expld.phase != lang_mark_phase_enum) - einfo (_("%X%S: unresolvable symbol `%s'" - " referenced in expression\n"), - tree->name.name); - } - else - new_rel (h->u.def.value + h->u.def.section->output_offset, - output_section); + output_section = h->u.def.section->output_section; + if (output_section == NULL) + { + if (expld.phase != lang_mark_phase_enum) + einfo (_("%X%S: unresolvable symbol `%s'" + " referenced in expression\n"), + tree->name.name); } + else + new_rel (h->u.def.value + h->u.def.section->output_offset, + output_section); } else if (expld.phase == lang_final_phase_enum || expld.assigning_to_dot) @@ -633,7 +626,7 @@ fold_name (etree_type *tree) if (expld.phase == lang_final_phase_enum) einfo (_("%F%S: undefined section `%s' referenced in expression\n"), tree->name.name); - new_abs (0); + new_number (0); } else if (os->processed_vma) { @@ -645,7 +638,7 @@ fold_name (etree_type *tree) else val = (bfd_vma)1 << os->bfd_section->alignment_power; - new_abs (val); + new_number (val); } } break; @@ -656,7 +649,7 @@ fold_name (etree_type *tree) mem = lang_memory_region_lookup (tree->name.name, FALSE); if (mem != NULL) - new_abs (mem->length); + new_number (mem->length); else einfo (_("%F%S: undefined MEMORY region `%s'" " referenced in expression\n"), tree->name.name); @@ -664,23 +657,24 @@ fold_name (etree_type *tree) break; case ORIGIN: - { - lang_memory_region_type *mem; + if (expld.phase != lang_first_phase_enum) + { + lang_memory_region_type *mem; - mem = lang_memory_region_lookup (tree->name.name, FALSE); - if (mem != NULL) - new_abs (mem->origin); - else - einfo (_("%F%S: undefined MEMORY region `%s'" - " referenced in expression\n"), tree->name.name); - } + mem = lang_memory_region_lookup (tree->name.name, FALSE); + if (mem != NULL) + new_rel_from_abs (mem->origin); + else + einfo (_("%F%S: undefined MEMORY region `%s'" + " referenced in expression\n"), tree->name.name); + } break; case CONSTANT: if (strcmp (tree->name.name, "MAXPAGESIZE") == 0) - new_abs (config.maxpagesize); + new_number (config.maxpagesize); else if (strcmp (tree->name.name, "COMMONPAGESIZE") == 0) - new_abs (config.commonpagesize); + new_number (config.commonpagesize); else einfo (_("%F%S: unknown constant `%s' referenced in expression\n"), tree->name.name); @@ -704,7 +698,7 @@ exp_fold_tree_1 (etree_type *tree) switch (tree->type.node_class) { case etree_value: - new_rel (tree->value.value, expld.section); + new_number (tree->value.value); expld.result.str = tree->value.str; break; @@ -767,7 +761,11 @@ exp_fold_tree_1 (etree_type *tree) { bfd_vma nextdot; - nextdot = expld.result.value + expld.result.section->vma; + nextdot = expld.result.value; + if (expld.result.section != NULL) + nextdot += expld.result.section->vma; + else + nextdot += expld.section->vma; if (nextdot < expld.dot && expld.section != bfd_abs_section_ptr) einfo (_("%F%S cannot move location counter backwards" @@ -818,6 +816,8 @@ exp_fold_tree_1 (etree_type *tree) lang_update_definedness (tree->assign.dst, h); h->type = bfd_link_hash_defined; h->u.def.value = expld.result.value; + if (expld.result.section == NULL) + expld.result.section = expld.section; h->u.def.section = expld.result.section; if (tree->type.node_class == etree_provide) tree->type.node_class = etree_provided; @@ -856,6 +856,12 @@ exp_fold_tree_1 (etree_type *tree) memset (&expld.result, 0, sizeof (expld.result)); break; } + + /* Any value not inside an output section statement is an + absolute value. */ + if (expld.result.valid_p + && expld.section == bfd_abs_section_ptr) + make_abs (); } void @@ -1186,7 +1192,8 @@ exp_get_abs_int (etree_type *tree, int def, char *name) if (expld.result.valid_p) { - expld.result.value += expld.result.section->vma; + if (expld.result.section != NULL) + expld.result.value += expld.result.section->vma; return expld.result.value; } else if (name != NULL && expld.phase != lang_mark_phase_enum) diff --git a/ld/ldlang.c b/ld/ldlang.c index 41ab2eed28..2b9971aff9 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -3916,7 +3916,7 @@ print_assignment (lang_assignment_statement_type *assignment, { value = expld.result.value; - if (expld.result.section) + if (expld.result.section != NULL) value += expld.result.section->vma; minfo ("0x%V", value); @@ -3933,7 +3933,7 @@ print_assignment (lang_assignment_statement_type *assignment, { value = h->u.def.value; - if (expld.result.section) + if (expld.result.section != NULL) value += expld.result.section->vma; minfo ("[0x%V]", value); @@ -4718,7 +4718,11 @@ lang_size_sections_1 exp_fold_tree (os->addr_tree, bfd_abs_section_ptr, &dot); if (expld.result.valid_p) - dot = expld.result.value + expld.result.section->vma; + { + dot = expld.result.value; + if (expld.result.section != NULL) + dot += expld.result.section->vma; + } else if (expld.phase != lang_mark_phase_enum) einfo (_("%F%S: non constant or forward reference" " address expression for section %s\n"), @@ -5397,8 +5401,11 @@ lang_do_assignments_1 (lang_statement_union_type *s, case lang_data_statement_enum: exp_fold_tree (s->data_statement.exp, bfd_abs_section_ptr, &dot); if (expld.result.valid_p) - s->data_statement.value = (expld.result.value - + expld.result.section->vma); + { + s->data_statement.value = expld.result.value; + if (expld.result.section != NULL) + s->data_statement.value += expld.result.section->vma; + } else einfo (_("%F%P: invalid data statement\n")); { diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index c15a44e99e..dd7ee77d7b 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2010-08-19 Alan Modra + + * ld-scripts/memory.t: Remove ORIGIN fudge. + 2010-08-13 H.J. Lu PR ld/11913 diff --git a/ld/testsuite/ld-scripts/memory.t b/ld/testsuite/ld-scripts/memory.t index 6623b28939..129bd7c123 100644 --- a/ld/testsuite/ld-scripts/memory.t +++ b/ld/testsuite/ld-scripts/memory.t @@ -15,19 +15,7 @@ SECTIONS . = 0; .text : { - /* The value returned by the ORIGIN operator is a constant. - However it is being assigned to a symbol declared within - a section. Therefore the symbol is section-relative and - its value will include the offset of that section from - the start of memory. ie the declaration: - text_start = ORIGIN (TEXTMEM); - here will result in text_start having a value of 0x200. - Hence we need to subtract the absolute value of the - location counter at this point in order to give text_start - a value that is truely absolute, and which coincidentally - will allow the tests in script.exp to work. */ - - text_start = ORIGIN(TEXTMEM) - ABSOLUTE (.); + text_start = ORIGIN (TEXTMEM); *(.text) *(.pr) text_end = .; -- 2.34.1