This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Relative expressions and ASSERT


I think this is about the best that can be done for ld script
expressions, if the aim is consistency of evaluation but we value
compatibility with previous versions of ld.  Compatibility dictates we
must have two modes of evaluation.  Expressions inside output sections
definitions are generally relative, while other expressions are always
absolute.  See the ld.texinfo patch below for some details.

Comments?

binutils/
	* NEWS: Mention change in linker script expression evaluation.
ld/
	* ld.texinfo (Expression Section): Detail expression evaluation.
	(Builtin Functions <ADDR>): Correct.
	(Builtin Functions <LOADADDR>): Don't mention LOADADDR normally
	the same as ADDR.
	(Builtin Functions <SEGMENT_START>): 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 <SEGMENT_START>): Use new_rel_from_abs to set value to
	a consistent result.
	(fold_name <SIZEOF_HEADERS>): Return new_number, not new_abs.
	(fold_name <DEFINED, SIZEOF, ALIGNOF, LENGTH, CONSTANT>): Likewise.
	(fold_name <NAME>): No need to handle absolute symbols differently
	from relative ones.
	(fold_name <ORIGIN>): Don't return valid result when
	lang_first_phase_enum.  Return new_rel_from_abs, not new_abs.
	(exp_fold_tree_1 <etree_value>): 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.

Index: binutils/NEWS
===================================================================
RCS file: /cvs/src/src/binutils/NEWS,v
retrieving revision 1.94
diff -u -p -r1.94 NEWS
--- binutils/NEWS	25 Mar 2010 21:12:27 -0000	1.94
+++ binutils/NEWS	16 Aug 2010 06:41:02 -0000
@@ -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
Index: ld/ld.texinfo
===================================================================
RCS file: /cvs/src/src/ld/ld.texinfo,v
retrieving revision 1.261
diff -u -p -r1.261 ld.texinfo
--- ld/ld.texinfo	20 Jul 2010 19:25:42 -0000	1.261
+++ ld/ld.texinfo	16 Aug 2010 14:50:23 -0000
@@ -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.
-
-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.
-
-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.
-
-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.
+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,
+
+@smallexample
+@group
+SECTIONS
+  @{
+    . = 0x100;
+    __executable_start = 0x100;
+    .data :
+    @{
+      . = 0x10;
+      __data_start = 0x10;
+      *(.data)
+    @}
+    @dots{}
+  @}
+@end group
+@end smallexample
+
+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.
+
+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{Expres
 @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 n
 @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 (w
 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})
Index: ld/ldexp.c
===================================================================
RCS file: /cvs/src/src/ld/ldexp.c,v
retrieving revision 1.85
diff -u -p -r1.85 ldexp.c
--- ld/ldexp.c	12 Aug 2010 13:36:50 -0000	1.85
+++ ld/ldexp.c	16 Aug 2010 05:22:23 -0000
@@ -138,7 +138,8 @@ exp_print_token (token_code_type code, i
 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;
 }
 
@@ -190,6 +191,15 @@ exp_relop (asection *section, bfd_vma va
 }
 
 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)
 {
   expld.result.valid_p = TRUE;
@@ -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 d
 
       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)
Index: ld/ldlang.c
===================================================================
RCS file: /cvs/src/src/ld/ldlang.c,v
retrieving revision 1.337
diff -u -p -r1.337 ldlang.c
--- ld/ldlang.c	6 Aug 2010 01:51:17 -0000	1.337
+++ ld/ldlang.c	16 Aug 2010 05:22:26 -0000
@@ -3916,7 +3916,7 @@ print_assignment (lang_assignment_statem
 	{
 	  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_statem
 	    {
 	      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_un
 	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"));
 	  {
Index: ld/testsuite/ld-scripts/memory.t
===================================================================
RCS file: /cvs/src/src/ld/testsuite/ld-scripts/memory.t,v
retrieving revision 1.2
diff -u -p -r1.2 memory.t
--- ld/testsuite/ld-scripts/memory.t	2 Mar 2009 17:27:36 -0000	1.2
+++ ld/testsuite/ld-scripts/memory.t	16 Aug 2010 05:22:28 -0000
@@ -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 = .;

-- 
Alan Modra
Australia Development Lab, IBM


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]