This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH] handle large deltas with DWARF fixed_advance_pc
- From: Bob Wilson <bwilson at tensilica dot com>
- To: binutils at sources dot redhat dot com
- Date: Mon, 07 Jan 2008 15:56:45 -0800
- Subject: [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