[RFC/dwarf-2] Add support for included files

Joel Brobecker
2004-01-02T07:25:06Z

[-- Attachment #1: Type: text/plain, Size: 4290 bytes --]

Hello,

Ada95 defines the notion of "separates", which allows a developper
to write the implementation of either a function or a package in
a separate file. Here is a small example:

foo.ads:

package Foo is
procedure Proc;
end Foo;

foo.adb:

Package body Foo is
procedure Proc is separate;
^^^^^^^^^^^
End Foo;

And then the body for procedure "Proc", as a separate in foo-proc.adb:

separate (Foo)
^^^^^^^^^^^^^^

procedure Proc is
begin
null;
end Proc;

This capability can be reproduced in C as well, using #include.
The following example is somewhat ugly with respect to the usual
coding practices, but does reproduce well the capability above:

hello.c:

void
say_hello (void)
{
printf ("Hello world.\n");
}

foo.c:

#include <stdio.h>

#include "hello.c" <--- Reproduces the "is separate"...

int
main (void)
{
say_hello ();
return 0;
}

We have noticed that, if compiled with dwarf-2, GDB is unable to
set a breakpoint inside hello.c using the file:lineno syntax:

% gcc -gdwarf-2 foo.c -o foo
% gdb foo
(gdb) b hello.c:4
No source file named hello.c.
(gdb) list hello.c:4
No source file named hello.c.

However, we can set the breakpoint using the function name:

(gdb) b say_hello
Breakpoint 1 at 0x804833e: file hello.c, line 4.

And then once this breakpoint is set, we can now insert the
breakpoint using the source location:

(gdb) b hello.c:4
Note: breakpoint 1 also set at pc 0x804833e.
Breakpoint 2 at 0x804833e: file hello.c, line 4.
(gdb) list hello.c:4
1 void
2 say_hello (void)
3 {
4 printf ("Hello world.\n");
5 }

In Dwarf-2, the list of included files is linked to the line table.
First, we have "Line Number Program Header" which contains the "file
names" table. And then the program itself which uses the DW_LNS_set_file
opcode to change from file to file. There is also the DW_LNE_defile_file
opcode that can be used to define new files instead of using the "file
names" table.

Our problem here is that we don't process this line table when building
the partial symbols. Hence, we miss the list of included files, and
therefore do not create any partial symtab for them. The problem does
not exist with stabs because the N_SOL and line number information
are embedded inside the rest of the stabs data.

If we want to support the case above, I don't see any other way but to
scan the line table as well. I suggest a fast scan, although I don't
see a simple way to skip the opcodes and their data that are ignored
during our scan without having a good knowledge of the opcode we are
skipping. This makes the function that does the quick scan of the line
table almost as fat as the one that is used to build the GDB line
table, almost a copy except that we ignore most of what we read.

Basically, I just copy/pasted the code from dwarf_decode_lines(),
simplified it for partial symtab processing, and then called it
right after the compilation unit psymtab has been built. Currently,
the function scans the line number program, and records which files
of the "file names" table have really been included in the line program
(that's the "file_included" array).

Here is a patch against GDB 6.0 that I used as a proof of concept.
It does not handle the case where the compiler uses DW_LNE_defile_file
instead of the the "file names" table yet, but that will be taken care
of. Would that be an acceptable approach to suggest for inclusion?

2004-01-02 Joel Brobecker <brobecker@gnat.com>

* dwarf2read.c (read_partial_die): Add new parameter.
(scan_partial_symbols): Update call to read_partial_die.
(dwarf2_add_include_psymtab): New function.
(dwarf2_build_include_psymtabs): New function.
(dwarf2_build_psymtabs_hard): Build the psymtabs for the
included files as well.
(psymtab_to_symtab_1): Build the symtab of all dependencies
as well.

Thank you.
--
Joel

[-- Attachment #2: dwarf2read.c.diff --]
[-- Type: text/plain, Size: 10811 bytes --]

Index: dwarf2read.c
===================================================================
RCS file: /nile.c/cvs/Dev/gdb/gdb-6.0/gdb/dwarf2read.c,v
retrieving revision 1.5
diff -u -p -r1.5 dwarf2read.c
--- dwarf2read.c 19 Nov 2003 18:02:23 -0000 1.5
+++ dwarf2read.c 2 Jan 2004 07:10:08 -0000
@@ -693,7 +693,8 @@ static struct abbrev_info *dwarf2_lookup

static char *read_partial_die (struct partial_die_info *,
bfd *, char *,
- const struct comp_unit_head *);
+ const struct comp_unit_head *,
+ unsigned int *);

static char *read_full_die (struct die_info **, bfd *, char *,
const struct comp_unit_head *);
@@ -1196,6 +1197,195 @@ read_comp_unit_head (struct comp_unit_he
return info_ptr;
}

+static void
+dwarf2_add_include_psymtab (char *name,
+ struct partial_symtab *pst,
+ struct objfile *objfile)
+{
+ struct partial_symtab *subpst = allocate_psymtab (name, objfile);
+
+ /* Copy the private data from the main psymtab. */
+ subpst->section_offsets = pst->section_offsets;
+ PST_PRIVATE (subpst) =
+ (char *) obstack_alloc (&objfile->psymbol_obstack,
+ sizeof (struct dwarf2_pinfo));
+ *PST_PRIVATE (subpst) = *PST_PRIVATE (pst);
+
+ subpst->textlow = 0;
+ subpst->texthigh = 0;
+
+ /* We could save slight bits of space by only making one of these,
+ shared by the entire set of include files. FIXME-someday. */
+ subpst->dependencies = (struct partial_symtab **)
+ obstack_alloc (&objfile->psymbol_obstack,
+ sizeof (struct partial_symtab *));
+ subpst->dependencies[0] = pst;
+ subpst->number_of_dependencies = 1;
+
+ subpst->globals_offset = 0;
+ subpst->n_global_syms = 0;
+ subpst->statics_offset = 0;
+ subpst->n_static_syms = 0;
+
+ subpst->readin = 0;
+ subpst->symtab = 0;
+ subpst->read_symtab = pst->read_symtab;
+}
+
+static void
+dwarf2_build_include_psymtabs (struct comp_unit_head *cu_header,
+ const unsigned int line_offset,
+ struct partial_symtab *pst,
+ struct objfile *objfile)
+{
+ bfd *abfd = objfile->obfd;
+ struct line_header *lh;
+ int file_index;
+ char *line_ptr;
+ char *line_end;
+ unsigned int bytes_read;
+ unsigned char op_code, extended_op, adj_opcode; // All used??? FIXME:JOEL
+ char *file_included;
+
+ lh = dwarf_decode_line_header (line_offset, abfd, cu_header);
+
+ if (lh == NULL)
+ return; /* No includes... */
+
+ line_ptr = lh->statement_program_start;
+ line_end = lh->statement_program_end;
+
+ /* FIXME: brobecker: Add comments. */
+ file_included = alloca (lh->num_file_names * sizeof (char));
+ memset (file_included, 0, lh->num_file_names * sizeof (char));
+
+ /* Read the statement sequences until there's nothing left. */
+ while (line_ptr < line_end)
+ {
+ /* state machine registers */
+ int end_sequence = 0;
+
+ /* Decode the table. */
+ while (!end_sequence)
+ {
+ op_code = read_1_byte (abfd, line_ptr);
+ line_ptr += 1;
+
+ if (op_code >= lh->opcode_base)
+ { /* Special operand. Nothing else to read. */
+ }
+ else switch (op_code)
+ {
+ case DW_LNS_extended_op:
+ line_ptr += 1; /* ignore length */
+ extended_op = read_1_byte (abfd, line_ptr);
+ line_ptr += 1;
+ switch (extended_op)
+ {
+ case DW_LNE_end_sequence:
+ end_sequence = 1;
+ break;
+ case DW_LNE_set_address:
+ read_address (abfd, line_ptr, cu_header, &bytes_read);
+ line_ptr += bytes_read;
+ break;
+ case DW_LNE_define_file:
+ {
+ // char *cur_file;
+ // unsigned int dir_index, mod_time, length;
+
+ // cur_file = read_string (abfd, line_ptr, &bytes_read);
+ // line_ptr += bytes_read;
+ // dir_index =
+ // read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ // line_ptr += bytes_read;
+ // mod_time =
+ // read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ // line_ptr += bytes_read;
+ // length =
+ // read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ // line_ptr += bytes_read;
+ // add_file_name (lh, cur_file, dir_index, mod_time, length);
+ /* Ignore for now, but the compiler is allowed to
+ create the list of files via DW_LNE_define_file
+ entries too. We will likely need to take this
+ into account someday. */
+ read_string (abfd, line_ptr, &bytes_read);
+ line_ptr += bytes_read;
+ read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ line_ptr += bytes_read;
+ read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ line_ptr += bytes_read;
+ read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ line_ptr += bytes_read;
+ }
+ break;
+ default:
+ complaint (&symfile_complaints,
+ "mangled .debug_line section");
+ return;
+ }
+ break;
+ case DW_LNS_copy:
+ break;
+ case DW_LNS_advance_pc:
+ read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ line_ptr += bytes_read;
+ break;
+ case DW_LNS_advance_line:
+ read_signed_leb128 (abfd, line_ptr, &bytes_read);
+ line_ptr += bytes_read;
+ break;
+ case DW_LNS_set_file:
+ {
+ /* lh->include_dirs and lh->file_names are 0-based,
+ but the directory and file name numbers in the
+ statement program are 1-based. */
+ unsigned int file;
+
+ file = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ line_ptr += bytes_read;
+ file_included[file - 1] = 1;
+ }
+ break;
+ case DW_LNS_set_column:
+ read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ line_ptr += bytes_read;
+ break;
+ case DW_LNS_negate_stmt:
+ break;
+ case DW_LNS_set_basic_block:
+ break;
+ case DW_LNS_const_add_pc:
+ break;
+ case DW_LNS_fixed_advance_pc:
+ line_ptr += 2;
+ break;
+ default:
+ { /* Unknown standard opcode, ignore it. */
+ int i;
+ for (i = 0; i < lh->standard_opcode_lengths[op_code]; i++)
+ {
+ (void) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+ line_ptr += bytes_read;
+ }
+ }
+ }
+ }
+ }
+
+ for (file_index = 0; file_index < lh->num_file_names; file_index++)
+ if (file_included[file_index] == 1)
+ {
+ char *include_name = lh->file_names [file_index].name;
+
+ if (strcmp (include_name, pst->filename) != 0)
+ dwarf2_add_include_psymtab (include_name, pst, objfile);
+ }
+
+ free_line_header (lh);
+}
+
/* Build the partial symbol table by doing a quick pass through the
.debug_info and .debug_abbrev sections. */

@@ -1262,6 +1452,8 @@ dwarf2_build_psymtabs_hard (struct objfi
while (info_ptr < dwarf_info_buffer + dwarf_info_size)
{
struct comp_unit_head cu_header;
+ unsigned int line_offset = 0;
+
beg_of_comp_unit = info_ptr;
info_ptr = read_comp_unit_head (&cu_header, info_ptr, abfd);

@@ -1307,7 +1499,7 @@ dwarf2_build_psymtabs_hard (struct objfi

/* Read the compilation unit die */
info_ptr = read_partial_die (&comp_unit_die, abfd, info_ptr,
- &cu_header);
+ &cu_header, &line_offset);

/* Set the language we're debugging */
set_cu_language (comp_unit_die.language);
@@ -1373,6 +1565,10 @@ dwarf2_build_psymtabs_hard (struct objfi

info_ptr = beg_of_comp_unit + cu_header.length
+ cu_header.initial_length_size;
+
+ /* Get the list of files included in the current compilation unit,
+ and build a psymtab for each of them. */
+ dwarf2_build_include_psymtabs (&cu_header, line_offset, pst, objfile);
}
do_cleanups (back_to);
}
@@ -1421,7 +1617,7 @@ scan_partial_symbols (char *info_ptr, st

while (nesting_level)
{
- info_ptr = read_partial_die (&pdi, abfd, info_ptr, cu_header);
+ info_ptr = read_partial_die (&pdi, abfd, info_ptr, cu_header, NULL);

/* Anonymous namespaces have no name but are interesting. */

@@ -1677,6 +1873,32 @@ psymtab_to_symtab_1 (struct partial_symt
struct symtab *symtab;
struct cleanup *back_to;
struct attribute *attr;
+ int i;
+
+ for (i = 0; i < pst->number_of_dependencies; i++)
+ if (!pst->dependencies[i]->readin)
+ {
+ /* Inform about additional files that need to be read in. */
+ if (info_verbose)
+ {
+ fputs_filtered (" ", gdb_stdout);
+ wrap_here ("");
+ fputs_filtered ("and ", gdb_stdout);
+ wrap_here ("");
+ printf_filtered ("%s...", pst->dependencies[i]->filename);
+ wrap_here (""); /* Flush output */
+ gdb_flush (gdb_stdout);
+ }
+ psymtab_to_symtab_1 (pst->dependencies[i]);
+ }
+
+ if (pst->textlow == 0 && pst->texthigh == 0)
+ {
+ /* It's an include file, no symbols to read for it.
+ Everything is in the parent symtab. */
+ pst->readin = 1;
+ return;
+ }

/* Set local variables from the partial symbol table info. */
offset = DWARF_INFO_OFFSET (pst);
@@ -4001,7 +4223,8 @@ dwarf2_lookup_abbrev (unsigned int numbe

static char *
read_partial_die (struct partial_die_info *part_die, bfd *abfd,
- char *info_ptr, const struct comp_unit_head *cu_header)
+ char *info_ptr, const struct comp_unit_head *cu_header,
+ unsigned int *line_offset)
{
unsigned int abbrev_number, bytes_read, i;
struct abbrev_info *abbrev;
@@ -4096,6 +4319,9 @@ read_partial_die (struct partial_die_inf
part_die->sibling =
dwarf_info_buffer + dwarf2_get_ref_die_offset (&attr);
break;
+ case DW_AT_stmt_list:
+ if (line_offset != NULL)
+ *line_offset = DW_UNSND (&attr);
default:
break;
}
@@ -4111,7 +4337,7 @@ read_partial_die (struct partial_die_inf
int dummy;

spec_ptr = dwarf_info_buffer + dwarf2_get_ref_die_offset (&spec_attr);
- read_partial_die (&spec_die, abfd, spec_ptr, cu_header);
+ read_partial_die (&spec_die, abfd, spec_ptr, cu_header, NULL);
if (spec_die.name)
{
part_die->name = spec_die.name;