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]

[PATCH] handle large deltas with DWARF fixed_advance_pc


DWARF's DW_LNS_fixed_advance_pc opcode has a 16-bit operand to specify the address delta between lines. When I added the DWARF2_USE_FIXED_ADVANCE_PC flag to enable using these opcodes, I didn't think about what would happen when there is more than 64K between lines. (The assembler dies.)

This patch fixes the problem by checking for an address delta that is close to the limit and using a DW_LNE_set_address opcode. The check is conservative because the whole point of using fix_advance_pc opcodes is to handle linker relaxation that changes the code offsets. I arbitrarily picked a value of 50,000 as the cutoff, but I don't have a strong opinion on the exact value. I don't expect linker relaxation to make big changes in the offsets, but on the other hand, such large address deltas won't occur often so it doesn't hurt to be cautious.

This patch is more complicated than the old code because the size of the debug info and the kind of fix records that are generated depend on the address values, so they have to be handled as part of relaxation.

I needed some code to emit a fix record for an address and didn't want to hardcode the address size. Instead I split out some existing code from emit_expr into a new function.

Is this OK?

2008-01-07 Bob Wilson <bob.wilson@acm.org>

gas/
	* dwarf2dbg.c (out_sleb128): Delete.
	(size_fixed_inc_line_addr, emit_fixed_inc_line_addr): New.
	(out_fixed_inc_line_addr): Delete.
	(relax_inc_line_addr, dwarf2dbg_estimate_size_before_relax): Call new
	size_fixed_inc_line_addr if DWARF2_USE_FIXED_ADVANCE_PC is set.
	(dwarf2dbg_convert_frag): Likewise for emit_fixed_inc_line_addr.
	(process_entries): Remove calls to out_fixed_inc_line_addr.  When
	DWARF2_USE_FIXED_ADVANCE_PC is set, call relax_inc_line_addr.
	* read.h (emit_expr_fix): New prototype.
	* read.c (emit_expr): Move code to emit_expr_fix and use it here.
	(emit_expr_fix): New.

testsuite/
	* gas/lns/lns.exp: Run new lns-big-delta test for targets that set
	DWARF2_USE_FIXED_ADVANCE_PC.
	* gas/lns/lns-big-delta.s: New.
	* gas/lns/lns-big-delta.d: New.


Index: dwarf2dbg.c
===================================================================
RCS file: /cvs/src/src/gas/dwarf2dbg.c,v
retrieving revision 1.94
diff -u -p -r1.94 dwarf2dbg.c
--- dwarf2dbg.c	19 Nov 2007 19:37:18 -0000	1.94
+++ dwarf2dbg.c	7 Jan 2008 22:03:24 -0000
@@ -198,13 +198,13 @@ static void out_two (int);
 static void out_four (int);
 static void out_abbrev (int, int);
 static void out_uleb128 (addressT);
-static void out_sleb128 (addressT);
 static offsetT get_frag_fix (fragS *, segT);
 static void out_set_addr (symbolS *);
 static int size_inc_line_addr (int, addressT);
 static void emit_inc_line_addr (int, addressT, char *, int);
+static int size_fixed_inc_line_addr (int, addressT);
+static void emit_fixed_inc_line_addr (int, addressT, fragS *, char *, int);
 static void out_inc_line_addr (int, addressT);
-static void out_fixed_inc_line_addr (int, symbolS *, symbolS *);
 static void relax_inc_line_addr (int, symbolS *, symbolS *);
 static void process_entries (segT, struct line_entry *);
 static void out_file_list (void);
@@ -762,14 +762,6 @@ out_uleb128 (addressT value)
   output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0);
 }
 
-/* Emit a signed "little-endian base 128" number.  */
-
-static void
-out_sleb128 (addressT value)
-{
-  output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1);
-}
-
 /* Emit a tuple for .debug_abbrev.  */
 
 static inline void
@@ -1005,41 +997,103 @@ out_inc_line_addr (int line_delta, addre
 
 /* Write out an alternative form of line and address skips using
    DW_LNS_fixed_advance_pc opcodes.  This uses more space than the default
-   line and address information, but it helps support linker relaxation that
-   changes the code offsets.  */
+   line and address information, but it is required if linker relaxation
+   could change the code offsets.  The following two routines *must* be
+   kept in sync.  */
 
-static void
-out_fixed_inc_line_addr (int line_delta, symbolS *to_sym, symbolS *from_sym)
+static int
+size_fixed_inc_line_addr (int line_delta, addressT addr_delta)
 {
-  expressionS expr;
+  int len = 0;
 
   /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence.  */
+  if (line_delta != INT_MAX)
+    len = 1 + sizeof_leb128 (line_delta, 1);
+
+  if (addr_delta > 50000)
+    {
+      /* DW_LNS_extended_op */
+      len += 1 + sizeof_leb128 (sizeof_address + 1, 0);
+      /* DW_LNE_set_address */
+      len += 1 + sizeof_address;
+    }
+  else
+    /* DW_LNS_fixed_advance_pc */
+    len += 3;
+
   if (line_delta == INT_MAX)
+    /* DW_LNS_extended_op + DW_LNE_end_sequence */
+    len += 3;
+  else
+    /* DW_LNS_copy */
+    len += 1;
+
+  return len;
+}
+
+static void
+emit_fixed_inc_line_addr (int line_delta, addressT addr_delta, fragS *frag,
+			  char *p, int len)
+{
+  expressionS *exp;
+  segT line_seg;
+  char *end = p + len;
+
+  /* Line number sequences cannot go backward in addresses.  This means
+     we've incorrectly ordered the statements in the sequence.  */
+  assert ((offsetT) addr_delta >= 0);
+
+  /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence.  */
+  if (line_delta != INT_MAX)
     {
-      out_opcode (DW_LNS_fixed_advance_pc);
-      expr.X_op = O_subtract;
+      *p++ = DW_LNS_advance_line;
+      p += output_leb128 (p, line_delta, 1);
+    }
+
+  exp = symbol_get_value_expression (frag->fr_symbol);
+  line_seg = subseg_get (".debug_line", 0);
+
+  /* The DW_LNS_fixed_advance_pc opcode has a 2-byte operand so it can
+     advance the address by at most 64K.  Linker relaxation (without
+     which this function would not be used) could change the operand by
+     an unknown amount.  If the address increment is getting close to
+     the limit, just reset the address.  */
+  if (addr_delta > 50000)
+    {
+      symbolS *to_sym;
+      expressionS expr;
+
+      assert (exp->X_op = O_subtract);
+      to_sym = exp->X_add_symbol;
+
+      *p++ = DW_LNS_extended_op;
+      p += output_leb128 (p, sizeof_address + 1, 0);
+      *p++ = DW_LNE_set_address;
+      expr.X_op = O_symbol;
       expr.X_add_symbol = to_sym;
-      expr.X_op_symbol = from_sym;
       expr.X_add_number = 0;
-      emit_expr (&expr, 2);
-
-      out_opcode (DW_LNS_extended_op);
-      out_byte (1);
-      out_opcode (DW_LNE_end_sequence);
-      return;
+      subseg_change (line_seg, 0);
+      emit_expr_fix (&expr, sizeof_address, frag, p);
+      p += sizeof_address;
+    }
+  else
+    {
+      *p++ = DW_LNS_fixed_advance_pc;
+      subseg_change (line_seg, 0);
+      emit_expr_fix (exp, 2, frag, p);
+      p += 2;
     }
 
-  out_opcode (DW_LNS_advance_line);
-  out_sleb128 (line_delta);
-
-  out_opcode (DW_LNS_fixed_advance_pc);
-  expr.X_op = O_subtract;
-  expr.X_add_symbol = to_sym;
-  expr.X_op_symbol = from_sym;
-  expr.X_add_number = 0;
-  emit_expr (&expr, 2);
+  if (line_delta == INT_MAX)
+    {
+      *p++ = DW_LNS_extended_op;
+      *p++ = 1;
+      *p++ = DW_LNE_end_sequence;
+    }
+  else
+    *p++ = DW_LNS_copy;
 
-  out_opcode (DW_LNS_copy);
+  assert (p == end);
 }
 
 /* Generate a variant frag that we can use to relax address/line
@@ -1058,7 +1112,11 @@ relax_inc_line_addr (int line_delta, sym
 
   /* The maximum size of the frag is the line delta with a maximum
      sized address delta.  */
-  max_chars = size_inc_line_addr (line_delta, -DWARF2_LINE_MIN_INSN_LENGTH);
+  if (DWARF2_USE_FIXED_ADVANCE_PC)
+    max_chars = size_fixed_inc_line_addr (line_delta,
+					  -DWARF2_LINE_MIN_INSN_LENGTH);
+  else
+    max_chars = size_inc_line_addr (line_delta, -DWARF2_LINE_MIN_INSN_LENGTH);
 
   frag_var (rs_dwarf2dbg, max_chars, max_chars, 1,
 	    make_expr_symbol (&expr), line_delta, NULL);
@@ -1075,7 +1133,10 @@ dwarf2dbg_estimate_size_before_relax (fr
   int size;
 
   addr_delta = resolve_symbol_value (frag->fr_symbol);
-  size = size_inc_line_addr (frag->fr_offset, addr_delta);
+  if (DWARF2_USE_FIXED_ADVANCE_PC)
+    size = size_fixed_inc_line_addr (frag->fr_offset, addr_delta);
+  else
+    size = size_inc_line_addr (frag->fr_offset, addr_delta);
 
   frag->fr_subtype = size;
 
@@ -1113,8 +1174,13 @@ dwarf2dbg_convert_frag (fragS *frag)
      course, have allocated enough memory earlier.  */
   assert (frag->fr_var >= (int) frag->fr_subtype);
 
-  emit_inc_line_addr (frag->fr_offset, addr_diff,
-		      frag->fr_literal + frag->fr_fix, frag->fr_subtype);
+  if (DWARF2_USE_FIXED_ADVANCE_PC)
+    emit_fixed_inc_line_addr (frag->fr_offset, addr_diff, frag,
+			      frag->fr_literal + frag->fr_fix,
+			      frag->fr_subtype);
+  else
+    emit_inc_line_addr (frag->fr_offset, addr_diff,
+			frag->fr_literal + frag->fr_fix, frag->fr_subtype);
 
   frag->fr_fix += frag->fr_subtype;
   frag->fr_type = rs_fill;
@@ -1192,9 +1258,7 @@ process_entries (segT seg, struct line_e
 	  out_set_addr (lab);
 	  out_inc_line_addr (line_delta, 0);
 	}
-      else if (DWARF2_USE_FIXED_ADVANCE_PC)
-	out_fixed_inc_line_addr (line_delta, lab, last_lab);
-      else if (frag == last_frag)
+      else if (frag == last_frag && ! DWARF2_USE_FIXED_ADVANCE_PC)
 	out_inc_line_addr (line_delta, frag_ofs - last_frag_ofs);
       else
 	relax_inc_line_addr (line_delta, lab, last_lab);
@@ -1213,12 +1277,7 @@ process_entries (segT seg, struct line_e
   /* Emit a DW_LNE_end_sequence for the end of the section.  */
   frag = last_frag_for_seg (seg);
   frag_ofs = get_frag_fix (frag, seg);
-  if (DWARF2_USE_FIXED_ADVANCE_PC)
-    {
-      lab = symbol_temp_new (seg, frag_ofs, frag);
-      out_fixed_inc_line_addr (INT_MAX, lab, last_lab);
-    }
-  else if (frag == last_frag)
+  if (frag == last_frag && ! DWARF2_USE_FIXED_ADVANCE_PC)
     out_inc_line_addr (INT_MAX, frag_ofs - last_frag_ofs);
   else
     {
Index: read.h
===================================================================
RCS file: /cvs/src/src/gas/read.h,v
retrieving revision 1.34
diff -u -p -r1.34 read.h
--- read.h	3 Jul 2007 11:01:03 -0000	1.34
+++ read.h	7 Jan 2008 22:03:24 -0000
@@ -111,6 +111,7 @@ extern void add_include_dir (char *path)
 extern void cons (int nbytes);
 extern void demand_empty_rest_of_line (void);
 extern void emit_expr (expressionS *exp, unsigned int nbytes);
+extern void emit_expr_fix (expressionS *, unsigned int, fragS *, char *);
 extern void equals (char *sym_name, int reassign);
 extern void float_cons (int float_type);
 extern void ignore_rest_of_line (void);
Index: read.c
===================================================================
RCS file: /cvs/src/src/gas/read.c,v
retrieving revision 1.134
diff -u -p -r1.134 read.c
--- read.c	4 Nov 2007 23:49:08 -0000	1.134
+++ read.c	7 Jan 2008 22:03:26 -0000
@@ -4175,41 +4175,45 @@ emit_expr (expressionS *exp, unsigned in
 	}
     }
   else
-    {
-      memset (p, 0, nbytes);
+    emit_expr_fix (exp, nbytes, frag_now, p);
+}
+
+void
+emit_expr_fix (expressionS *exp, unsigned int nbytes, fragS *frag, char *p)
+{
+  memset (p, 0, nbytes);
 
-      /* Now we need to generate a fixS to record the symbol value.  */
+  /* Generate a fixS to record the symbol value.  */
 
 #ifdef TC_CONS_FIX_NEW
-      TC_CONS_FIX_NEW (frag_now, p - frag_now->fr_literal, nbytes, exp);
+  TC_CONS_FIX_NEW (frag, p - frag->fr_literal, nbytes, exp);
 #else
-      {
-	bfd_reloc_code_real_type r;
+  {
+    bfd_reloc_code_real_type r;
 
-	switch (nbytes)
-	  {
-	  case 1:
-	    r = BFD_RELOC_8;
-	    break;
-	  case 2:
-	    r = BFD_RELOC_16;
-	    break;
-	  case 4:
-	    r = BFD_RELOC_32;
-	    break;
-	  case 8:
-	    r = BFD_RELOC_64;
-	    break;
-	  default:
-	    as_bad (_("unsupported BFD relocation size %u"), nbytes);
-	    r = BFD_RELOC_32;
-	    break;
-	  }
-	fix_new_exp (frag_now, p - frag_now->fr_literal, (int) nbytes, exp,
-		     0, r);
+    switch (nbytes)
+      {
+      case 1:
+	r = BFD_RELOC_8;
+	break;
+      case 2:
+	r = BFD_RELOC_16;
+	break;
+      case 4:
+	r = BFD_RELOC_32;
+	break;
+      case 8:
+	r = BFD_RELOC_64;
+	break;
+      default:
+	as_bad (_("unsupported BFD relocation size %u"), nbytes);
+	r = BFD_RELOC_32;
+	break;
       }
+    fix_new_exp (frag, p - frag->fr_literal, (int) nbytes, exp,
+		 0, r);
+  }
 #endif
-    }
 }
 
 #ifdef BITFIELD_CONS_EXPRESSIONS
Index: testsuite/gas/lns/lns.exp
===================================================================
RCS file: /cvs/src/src/gas/testsuite/gas/lns/lns.exp,v
retrieving revision 1.8
diff -u -r1.8 lns.exp
--- testsuite/gas/lns/lns.exp	19 Nov 2007 18:15:53 -0000	1.8
+++ testsuite/gas/lns/lns.exp	7 Jan 2008 23:07:08 -0000
@@ -22,6 +22,7 @@
     # Use alternate file for targets using DW_LNS_fixed_advance_pc opcodes.
     if { [istarget xtensa-*-*] } {
       run_dump_test "lns-common-1-alt"
+      run_dump_test "lns-big-delta"
     } elseif { [istarget ia64*-*-*] } {
       run_dump_test "lns-common-1" { { source "lns-common-1-ia64.s" } }
     } else {
--- /dev/null	2007-04-16 22:20:02.000000000 -0700
+++ testsuite/gas/lns/lns-big-delta.s	2008-01-07 14:25:42.000000000 -0800
@@ -0,0 +1,5 @@
+	.file 1 "foo.c"
+	.loc 1 1 0
+	.loc 1 2 0
+	.space	75000
+	.loc 1 3 0
--- /dev/null	2007-04-16 22:20:02.000000000 -0700
+++ testsuite/gas/lns/lns-big-delta.d	2008-01-07 14:39:53.000000000 -0800
@@ -0,0 +1,14 @@
+#source: lns-big-delta.s
+#readelf: -wl
+#name: lns-big-delta
+Dump of debug contents of section \.debug_line:
+#...
+ Line Number Statements:
+  Extended opcode 2: set Address to .*
+  Copy
+  Advance Line by 1 to 2
+  Extended opcode 2: set Address to .*
+  Copy
+  Advance PC by fixed size amount 0 to .*
+  Extended opcode 1: End of Sequence
+#pass

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