[RFC] Partial support for dwarf3 DW_AT_ranges

Richard Henderson
2001-12-31T08:34:55Z

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.)

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.

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.

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~

* dwarf2read.c: Remove dead comp_unit_header code.
(dwarf_ranges_offset, dwarf_ranges_size, dwarf_ranges_buffer): New.
(struct dwarf2_pinfo): Add dwarf_ranges_buffer, dwarf_ranges_size.
(DWARF_RANGES_BUFFER, DWARF_RANGES_SIZE): New.
(dwarf2_has_info): Init dwarf_ranges_offset/size.
(dwarf2_locate_sections): Likewise.
(dwarf2_build_psymtabs): Read .debug_ranges.
(dwarf2_build_psymtabs_hard): Swap dwarf_ranges out.
(psymtab_to_symtab_1): Swap dwarf_ranges in. Set cu_header.die.
(RANGES_SECTION): New.
(struct comp_unit_head): Add die member.
(dwarf2_get_pc_bounds): New cu_header argument; adjust all callers.
Look for DW_AT_ranges and return the bounding box.

Index: dwarf2read.c
===================================================================
RCS file: /cvs/src/src/gdb/dwarf2read.c,v
retrieving revision 1.42
diff -c -p -d -u -r1.42 dwarf2read.c
--- dwarf2read.c 2001/12/12 02:11:51 1.42
+++ dwarf2read.c 2001/12/31 04:07:13
@@ -50,23 +50,6 @@
#define DWARF2_REG_TO_REGNUM(REG) (REG)
#endif

-#if 0
-/* .debug_info header for a compilation unit
- Because of alignment constraints, this structure has padding and cannot
- be mapped directly onto the beginning of the .debug_info section. */
-typedef struct comp_unit_header
- {
- unsigned int length; /* length of the .debug_info
- contribution */
- unsigned short version; /* version number -- 2 for DWARF
- version 2 */
- unsigned int abbrev_offset; /* offset into .debug_abbrev section */
- unsigned char addr_size; /* byte size of an address -- 4 */
- }
-_COMP_UNIT_HEADER;
-#define _ACTUAL_COMP_UNIT_HEADER_SIZE 11
-#endif
-
/* .debug_pubnames header
Because of alignment constraints, this structure has padding and cannot
be mapped directly onto the beginning of the .debug_info section. */
@@ -132,6 +115,7 @@ static file_ptr dwarf_aranges_offset;
static file_ptr dwarf_loc_offset;
static file_ptr dwarf_macinfo_offset;
static file_ptr dwarf_str_offset;
+static file_ptr dwarf_ranges_offset;
file_ptr dwarf_frame_offset;
file_ptr dwarf_eh_frame_offset;

@@ -143,6 +127,7 @@ static unsigned int dwarf_aranges_size;
static unsigned int dwarf_loc_size;
static unsigned int dwarf_macinfo_size;
static unsigned int dwarf_str_size;
+static unsigned int dwarf_ranges_size;
unsigned int dwarf_frame_size;
unsigned int dwarf_eh_frame_size;

@@ -156,6 +141,7 @@ unsigned int dwarf_eh_frame_size;
#define LOC_SECTION ".debug_loc"
#define MACINFO_SECTION ".debug_macinfo"
#define STR_SECTION ".debug_str"
+#define RANGES_SECTION ".debug_ranges"
#define FRAME_SECTION ".debug_frame"
#define EH_FRAME_SECTION ".eh_frame"

@@ -173,6 +159,7 @@ struct comp_unit_head
unsigned int offset_size; /* size of file offsets; either 4 or 8 */
unsigned int initial_length_size; /* size of the length field; either
4 or 12 */
+ struct die_info *die;
};

/* The data in the .debug_line statement prologue looks like this. */
@@ -310,6 +297,7 @@ static char *dwarf_info_buffer;
static char *dwarf_abbrev_buffer;
static char *dwarf_line_buffer;
static char *dwarf_str_buffer;
+static char *dwarf_ranges_buffer;

/* A zeroed version of a partial die for initialization purposes. */
static struct partial_die_info zeroed_partial_die;
@@ -373,32 +361,31 @@ static CORE_ADDR baseaddr; /* Add to eac
struct dwarf2_pinfo
{
/* Pointer to start of dwarf info buffer for the objfile. */
-
char *dwarf_info_buffer;

/* Offset in dwarf_info_buffer for this compilation unit. */
-
unsigned long dwarf_info_offset;

/* Pointer to start of dwarf abbreviation buffer for the objfile. */
-
char *dwarf_abbrev_buffer;

/* Size of dwarf abbreviation section for the objfile. */
-
unsigned int dwarf_abbrev_size;

/* Pointer to start of dwarf line buffer for the objfile. */
-
char *dwarf_line_buffer;

/* Pointer to start of dwarf string buffer for the objfile. */
-
char *dwarf_str_buffer;

/* Size of dwarf string section for the objfile. */
-
unsigned int dwarf_str_size;
+
+ /* Pointer to start of dwarf ranges buffer for the objfile. */
+ char *dwarf_ranges_buffer;
+
+ /* Size of dwarf ranges buffer for the objfile. */
+ unsigned int dwarf_ranges_size;
};

#define PST_PRIVATE(p) ((struct dwarf2_pinfo *)(p)->read_symtab_private)
@@ -409,6 +396,8 @@ struct dwarf2_pinfo
#define DWARF_LINE_BUFFER(p) (PST_PRIVATE(p)->dwarf_line_buffer)
#define DWARF_STR_BUFFER(p) (PST_PRIVATE(p)->dwarf_str_buffer)
#define DWARF_STR_SIZE(p) (PST_PRIVATE(p)->dwarf_str_size)
+#define DWARF_RANGES_BUFFER(p) (PST_PRIVATE(p)->dwarf_ranges_buffer)
+#define DWARF_RANGES_SIZE(p) (PST_PRIVATE(p)->dwarf_ranges_size)

/* Maintain an array of referenced fundamental types for the current
compilation unit being read. For DWARF version 1, we have to construct
@@ -692,7 +681,8 @@ static void read_lexical_block_scope (st
const struct comp_unit_head *);

static int dwarf2_get_pc_bounds (struct die_info *,
- CORE_ADDR *, CORE_ADDR *, struct objfile *);
+ CORE_ADDR *, CORE_ADDR *, struct objfile *,
+ const struct comp_unit_head *);

static void dwarf2_add_field (struct field_info *, struct die_info *,
struct objfile *, const struct comp_unit_head *);
@@ -807,9 +797,14 @@ static struct die_info *dwarf_alloc_die
int
dwarf2_has_info (bfd *abfd)
{
- dwarf_info_offset = dwarf_abbrev_offset = dwarf_line_offset = 0;
+ dwarf_info_offset = 0;
+ dwarf_abbrev_offset = 0;
+ dwarf_line_offset = 0;
dwarf_str_offset = 0;
- dwarf_frame_offset = dwarf_eh_frame_offset = 0;
+ dwarf_ranges_offset = 0;
+ dwarf_frame_offset = 0;
+ dwarf_eh_frame_offset = 0;
+
bfd_map_over_sections (abfd, dwarf2_locate_sections, NULL);
if (dwarf_info_offset && dwarf_abbrev_offset)
{
@@ -868,6 +863,11 @@ dwarf2_locate_sections (bfd *ignore_abfd
dwarf_str_offset = sectp->filepos;
dwarf_str_size = bfd_get_section_size_before_reloc (sectp);
}
+ else if (STREQ (sectp->name, RANGES_SECTION))
+ {
+ dwarf_ranges_offset = sectp->filepos;
+ dwarf_ranges_size = bfd_get_section_size_before_reloc (sectp);
+ }
else if (STREQ (sectp->name, FRAME_SECTION))
{
dwarf_frame_offset = sectp->filepos;
@@ -905,6 +905,13 @@ dwarf2_build_psymtabs (struct objfile *o
else
dwarf_str_buffer = NULL;

+ if (dwarf_ranges_offset)
+ dwarf_ranges_buffer = dwarf2_read_section (objfile,
+ dwarf_ranges_offset,
+ dwarf_ranges_size);
+ else
+ dwarf_ranges_buffer = NULL;
+
if (mainline
|| (objfile->global_psymbols.size == 0
&& objfile->static_psymbols.size == 0))
@@ -1112,6 +1119,8 @@ dwarf2_build_psymtabs_hard (struct objfi
DWARF_LINE_BUFFER (pst) = dwarf_line_buffer;
DWARF_STR_BUFFER (pst) = dwarf_str_buffer;
DWARF_STR_SIZE (pst) = dwarf_str_size;
+ DWARF_RANGES_BUFFER (pst) = dwarf_ranges_buffer;
+ DWARF_RANGES_SIZE (pst) = dwarf_ranges_size;
baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));

/* Store the function that reads in the rest of the symbol table */
@@ -1413,6 +1422,8 @@ psymtab_to_symtab_1 (struct partial_symt
dwarf_line_buffer = DWARF_LINE_BUFFER (pst);
dwarf_str_buffer = DWARF_STR_BUFFER (pst);
dwarf_str_size = DWARF_STR_SIZE (pst);
+ dwarf_ranges_buffer = DWARF_RANGES_BUFFER (pst);
+ dwarf_ranges_size = DWARF_RANGES_SIZE (pst);
baseaddr = ANOFFSET (pst->section_offsets, SECT_OFF_TEXT (objfile));
cu_header_offset = offset;
info_ptr = dwarf_info_buffer + offset;
@@ -1435,9 +1446,10 @@ psymtab_to_symtab_1 (struct partial_symt
make_cleanup_free_die_list (dies);

/* Do line number decoding in read_file_scope () */
+ cu_header.die = dies;
process_die (dies, objfile, &cu_header);

- if (!dwarf2_get_pc_bounds (dies, &lowpc, &highpc, objfile))
+ if (!dwarf2_get_pc_bounds (dies, &lowpc, &highpc, objfile, &cu_header))
{
/* Some compilers don't define a DW_AT_high_pc attribute for
the compilation unit. If the DW_AT_high_pc is missing,
@@ -1452,7 +1464,8 @@ psymtab_to_symtab_1 (struct partial_symt
{
CORE_ADDR low, high;

- if (dwarf2_get_pc_bounds (child_die, &low, &high, objfile))
+ if (dwarf2_get_pc_bounds (child_die, &low, &high,
+ objfile, &cu_header))
{
highpc = max (highpc, high);
}
@@ -1561,7 +1574,7 @@ read_file_scope (struct die_info *die, s
struct die_info *child_die;
bfd *abfd = objfile->obfd;

- if (!dwarf2_get_pc_bounds (die, &lowpc, &highpc, objfile))
+ if (!dwarf2_get_pc_bounds (die, &lowpc, &highpc, objfile, cu_header))
{
if (die->has_children)
{
@@ -1572,7 +1585,8 @@ read_file_scope (struct die_info *die, s
{
CORE_ADDR low, high;

- if (dwarf2_get_pc_bounds (child_die, &low, &high, objfile))
+ if (dwarf2_get_pc_bounds (child_die, &low, &high,
+ objfile, cu_header))
{
lowpc = min (lowpc, low);
highpc = max (highpc, high);
@@ -1675,7 +1689,8 @@ read_func_scope (struct die_info *die, s

/* Ignore functions with missing or empty names and functions with
missing or invalid low and high pc attributes. */
- if (name == NULL || !dwarf2_get_pc_bounds (die, &lowpc, &highpc, objfile))
+ if (name == NULL
+ || !dwarf2_get_pc_bounds (die, &lowpc, &highpc, objfile, cu_header))
return;

lowpc += baseaddr;
@@ -1742,7 +1757,11 @@ read_lexical_block_scope (struct die_inf
struct die_info *child_die;

/* Ignore blocks with missing or invalid low and high pc attributes. */
- if (!dwarf2_get_pc_bounds (die, &lowpc, &highpc, objfile))
+ /* ??? Perhaps consider discontiguous blocks defined by DW_AT_ranges
+ as multiple lexical blocks? Handling children in a sane way would
+ be nasty. Might be easier to properly extend generic blocks to
+ describe ranges. */
+ if (!dwarf2_get_pc_bounds (die, &lowpc, &highpc, objfile, cu_header))
return;
lowpc += baseaddr;
highpc += baseaddr;
@@ -1767,28 +1786,117 @@ read_lexical_block_scope (struct die_inf
local_symbols = new->locals;
}

-/* 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. */