[PATCH] gdb: Don't hard code 0 as end marker in GDB's line table

Andrew Burgess
2020-07-20T12:55:13Z

Currently GDB hard-codes the use of 0 as the end of sequence marker in
its line tables. After this commit GDB now uses a named
constant (linetable_entry::end_marker) as the end of sequence marker.
The value of this constant is -1, not 0. This change was made in
order to aid fixing bug PR gdb/26243.

Bug PR gdb/26243 is about allowing line number 0 to be used to
indicate a program address that has no corresponding source line (this
use is specified in the DWARF standard). Currently GDB can't use line
number 0 as this line number is hard coded as the end of sequence
marker, but, after this commit line number 0 no longer has any special
meaning.

This commit does not fix PR gdb/26243, but it is step towards allowing
that issue to be fixed.

gdb/ChangeLog:

PR gdb/26243
* buildsym.c (buildsym_compunit::record_line): Add an assert for
the incoming line number. Update comments to not mention 0
specifically. Update to check for linetable_entry::end_marker
rather than 0.
* disasm.c (do_mixed_source_and_assembly_deprecated): Check for
linetable_entry::end_marker not 0.
(do_mixed_source_and_assembly): Likewise.
* dwarf2/read.c (dwarf_finish_line): Pass
linetable_entry::end_marker not 0.
* mdebugread.c (add_line): Set is_stmt field.
* python/py-linetable.c (ltpy_get_all_source_lines): Check for
linetable_entry::end_marker not 0, update comments.
(ltpy_iternext): Likewise.
* record-btrace.c (btrace_find_line_range): Likewise.
* symmisc.c (maintenance_print_one_line_table): Likewise.
* symtab.c (find_pc_sect_line): Likewise.
(find_line_symtab): Likewise.
(skip_prologue_using_sal): Likewise.
* symtab.h (linetable_entry::end_marker): New const static member
variable. Add a static assert for this field.
* xcoffread.c (arrange_linetable): Check for
linetable_entry::end_marker not 0.
---
gdb/ChangeLog | 26 ++++++++++++++++++++++++++
gdb/buildsym.c | 36 ++++++++++++++++++++----------------
gdb/disasm.c | 9 +++++----
gdb/dwarf2/read.c | 3 ++-
gdb/mdebugread.c | 1 +
gdb/python/py-linetable.c | 13 +++++++------
gdb/record-btrace.c | 3 ++-
gdb/symmisc.c | 2 +-
gdb/symtab.c | 22 +++++++++++++---------
gdb/symtab.h | 12 ++++++++++++
gdb/xcoffread.c | 11 +++++++----
11 files changed, 96 insertions(+), 42 deletions(-)

diff --git a/gdb/buildsym.c b/gdb/buildsym.c
index bd0ca491401..e6d4dc117c1 100644
--- a/gdb/buildsym.c
+++ b/gdb/buildsym.c
@@ -671,6 +671,10 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
{
struct linetable_entry *e;

+ /* Is this an asset? Or is this processing user input and so should we
+ be handling, or throwing an error for invalid data? */
+ gdb_assert (line == linetable_entry::end_marker || line >= 0);
+
/* Make sure line vector exists and is big enough. */
if (!subfile->line_vector)
{
@@ -692,20 +696,19 @@ buildsym_compunit::record_line (struct subfile *subfile, int line,
* sizeof (struct linetable_entry))));
}

- /* Normally, we treat lines as unsorted. But the end of sequence
- marker is special. We sort line markers at the same PC by line
- number, so end of sequence markers (which have line == 0) appear
- first. This is right if the marker ends the previous function,
- and there is no padding before the next function. But it is
- wrong if the previous line was empty and we are now marking a
- switch to a different subfile. We must leave the end of sequence
- marker at the end of this group of lines, not sort the empty line
- to after the marker. The easiest way to accomplish this is to
- delete any empty lines from our table, if they are followed by
- end of sequence markers. All we lose is the ability to set
- breakpoints at some lines which contain no instructions
- anyway. */
- if (line == 0)
+ /* Normally, we treat lines as unsorted. But the end of sequence marker
+ is special. We sort line markers at the same PC by line number, so
+ end of sequence markers (which have line ==
+ linetable_entry::end_marker) appear first. This is right if the
+ marker ends the previous function, and there is no padding before the
+ next function. But it is wrong if the previous line was empty and we
+ are now marking a switch to a different subfile. We must leave the
+ end of sequence marker at the end of this group of lines, not sort the
+ empty line to after the marker. The easiest way to accomplish this is
+ to delete any empty lines from our table, if they are followed by end
+ of sequence markers. All we lose is the ability to set breakpoints at
+ some lines which contain no instructions anyway. */
+ if (line == linetable_entry::end_marker)
{
while (subfile->line_vector->nitems > 0)
{
@@ -944,8 +947,9 @@ buildsym_compunit::end_symtab_with_blockvector (struct block *static_block,
const linetable_entry &ln2) -> bool
{
if (ln1.pc == ln2.pc
- && ((ln1.line == 0) != (ln2.line == 0)))
- return ln1.line == 0;
+ && ((ln1.line == linetable_entry::end_marker)
+ != (ln2.line == linetable_entry::end_marker)))
+ return ln1.line == linetable_entry::end_marker;

return (ln1.pc < ln2.pc);
};
diff --git a/gdb/disasm.c b/gdb/disasm.c
index 143ba2f59b9..4b7cc88fbfb 100644
--- a/gdb/disasm.c
+++ b/gdb/disasm.c
@@ -383,7 +383,7 @@ do_mixed_source_and_assembly_deprecated
continue;

/* Skip any end-of-function markers. */
- if (le[i].line == 0)
+ if (le[i].line == linetable_entry::end_marker)
continue;

mle[newlines].line = le[i].line;
@@ -571,7 +571,7 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch,
gdb::optional<ui_out_emit_list> list_emitter;

last_symtab = NULL;
- last_line = 0;
+ last_line = linetable_entry::end_marker;
pc = low;

while (pc < high)
@@ -591,7 +591,7 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch,

/* If this is the first line of output, check for any preceding
lines. */
- if (last_line == 0
+ if (last_line == linetable_entry::end_marker
&& first_le != NULL
&& first_le->line < sal.line)
{
@@ -604,7 +604,8 @@ do_mixed_source_and_assembly (struct gdbarch *gdbarch,
/* Same source file as last time. */
if (sal.symtab != NULL)
{
- if (sal.line > last_line + 1 && last_line != 0)
+ if (last_line != linetable_entry::end_marker
+ && sal.line > last_line + 1)
{
int l;

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 39ed455def5..bdbecf640ff 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -20390,7 +20390,8 @@ dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
paddress (gdbarch, address));
}

- dwarf_record_line_1 (gdbarch, subfile, 0, address, true, cu);
+ dwarf_record_line_1 (gdbarch, subfile, linetable_entry::end_marker,
+ address, true, cu);
}

void
diff --git a/gdb/mdebugread.c b/gdb/mdebugread.c
index d38372041d7..01029fbd971 100644
--- a/gdb/mdebugread.c
+++ b/gdb/mdebugread.c
@@ -4516,6 +4516,7 @@ add_line (struct linetable *lt, int lineno, CORE_ADDR adr, int last)
return lineno;

lt->item[lt->nitems].line = lineno;
+ lt->item[lt->nitems].is_stmt = 1;
lt->item[lt->nitems++].pc = adr << 2;
return lineno;
}
diff --git a/gdb/python/py-linetable.c b/gdb/python/py-linetable.c
index 858313bb22d..22a44fe5f36 100644
--- a/gdb/python/py-linetable.c
+++ b/gdb/python/py-linetable.c
@@ -238,10 +238,11 @@ ltpy_get_all_source_lines (PyObject *self, PyObject *args)
{
item = &(SYMTAB_LINETABLE (symtab)->item[index]);

- /* 0 is used to signify end of line table information. Do not
- include in the source set. */
- if (item->line > 0)
+ /* The special value linetable_entry::end_marker is used to signify
+ end of line table information. Do not include in the source set. */
+ if (item->line != linetable_entry::end_marker)
{
+ gdb_assert (item->line >= 0);
gdbpy_ref<> line = gdb_py_object_from_longest (item->line);

if (line == NULL)
@@ -407,9 +408,9 @@ ltpy_iternext (PyObject *self)

item = &(SYMTAB_LINETABLE (symtab)->item[iter_obj->current_index]);

- /* Skip over internal entries such as 0. 0 signifies the end of
- line table data and is not useful to the API user. */
- while (item->line < 1)
+ /* Skip over internal entries such as the end of sequence marker,
+ linetable_entry::end_marker as this is not useful to the API user. */
+ while (item->line == linetable_entry::end_marker)
{
iter_obj->current_index++;

diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 718de62f280..36ae671fa90 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -732,7 +732,8 @@ btrace_find_line_range (CORE_ADDR pc)
possibly adding more line numbers to the range. At the time this
change was made I was unsure how to test this so chose to go with
maintaining the existing experience. */
- if ((lines[i].pc == pc) && (lines[i].line != 0)
+ if ((lines[i].pc == pc)
+ && (lines[i].line != linetable_entry::end_marker)
&& (lines[i].is_stmt == 1))
range = btrace_line_range_add (range, lines[i].line);
}
diff --git a/gdb/symmisc.c b/gdb/symmisc.c
index fc56cfa9381..9b05ca2f4da 100644
--- a/gdb/symmisc.c
+++ b/gdb/symmisc.c
@@ -1036,7 +1036,7 @@ maintenance_print_one_line_table (struct symtab *symtab, void *data)
item = &linetable->item [i];
ui_out_emit_tuple tuple_emitter (uiout, nullptr);
uiout->field_signed ("index", i);
- if (item->line > 0)
+ if (item->line != linetable_entry::end_marker)
uiout->field_signed ("line", item->line);
else
uiout->field_string ("line", _("END"));
diff --git a/gdb/symtab.c b/gdb/symtab.c
index f96ad9554d9..02a08481008 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -3248,7 +3248,8 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
save prev if it represents the end of a function (i.e. line number
0) instead of a real line. */

- if (prev && prev->line && (!best || prev->pc > best->pc))
+ if (prev && prev->line != linetable_entry::end_marker
+ && (!best || prev->pc > best->pc))
{
best = prev;
best_symtab = iter_s;
@@ -3264,7 +3265,8 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
{
struct linetable_entry *tmp = best;
while (tmp > first && (tmp - 1)->pc == tmp->pc
- && (tmp - 1)->line != 0 && !tmp->is_stmt)
+ && (tmp - 1)->line != linetable_entry::end_marker
+ && !tmp->is_stmt)
--tmp;
if (tmp->is_stmt)
best = tmp;
@@ -3291,7 +3293,7 @@ find_pc_sect_line (CORE_ADDR pc, struct obj_section *section, int notcurrent)
don't make some up. */
val.pc = pc;
}
- else if (best->line == 0)
+ else if (best->line == linetable_entry::end_marker)
{
/* If our best fit is in a range of PC's for which no line
number info is available (line number is zero) then we didn't
@@ -3378,14 +3380,14 @@ find_line_symtab (struct symtab *sym_tab, int line,
the GLOBAL_BLOCK of a symtab has a begin and end address). */

/* BEST is the smallest linenumber > LINE so far seen,
- or 0 if none has been seen so far.
+ or the end marker if none has been seen so far.
BEST_INDEX and BEST_LINETABLE identify the item for it. */
int best;

if (best_index >= 0)
best = best_linetable->item[best_index].line;
else
- best = 0;
+ best = linetable_entry::end_marker;

for (objfile *objfile : current_program_space->objfiles ())
{
@@ -3419,7 +3421,8 @@ find_line_symtab (struct symtab *sym_tab, int line,
best_symtab = s;
goto done;
}
- if (best == 0 || l->item[ind].line < best)
+ if (best == linetable_entry::end_marker
+ || l->item[ind].line < best)
{
best = l->item[ind].line;
best_index = ind;
@@ -3718,7 +3721,8 @@ skip_prologue_using_lineinfo (CORE_ADDR func_addr, struct symtab *symtab)
/* Don't use line numbers of zero, they mark special entries in
the table. See the commentary on symtab.h before the
definition of struct linetable. */
- if (item->line > 0 && func_start <= item->pc && item->pc < func_end)
+ if (item->line != linetable_entry::end_marker
+ && func_start <= item->pc && item->pc < func_end)
return item->pc;
}

@@ -3946,11 +3950,11 @@ skip_prologue_using_sal (struct gdbarch *gdbarch, CORE_ADDR func_addr)
/* Skip any earlier lines, and any end-of-sequence marker
from a previous function. */
while (linetable->item[idx].pc != prologue_sal.pc
- || linetable->item[idx].line == 0)
+ || linetable->item[idx].line == linetable_entry::end_marker)
idx++;

if (idx+1 < linetable->nitems
- && linetable->item[idx+1].line != 0
+ && linetable->item[idx+1].line != linetable_entry::end_marker
&& linetable->item[idx+1].pc == start_pc)
return start_pc;
}
diff --git a/gdb/symtab.h b/gdb/symtab.h
index 0b186554ea1..83d60d8d64b 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1306,6 +1306,11 @@ struct rust_vtable_symbol : public symbol

struct linetable_entry
{
+ /* Special value placed into the LINE field to indicate an end of
+ sequence in the line table. */
+
+ static const int end_marker = -1;
+
/* The line number for this entry. */
int line;

@@ -1316,6 +1321,13 @@ struct linetable_entry
CORE_ADDR pc;
};

+/* Normally line numbers in a program are positive integers greater than
+ zero. The line number 0 is reserved in DWARF to indicate instructions
+ that don't have associated source code. The end marker then must be
+ less than zero. */
+
+gdb_static_assert (linetable_entry::end_marker < 0);
+
/* The order of entries in the linetable is significant. They should
be sorted by increasing values of the pc field. If there is more than
one entry for a given pc, then I'm not sure what should happen (and
diff --git a/gdb/xcoffread.c b/gdb/xcoffread.c
index a792c0fea2e..4c99c2da3ef 100644
--- a/gdb/xcoffread.c
+++ b/gdb/xcoffread.c
@@ -435,7 +435,7 @@ arrange_linetable (struct linetable *oldLineTb)
if (oldLineTb->item[ii].is_stmt == 0)
continue;

- if (oldLineTb->item[ii].line == 0)
+ if (oldLineTb->item[ii].line == linetable_entry::end_marker)
{ /* Function entry found. */
if (function_count >= fentry_size)
{ /* Make sure you have room. */
@@ -477,9 +477,11 @@ arrange_linetable (struct linetable *oldLineTb)
a function begin. */

newline = 0;
- if (oldLineTb->item[0].line != 0)
+ if (oldLineTb->item[0].line != linetable_entry::end_marker)
for (newline = 0;
- newline < oldLineTb->nitems && oldLineTb->item[newline].line; ++newline)
+ newline < oldLineTb->nitems
+ && oldLineTb->item[newline].line != linetable_entry::end_marker;
+ ++newline)
newLineTb->item[newline] = oldLineTb->item[newline];

/* Now copy function lines one by one. */
@@ -498,7 +500,8 @@ arrange_linetable (struct linetable *oldLineTb)
}

for (jj = fentry[ii].line + 1;
- jj < oldLineTb->nitems && oldLineTb->item[jj].line != 0;
+ jj < oldLineTb->nitems
+ && oldLineTb->item[jj].line != linetable_entry::end_marker;
++jj, ++newline)
newLineTb->item[newline] = oldLineTb->item[jj];
}
--
2.25.4