aboutsummaryrefslogtreecommitdiff
path: root/gas/dwarf2dbg.c
diff options
context:
space:
mode:
Diffstat (limited to 'gas/dwarf2dbg.c')
-rw-r--r--gas/dwarf2dbg.c313
1 files changed, 304 insertions, 9 deletions
diff --git a/gas/dwarf2dbg.c b/gas/dwarf2dbg.c
index e02b6e8..4f70330 100644
--- a/gas/dwarf2dbg.c
+++ b/gas/dwarf2dbg.c
@@ -162,13 +162,20 @@
#define TC_PARSE_CONS_RETURN_NONE BFD_RELOC_NONE
#endif
-struct line_entry {
+struct line_entry
+{
struct line_entry *next;
symbolS *label;
struct dwarf2_line_info loc;
};
-struct line_subseg {
+/* Don't change the offset of next in line_entry. set_or_check_view
+ calls in dwarf2_gen_line_info_1 depend on it. */
+static char unused[offsetof(struct line_entry, next) ? -1 : 1]
+ATTRIBUTE_UNUSED;
+
+struct line_subseg
+{
struct line_subseg *next;
subsegT subseg;
struct line_entry *head;
@@ -176,7 +183,8 @@ struct line_subseg {
struct line_entry **pmove_tail;
};
-struct line_seg {
+struct line_seg
+{
struct line_seg *next;
segT seg;
struct line_subseg *head;
@@ -188,7 +196,8 @@ struct line_seg {
static struct line_seg *all_segs;
static struct line_seg **last_seg_ptr;
-struct file_entry {
+struct file_entry
+{
const char *filename;
unsigned int dir;
};
@@ -212,12 +221,21 @@ bfd_boolean dwarf2_loc_directive_seen;
bfd_boolean dwarf2_loc_mark_labels;
/* Current location as indicated by the most recent .loc directive. */
-static struct dwarf2_line_info current = {
+static struct dwarf2_line_info current =
+{
1, 1, 0, 0,
DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0,
- 0
+ 0, NULL
};
+/* This symbol is used to recognize view number forced resets in loc
+ lists. */
+static symbolS *force_reset_view;
+
+/* This symbol evaluates to an expression that, if nonzero, indicates
+ some view assert check failed. */
+static symbolS *view_assert_failed;
+
/* The size of an address on the target. */
static unsigned int sizeof_address;
@@ -283,6 +301,168 @@ get_line_subseg (segT seg, subsegT subseg, bfd_boolean create_p)
return lss;
}
+/* (Un)reverse the line_entry list starting from H. */
+
+static struct line_entry *
+reverse_line_entry_list (struct line_entry *h)
+{
+ struct line_entry *p = NULL, *e, *n;
+
+ for (e = h; e; e = n)
+ {
+ n = e->next;
+ e->next = p;
+ p = e;
+ }
+ return p;
+}
+
+/* Compute the view for E based on the previous entry P. If we
+ introduce an (undefined) view symbol for P, and H is given (P must
+ be the tail in this case), introduce view symbols for earlier list
+ entries as well, until one of them is constant. */
+
+static void
+set_or_check_view (struct line_entry *e, struct line_entry *p,
+ struct line_entry *h)
+{
+ expressionS viewx;
+
+ memset (&viewx, 0, sizeof (viewx));
+ viewx.X_unsigned = 1;
+
+ /* First, compute !(E->label > P->label), to tell whether or not
+ we're to reset the view number. If we can't resolve it to a
+ constant, keep it symbolic. */
+ if (!p || (e->loc.view == force_reset_view && force_reset_view))
+ {
+ viewx.X_op = O_constant;
+ viewx.X_add_number = 0;
+ viewx.X_add_symbol = NULL;
+ viewx.X_op_symbol = NULL;
+ }
+ else
+ {
+ viewx.X_op = O_gt;
+ viewx.X_add_number = 0;
+ viewx.X_add_symbol = e->label;
+ viewx.X_op_symbol = p->label;
+ resolve_expression (&viewx);
+ if (viewx.X_op == O_constant)
+ viewx.X_add_number = !viewx.X_add_number;
+ else
+ {
+ viewx.X_add_symbol = make_expr_symbol (&viewx);
+ viewx.X_add_number = 0;
+ viewx.X_op_symbol = NULL;
+ viewx.X_op = O_logical_not;
+ }
+ }
+
+ if (S_IS_DEFINED (e->loc.view) && symbol_constant_p (e->loc.view))
+ {
+ expressionS *value = symbol_get_value_expression (e->loc.view);
+ /* We can't compare the view numbers at this point, because in
+ VIEWX we've only determined whether we're to reset it so
+ far. */
+ if (viewx.X_op == O_constant)
+ {
+ if (!value->X_add_number != !viewx.X_add_number)
+ as_bad (_("view number mismatch"));
+ }
+ /* Record the expression to check it later. It is the result of
+ a logical not, thus 0 or 1. We just add up all such deferred
+ expressions, and resolve it at the end. */
+ else if (!value->X_add_number)
+ {
+ symbolS *deferred = make_expr_symbol (&viewx);
+ if (view_assert_failed)
+ {
+ expressionS chk;
+ memset (&chk, 0, sizeof (chk));
+ chk.X_unsigned = 1;
+ chk.X_op = O_add;
+ chk.X_add_number = 0;
+ chk.X_add_symbol = view_assert_failed;
+ chk.X_op_symbol = deferred;
+ deferred = make_expr_symbol (&chk);
+ }
+ view_assert_failed = deferred;
+ }
+ }
+
+ if (viewx.X_op != O_constant || viewx.X_add_number)
+ {
+ expressionS incv;
+
+ if (!p->loc.view)
+ {
+ p->loc.view = symbol_temp_make ();
+ gas_assert (!S_IS_DEFINED (p->loc.view));
+ }
+
+ memset (&incv, 0, sizeof (incv));
+ incv.X_unsigned = 1;
+ incv.X_op = O_symbol;
+ incv.X_add_symbol = p->loc.view;
+ incv.X_add_number = 1;
+
+ if (viewx.X_op == O_constant)
+ {
+ gas_assert (viewx.X_add_number == 1);
+ viewx = incv;
+ }
+ else
+ {
+ viewx.X_add_symbol = make_expr_symbol (&viewx);
+ viewx.X_add_number = 0;
+ viewx.X_op_symbol = make_expr_symbol (&incv);
+ viewx.X_op = O_multiply;
+ }
+ }
+
+ if (!S_IS_DEFINED (e->loc.view))
+ {
+ symbol_set_value_expression (e->loc.view, &viewx);
+ S_SET_SEGMENT (e->loc.view, absolute_section);
+ symbol_set_frag (e->loc.view, &zero_address_frag);
+ }
+
+ /* Define and attempt to simplify any earlier views needed to
+ compute E's. */
+ if (h && p && p->loc.view && !S_IS_DEFINED (p->loc.view))
+ {
+ struct line_entry *h2;
+ /* Reverse the list to avoid quadratic behavior going backwards
+ in a single-linked list. */
+ struct line_entry *r = reverse_line_entry_list (h);
+
+ gas_assert (r == p);
+ /* Set or check views until we find a defined or absent view. */
+ do
+ set_or_check_view (r, r->next, NULL);
+ while (r->next && r->next->loc.view && !S_IS_DEFINED (r->next->loc.view)
+ && (r = r->next));
+
+ /* Unreverse the list, so that we can go forward again. */
+ h2 = reverse_line_entry_list (p);
+ gas_assert (h2 == h);
+
+ /* Starting from the last view we just defined, attempt to
+ simplify the view expressions, until we do so to P. */
+ do
+ {
+ gas_assert (S_IS_DEFINED (r->loc.view));
+ resolve_expression (symbol_get_value_expression (r->loc.view));
+ }
+ while (r != p && (r = r->next));
+
+ /* Now that we've defined and computed all earlier views that might
+ be needed to compute E's, attempt to simplify it. */
+ resolve_expression (symbol_get_value_expression (e->loc.view));
+ }
+}
+
/* Record an entry for LOC occurring at LABEL. */
static void
@@ -297,6 +477,12 @@ dwarf2_gen_line_info_1 (symbolS *label, struct dwarf2_line_info *loc)
e->loc = *loc;
lss = get_line_subseg (now_seg, now_subseg, TRUE);
+
+ if (loc->view)
+ set_or_check_view (e,
+ !lss->head ? NULL : (struct line_entry *)lss->ptail,
+ lss->head);
+
*lss->ptail = e;
lss->ptail = &e->next;
}
@@ -350,12 +536,16 @@ dwarf2_where (struct dwarf2_line_info *line)
{
if (debug_type == DEBUG_DWARF2)
{
- const char *filename = as_where (&line->line);
+ const char *filename;
+
+ memset (line, 0, sizeof (*line));
+ filename = as_where (&line->line);
line->filenum = get_filenum (filename, 0);
line->column = 0;
line->flags = DWARF2_FLAG_IS_STMT;
line->isa = current.isa;
line->discriminator = current.discriminator;
+ line->view = NULL;
}
else
*line = current;
@@ -380,7 +570,9 @@ dwarf2_emit_insn (int size)
{
struct dwarf2_line_info loc;
- if (!dwarf2_loc_directive_seen && debug_type != DEBUG_DWARF2)
+ if (debug_type != DEBUG_DWARF2
+ ? !dwarf2_loc_directive_seen
+ : !seen_at_least_1_file ())
return;
dwarf2_where (&loc);
@@ -721,6 +913,54 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
return;
}
}
+ else if (strcmp (p, "view") == 0)
+ {
+ symbolS *sym;
+
+ (void) restore_line_pointer (c);
+ SKIP_WHITESPACE ();
+
+ if (ISDIGIT (*input_line_pointer)
+ || *input_line_pointer == '-')
+ {
+ bfd_boolean force_reset = *input_line_pointer == '-';
+
+ value = get_absolute_expression ();
+ if (value != 0)
+ {
+ as_bad (_("numeric view can only be asserted to zero"));
+ return;
+ }
+ if (force_reset && force_reset_view)
+ sym = force_reset_view;
+ else
+ {
+ sym = symbol_temp_new (absolute_section, value,
+ &zero_address_frag);
+ if (force_reset)
+ force_reset_view = sym;
+ }
+ }
+ else
+ {
+ char *name = read_symbol_name ();
+
+ if (!name)
+ return;
+ sym = symbol_find_or_make (name);
+ if (S_IS_DEFINED (sym))
+ {
+ if (!S_CAN_BE_REDEFINED (sym))
+ as_bad (_("symbol `%s' is already defined"), name);
+ else
+ sym = symbol_clone (sym, 1);
+ S_SET_SEGMENT (sym, undefined_section);
+ S_SET_VALUE (sym, 0);
+ symbol_set_frag (sym, &zero_address_frag);
+ }
+ }
+ current.view = sym;
+ }
else
{
as_bad (_("unknown .loc sub-directive `%s'"), p);
@@ -734,6 +974,10 @@ dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED)
demand_empty_rest_of_line ();
dwarf2_loc_directive_seen = TRUE;
debug_type = DEBUG_NONE;
+
+ /* If we were given a view id, emit the row right away. */
+ if (current.view)
+ dwarf2_emit_insn (0);
}
void
@@ -1362,7 +1606,19 @@ process_entries (segT seg, struct line_entry *e)
frag = symbol_get_frag (lab);
frag_ofs = S_GET_VALUE (lab);
- if (last_frag == NULL)
+ if (last_frag == NULL
+ || (e->loc.view == force_reset_view && force_reset_view
+ /* If we're going to reset the view, but we know we're
+ advancing the PC, we don't have to force with
+ set_address. We know we do when we're at the same
+ address of the same frag, and we know we might when
+ we're in the beginning of a frag, and we were at the
+ end of the previous frag. */
+ && (frag == last_frag
+ ? (last_frag_ofs == frag_ofs)
+ : (frag_ofs == 0
+ && ((offsetT)last_frag_ofs
+ >= get_frag_fix (last_frag, seg))))))
{
out_set_addr (lab);
out_inc_line_addr (line_delta, 0);
@@ -1975,3 +2231,42 @@ dwarf2_finish (void)
name_sym, comp_dir_sym, producer_sym);
}
}
+
+/* Perform any deferred checks pertaining to debug information. */
+
+void
+dwarf2dbg_final_check (void)
+{
+ /* Perform reset-view checks. Don't evaluate view_assert_failed
+ recursively: it could be very deep. It's a chain of adds, with
+ each chain element pointing to the next in X_add_symbol, and
+ holding the check value in X_op_symbol. */
+ while (view_assert_failed)
+ {
+ expressionS *expr;
+ symbolS *sym;
+ offsetT failed;
+
+ gas_assert (!symbol_resolved_p (view_assert_failed));
+
+ expr = symbol_get_value_expression (view_assert_failed);
+ sym = view_assert_failed;
+
+ /* If view_assert_failed looks like a compound check in the
+ chain, break it up. */
+ if (expr->X_op == O_add && expr->X_add_number == 0 && expr->X_unsigned)
+ {
+ view_assert_failed = expr->X_add_symbol;
+ sym = expr->X_op_symbol;
+ }
+ else
+ view_assert_failed = NULL;
+
+ failed = resolve_symbol_value (sym);
+ if (!symbol_resolved_p (sym) || failed)
+ {
+ as_bad (_("view number mismatch"));
+ break;
+ }
+ }
+}