[RFA/DWARF2] Handle nested subprograms in CU pc bound calculation

Joel Brobecker
2008-09-30T15:28:39Z

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

Hello,

We just recently encountered a situation where GDB was emitting the following
warning:

| (gdb) b f1
| warning: (Internal error: pc 0x80495ce in read in psymtab, but not in symtab.)
| warning: (Internal error: pc 0x80495d1 in read in psymtab, but not in symtab.)
| warning: (Internal error: pc 0x80495d1 in read in psymtab, but not in symtab.)
| warning: (Internal error: pc 0x80495d1 in read in psymtab, but not in symtab.)
| Breakpoint 1 at 0x80495d1: file gen1.adb, line 3.

This happened after compiling the CU with -ffunction-sections, and
is related to the fact that f1 is a nested subprogram. The code looks
like this:

procedure Foo is
procedure F1 is new Gen;
begin
...

Here is what happens:

1. When compiling with -ffunction-sections, the compiler is not
providing the CU PC bounds (DW_AT_low_pc and DW_AT_high_pc).
I would imagine that this is because functions might be removed
later during the link, and thus bounds might be affected.

2. As a result, GDB ends up having to compute the PC bounds itself
by inspecting the children of the CU. This is done inside
get_scope_pc_bounds. However, this function only handles
non-nested functions. As a result, after having determined
the bounds of the main procedure Foo, it goes directly to
the next sibling of that function, past the two nested
subprograms. The net effect is that the low/high PC range
is too short, missing the range for our nested subprogram.

3. Later, when the user inserts a breakpoint on the nested
subprogram, GDB finds symbol f1, converts the associated
psymtab into a symtab, and then tries to find the first
"real" line of code, and so ends up searching the symtab
associated to a PC inside our subprogram. The search
among known symtabs fails because of the incorrect PC bounds.
So the next step is searching through the psymtabs, where
we find the correct one, and realize that it has already
been read in, meaning that the previous search should have
found it -> warning.

The attached patch fixes this problem by handling nested subprograms.
Similarly to what was done earlier, I am limiting this to languages
that allow nested subprogram, which means only Ada for now. Other
languages should be unaffected.

2008-09-30 Joel Brobecker <brobecker@adacore.com>

* dwarf2read.c (dwarf2_get_subprogram_pc_bounds): New function.
(get_scope_pc_bounds): Use it.

Tested on x86-linux. No regression. Any objection?

Thanks,
--
Joel

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

Index: dwarf2read.c
===================================================================
RCS file: /cvs/src/src/gdb/dwarf2read.c,v
retrieving revision 1.284
diff -u -p -r1.284 dwarf2read.c
--- dwarf2read.c 23 Sep 2008 17:36:51 -0000 1.284
+++ dwarf2read.c 30 Sep 2008 14:54:29 -0000
@@ -3324,6 +3324,43 @@ dwarf2_get_pc_bounds (struct die_info *d
return ret;
}

+/* Assuming that DIE represents a subprogram DIE or a lexical block, get
+ its low and high PC addresses. Do nothing if these addresses could not
+ be determined. Otherwise, set LOWPC to the low address if it is smaller,
+ and HIGHPC to the high address if greater than HIGHPC. */
+
+static void
+dwarf2_get_subprogram_pc_bounds (struct die_info *die,
+ CORE_ADDR *lowpc, CORE_ADDR *highpc,
+ struct dwarf2_cu *cu)
+{
+ CORE_ADDR low, high;
+ struct die_info *child = die->child;
+
+ if (dwarf2_get_pc_bounds (die, &low, &high, cu))
+ {
+ *lowpc = min (*lowpc, low);
+ *highpc = max (*highpc, high);
+ }
+
+ /* If the language does not allow nested subprograms (either inside
+ subprograms or lexical blocks), we're done. */
+ if (cu->language != language_ada)
+ return;
+
+ /* Check all the children of the given DIE. If it contains nested
+ subprograms, then check their pc bounds. Likewise, we need to
+ check lexical blocks as well, as they may also contain subprogram
+ definitions. */
+ while (child && child->tag)
+ {
+ if (child->tag == DW_TAG_subprogram
+ || child->tag == DW_TAG_lexical_block)
+ dwarf2_get_subprogram_pc_bounds (child, lowpc, highpc, cu);
+ child = sibling_die (child);
+ }
+}
+
/* Get the low and high pc's represented by the scope DIE, and store
them in *LOWPC and *HIGHPC. If the correct values can't be
determined, set *LOWPC to -1 and *HIGHPC to 0. */
@@ -3350,11 +3387,7 @@ get_scope_pc_bounds (struct die_info *di
{
switch (child->tag) {
case DW_TAG_subprogram:
- if (dwarf2_get_pc_bounds (child, &current_low, &current_high, cu))
- {
- best_low = min (best_low, current_low);
- best_high = max (best_high, current_high);
- }
+ dwarf2_get_subprogram_pc_bounds (child, &best_low, &best_high, cu);
break;
case DW_TAG_namespace:
/* FIXME: carlton/2004-01-16: Should we do this for