Re: [RFC] Partial support for dwarf3 DW_AT_ranges

Elena Zannoni
2002-01-04T17:47:30Z

Richard Henderson writes:
> GCC began emitting DW_AT_ranges back in September to deal with
> lexical scopes made discontiguous by basic block reordering.
>
> As of today, it may also create discontiguous lexical scopes
> due to scheduling. (Before today under the same circumstances
> we'd lose track of which instructions belonged to which scope
> and fail to emit any debug information whatsoever.)
>

Richard, there was some initial effort to deal with this problem
in gdb's symbol tables structures back in October.
See the thread at:
http://sources.redhat.com/ml/gdb-patches/2001-10/msg00304.html
However, no real changes have been made to the symbol tables yet.

> However, GDB doesn't recognize DW_AT_ranges as a valid way of
> marking a lexical scope, which causes it to discard the scope
> entirely. Which is probably the least useful thing that could
> be done.
>
> The following does not add proper support for discontiguous
> address ranges. I couldn't figure out how to do that in any
> way that wasn't horribly invasive. I'm willing to expend a
> significant amount of effort on this if someone is willing to
> provide some direction.
>

The thread mentioned above has some initial implementation of an
address set for partial symbol tables which would be used in case of
non contiguous addr ranges. You should coordinate with Jim.

> What this does do is find the "bounding box" of the discontiguous
> range and use that. Yes, that will do the wrong thing in some
> circumstances, but the current behaviour is wrong under all
> circumstances, so it may be a net improvement.
>

I am OK with committing some initial support. At least now gdb can
read the info.

I have a few comments on your changes. Maybe I am missing it, but is
the -1 value returned by dwarf2_get_pc_bounds used anywhere yet? If
not, I would suggest we don't bother with it right now, given that the
final solution will probably have to restructure that function again.
Also, I would not want to introduce more goto's in gdb. I think that
that sequence can be rewritten w/o goto's pretty easily. Also there
is some formatting problem (white spaces around operators, full names
for variables). Adding some comments on how the .debug_ranges list is
organized would be helpful too.

I think JimB is the one which has the last say on this however, since
he has a patch in the works.

thanks
Elena

> My test case for this was
>
> static int foo(int *);
> static void bar(int *);
>
> int main()
> {
> {
> int x = 0, r;
> r = foo(&x);
> if (__builtin_expect (r, 1))
> return 0;
> bar (&x);
> return 1;
> }
> }
>
> static int foo(int *p)
> {
> *p = 1;
> return 0;
> }
>
> static void bar(int *p)
> {
> *p = 2;
> }
>
> For any GCC target that can emit epilogues as rtl, we will
> arrange the code here as
>
> x = 0
> call foo
> if r == 0 goto L1
> ret = 0
> L0:
> return ret
> L1:
> call bar
> ret = 1
> goto L0
>
> The lexical scope created by the extra set of braces doesn't
> cover the epilogue, so the scope's range is the block before
> L0, plus the block after L1.
>
>
> r~

> -/* Get low and high pc attributes from a die.
> - Return 1 if the attributes are present and valid, otherwise, return 0. */
> +/* Get low and high pc attributes from a die. Return 1 if the attributes
> + are present and valid, otherwise, return 0. Return -1 if the range is
> + discontinuous, i.e. derived from DW_AT_ranges information. */
>
> static int
> -dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc, CORE_ADDR *highpc,
> - struct objfile *objfile)
> +dwarf2_get_pc_bounds (struct die_info *die, CORE_ADDR *lowpc,
> + CORE_ADDR *highpc, struct objfile *objfile,
> + const struct comp_unit_head *cu_header)
> {
> + bfd *obfd = objfile->obfd;
> struct attribute *attr;
> CORE_ADDR low;
> CORE_ADDR high;
> + int ret;
>
> - attr = dwarf_attr (die, DW_AT_low_pc);
> - if (attr)
> - low = DW_ADDR (attr);
> - else
> - return 0;
> attr = dwarf_attr (die, DW_AT_high_pc);
> if (attr)
> - high = DW_ADDR (attr);
> - else
> - return 0;
> + {
> + high = DW_ADDR (attr);
> + attr = dwarf_attr (die, DW_AT_low_pc);
> + if (attr)
> + low = DW_ADDR (attr);
> + else
> + return 0;
> + ret = 1;
> + }
> + else if ((attr = dwarf_attr (die, DW_AT_ranges)) != NULL)
> + {
> + unsigned int addr_size = cu_header->addr_size;
> + CORE_ADDR mask = ~(~(CORE_ADDR)1 << (addr_size * 8 - 1));
> + unsigned int offset = DW_UNSND (attr);
> + CORE_ADDR base;
> + int dummy;
> + unsigned int i;
> + char *buffer;
>
> + /* The applicable base address is determined by (1) the closest
> + preceding base address selection entry in the range list or
> + (2) the DW_AT_low_pc of the compilation unit. */
> + /* ??? We definitely need some sort of indexed data structure here.
> + At minimum we should recognize the common case of there being
> + no base address selection entries. */
> +
> + buffer = dwarf_ranges_buffer + offset;
> + for (i = offset; i > 2 * addr_size; )
> + {
> + CORE_ADDR marker;
> +
> + i -= 2 * addr_size;
> + buffer -= 2 * addr_size;
> +
> + marker = read_address (obfd, buffer, cu_header, &dummy);
> + if ((marker & mask) == mask)
> + {
> + base = read_address (obfd, buffer+addr_size, cu_header, &dummy);
> + goto found_base;
> + }
> + }
> +
> + /* ??? Was in dwarf3 draft4, and has since been removed.
> + GCC still uses it though. */
> + attr = dwarf_attr (cu_header->die, DW_AT_entry_pc);
> + if (attr)
> + {
> + base = DW_ADDR (attr);
> + goto found_base;
> + }
> +
> + attr = dwarf_attr (cu_header->die, DW_AT_low_pc);
> + if (attr)
> + {
> + base = DW_ADDR (attr);
> + goto found_base;
> + }
> +
> + /* We have no valid base address for the ranges data. */
> + return 0;
> +
> + found_base:
> + buffer = dwarf_ranges_buffer + offset;
> + low = read_address (obfd, buffer, cu_header, &dummy);
> + buffer += addr_size;
> + high = read_address (obfd, buffer, cu_header, &dummy);
> + buffer += addr_size;
> + if (low == 0 && high == 0)
> + /* If the first entry is an end-of-list marker, the range
> + describes an empty scope, i.e. no instructions. */
> + return 0;
> +
> + while (1)
> + {
> + CORE_ADDR b, e;
> + offset += 2 * addr_size;
> +
> + b = read_address (obfd, buffer, cu_header, &dummy);
> + buffer += addr_size;
> + e = read_address (obfd, buffer, cu_header, &dummy);
> + buffer += addr_size;
> + if (b == 0 && e == 0)
> + break;
> + if (b < low)
> + low = b;
> + if (e > high)
> + high = e;
> + }
> +
> + low += base;
> + high += base;
> + ret = -1;
> + }
> +
> if (high < low)
> return 0;
>
> @@ -1800,12 +1908,12 @@ dwarf2_get_pc_bounds (struct die_info *d
> labels are not in the output, so the relocs get a value of 0.
> If this is a discarded function, mark the pc bounds as invalid,
> so that GDB will ignore it. */
> - if (low == 0 && (bfd_get_file_flags (objfile->obfd) & HAS_RELOC) == 0)
> + if (low == 0 && (bfd_get_file_flags (obfd) & HAS_RELOC) == 0)
> return 0;
>
> *lowpc = low;
> *highpc = high;
> - return 1;
> + return ret;
> }
>
> /* Add an aggregate field to the field list. */