RFA: Location list support for DWARF-2

Daniel Jacobowitz
2003-03-10T16:06:32Z

Here's initial support for location lists. I'd like to take a moment
to thank Jim Blandy and Daniel Berlin for hashing out the interface to
LOC_COMPUTED so thoroughly; it turned out that I didn't need to do
anything at all to the interface or to its clients in order to make
this work.

To test this, you'll need a GCC CVS checkout from the "rtlopt-branch"
branch tag. This patch has no effect if location lists aren't used,
and is a strict improvement if they are, since otherwise we won't find
things at all. However, debug output from that branch is still a
little immature. One problem I've noticed so far is that we emit
incomplete location lists for global variables; see my post on the gcc@
list for more details. So sometimes we can't find globals, which is a
regression in the debug info.

This patch also does not support multiple overlapping location lists;
it simply uses the first match. The rest of GDB isn't ready for
multiple locations yet; that's down the list after DW_OP_piece I think.

I had to add the CU base address to each symbol's baton. Long term we
can get rid of it by making sure we can go from symbol to symtab and
storing it in the symtab; I think that's a good idea but it's a patch
for another day.

Jim, is this OK? I'd appreciate another set of eyes on it.

--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer

2003-03-10 Daniel Jacobowitz <drow@mvista.com>

* dwarf2expr.c (dwarf2_read_address): Renamed from read_address;
made non-static.
(execute_stack_op): All callers updated.
* dwarf2expr.h: Add prototype for dwarf2_read_address.
* dwarf2loc.c (find_location_expression): New function.
(dwarf_expr_frame_base, locexpr_read_variable): Call it.
(locexpr_tracepoint_var_ref): Likewise.
(locexpr_read_needs_frame, locexpr_describe_location): Handle
location lists.
* dwarf2loc.c (struct dwarf2_locexpr_baton): Add is_list and
base_address.
* dwarf2read.c (struct comp_unit_head): Remove DIE member, add
base_address and base_known.
(dwarf_loc_buffer): New variable.
(struct dwarf2_pinfo): Add dwarf_loc_buffer and dwarf_loc_size.
(DWARF_LOC_BUFFER, DWARF_LOC_SIZE): New macros.
(dwarf2_has_info): Initialize dwarf_loc_offset.
(dwarf2_build_psymtabs): Read in .debug_loc.
(dwarf2_build_psymtabs_hard): Use DWARF_LOC_BUFFER and
DWARF_LOC_SIZE.
(psymtab_to_symtab_1): Likewise. Move base address calculation
here, from...
(dwarf2_get_pc_bounds): ... here. Use the base address from
cu_header.
(dump_die): Handle DW_FORM_ref8.
(dwarf2_symbol_mark_computed): Handle location lists.

Index: dwarf2expr.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/dwarf2expr.c,v
retrieving revision 1.3
diff -u -p -r1.3 dwarf2expr.c
--- dwarf2expr.c 5 Mar 2003 18:00:02 -0000 1.3
+++ dwarf2expr.c 10 Mar 2003 00:30:18 -0000
@@ -170,13 +170,13 @@ read_sleb128 (unsigned char *buf, unsign
BUF_END. The address is returned, and *BYTES_READ is set to the
number of bytes read from BUF. */

-static CORE_ADDR
-read_address (unsigned char *buf, unsigned char *buf_end, int *bytes_read)
+CORE_ADDR
+dwarf2_read_address (unsigned char *buf, unsigned char *buf_end, int *bytes_read)
{
CORE_ADDR result;

if (buf_end - buf < TARGET_ADDR_BIT / TARGET_CHAR_BIT)
- error ("read_address: Corrupted DWARF expression.");
+ error ("dwarf2_read_address: Corrupted DWARF expression.");

*bytes_read = TARGET_ADDR_BIT / TARGET_CHAR_BIT;
result = extract_address (buf, TARGET_ADDR_BIT / TARGET_CHAR_BIT);
@@ -277,7 +277,7 @@ execute_stack_op (struct dwarf_expr_cont
break;

case DW_OP_addr:
- result = read_address (op_ptr, op_end, &bytes_read);
+ result = dwarf2_read_address (op_ptr, op_end, &bytes_read);
op_ptr += bytes_read;
break;

@@ -464,9 +464,10 @@ execute_stack_op (struct dwarf_expr_cont

(ctx->read_mem) (ctx->baton, buf, result,
TARGET_ADDR_BIT / TARGET_CHAR_BIT);
- result = read_address (buf,
- buf + TARGET_ADDR_BIT / TARGET_CHAR_BIT,
- &bytes_read);
+ result = dwarf2_read_address (buf,
+ buf + (TARGET_ADDR_BIT
+ / TARGET_CHAR_BIT),
+ &bytes_read);
}
result = result + offset;
ctx->stack_len = before_stack_len;
@@ -525,9 +526,10 @@ execute_stack_op (struct dwarf_expr_cont

(ctx->read_mem) (ctx->baton, buf, result,
TARGET_ADDR_BIT / TARGET_CHAR_BIT);
- result = read_address (buf,
- buf + TARGET_ADDR_BIT / TARGET_CHAR_BIT,
- &bytes_read);
+ result = dwarf2_read_address (buf,
+ buf + (TARGET_ADDR_BIT
+ / TARGET_CHAR_BIT),
+ &bytes_read);
}
break;

@@ -537,9 +539,10 @@ execute_stack_op (struct dwarf_expr_cont
int bytes_read;

(ctx->read_mem) (ctx->baton, buf, result, *op_ptr++);
- result = read_address (buf,
- buf + TARGET_ADDR_BIT / TARGET_CHAR_BIT,
- &bytes_read);
+ result = dwarf2_read_address (buf,
+ buf + (TARGET_ADDR_BIT
+ / TARGET_CHAR_BIT),
+ &bytes_read);
}
break;

Index: dwarf2expr.h
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/dwarf2expr.h,v
retrieving revision 1.2
diff -u -p -r1.2 dwarf2expr.h
--- dwarf2expr.h 28 Feb 2003 20:03:18 -0000 1.2
+++ dwarf2expr.h 10 Mar 2003 00:30:37 -0000
@@ -99,5 +99,7 @@ unsigned char *read_uleb128 (unsigned ch
ULONGEST * r);
unsigned char *read_sleb128 (unsigned char *buf, unsigned char *buf_end,
LONGEST * r);
+CORE_ADDR dwarf2_read_address (unsigned char *buf, unsigned char *buf_end,
+ int *bytes_read);

#endif
Index: dwarf2loc.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/dwarf2loc.c,v
retrieving revision 1.3
diff -u -p -r1.3 dwarf2loc.c
--- dwarf2loc.c 5 Mar 2003 18:00:02 -0000 1.3
+++ dwarf2loc.c 10 Mar 2003 15:22:17 -0000
@@ -40,6 +40,68 @@
#define DWARF2_REG_TO_REGNUM(REG) (REG)
#endif

+
+/* A helper function for dealing with location lists. Given a
+ symbol baton (BATON) and a pc value (PC), find the appropriate
+ location expression, set *LOCEXPR_LENGTH, and return a pointer
+ to the beginning of the expression. Returns NULL on failure.
+
+ For now, only return the first matching location expression; there
+ can be more than one in the list. */
+
+static char *
+find_location_expression (struct dwarf2_locexpr_baton *baton,
+ int *locexpr_length, CORE_ADDR pc)
+{
+ CORE_ADDR base_address = baton->base_address;
+ CORE_ADDR low, high;
+ char *loc_ptr;
+ unsigned int addr_size = TARGET_ADDR_BIT / TARGET_CHAR_BIT, length;
+ CORE_ADDR base_mask = ~(~(CORE_ADDR)1 << (addr_size * 8 - 1));
+
+ if (! baton->is_list)
+ {
+ *locexpr_length = baton->size;
+ return baton->data;
+ }
+
+ loc_ptr = baton->data;
+
+ while (1)
+ {
+ low = dwarf2_read_address (loc_ptr, loc_ptr + addr_size, &length);
+ loc_ptr += length;
+ high = dwarf2_read_address (loc_ptr, loc_ptr + addr_size, &length);
+ loc_ptr += length;
+
+ /* An end-of-list entry. */
+ if (low == 0 && high == 0)
+ return NULL;
+
+ /* A base-address-selection entry. */
+ if ((low & base_mask) == base_mask)
+ {
+ base_address = high;
+ continue;
+ }
+
+ /* Otherwise, a location expression entry. */
+ low += base_address;
+ high += base_address;
+
+ length = extract_unsigned_integer (loc_ptr, 2);
+ loc_ptr += 2;
+
+ if (pc >= low && pc < high)
+ {
+ *locexpr_length = length;
+ return loc_ptr;
+ }
+
+ loc_ptr += length;
+ }
+}
+
/* This is the baton used when performing dwarf2 expression
evaluation. */
struct dwarf_expr_baton
@@ -90,10 +152,14 @@ dwarf_expr_frame_base (void *baton, unsi
struct symbol *framefunc;
struct dwarf2_locexpr_baton *symbaton;
struct dwarf_expr_baton *debaton = (struct dwarf_expr_baton *) baton;
+
framefunc = get_frame_function (debaton->frame);
symbaton = SYMBOL_LOCATION_BATON (framefunc);
- *start = symbaton->data;
- *length = symbaton->size;
+ *start = find_location_expression (symbaton, length,
+ get_frame_pc (debaton->frame));
+ if (*start == NULL)
+ error ("Could not find the frame base for \"%s\".",
+ SYMBOL_NATURAL_NAME (framefunc));
}

/* Using the objfile specified in BATON, find the address for the
@@ -248,8 +314,15 @@ locexpr_read_variable (struct symbol *sy
{
struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol);
struct value *val;
- val = dwarf2_evaluate_loc_desc (symbol, frame, dlbaton->data, dlbaton->size,
- dlbaton->objfile);
+ unsigned char *data;
+ int size;
+
+ data = find_location_expression (dlbaton, &size,
+ frame ? get_frame_pc (frame) : 0);
+ if (data == NULL)
+ error ("Variable \"%s\" is not available.", SYMBOL_NATURAL_NAME (symbol));
+
+ val = dwarf2_evaluate_loc_desc (symbol, frame, data, size, dlbaton->objfile);

return val;
}
@@ -259,6 +332,12 @@ static int
locexpr_read_needs_frame (struct symbol *symbol)
{
struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol);
+
+ /* If there's a location list, then assume we need to have a frame
+ to choose the appropriate location expression. */
+ if (dlbaton->is_list)
+ return 1;
+
return dwarf2_loc_desc_needs_frame (dlbaton->data, dlbaton->size);
}

@@ -269,6 +348,12 @@ locexpr_describe_location (struct symbol
/* FIXME: be more extensive. */
struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol);

+ if (dlbaton->is_list)
+ {
+ fprintf_filtered (stream, "a variable with multiple locations");
+ return 1;
+ }
+
if (dlbaton->size == 1
&& dlbaton->data[0] >= DW_OP_reg0
&& dlbaton->data[0] <= DW_OP_reg31)
@@ -298,27 +383,32 @@ locexpr_tracepoint_var_ref (struct symbo
struct axs_value * value)
{
struct dwarf2_locexpr_baton *dlbaton = SYMBOL_LOCATION_BATON (symbol);
+ unsigned char *data;
+ int size;
+
+ data = find_location_expression (dlbaton, &size, ax->scope);
+ if (data == NULL)
+ error ("Variable \"%s\" is not available.", SYMBOL_NATURAL_NAME (symbol));

- if (dlbaton->size == 0)
+ if (size == 0)
error ("Symbol \"%s\" has been optimized out.",
SYMBOL_PRINT_NAME (symbol));

- if (dlbaton->size == 1
- && dlbaton->data[0] >= DW_OP_reg0
- && dlbaton->data[0] <= DW_OP_reg31)
+ if (size == 1
+ && data[0] >= DW_OP_reg0
+ && data[0] <= DW_OP_reg31)
{
value->kind = axs_lvalue_register;
- value->u.reg = dlbaton->data[0] - DW_OP_reg0;
+ value->u.reg = data[0] - DW_OP_reg0;
}
- else if (dlbaton->data[0] == DW_OP_regx)
+ else if (data[0] == DW_OP_regx)
{
ULONGEST reg;
- read_uleb128 (dlbaton->data + 1, dlbaton->data + dlbaton->size,
- &reg);
+ read_uleb128 (data + 1, data + size, &reg);
value->kind = axs_lvalue_register;
value->u.reg = reg;
}
- else if (dlbaton->data[0] == DW_OP_fbreg)
+ else if (data[0] == DW_OP_fbreg)
{
/* And this is worse than just minimal; we should honor the frame base
as above. */
@@ -326,9 +416,8 @@ locexpr_tracepoint_var_ref (struct symbo
LONGEST frame_offset;
unsigned char *buf_end;

- buf_end = read_sleb128 (dlbaton->data + 1, dlbaton->data + dlbaton->size,
- &frame_offset);
- if (buf_end != dlbaton->data + dlbaton->size)
+ buf_end = read_sleb128 (data + 1, data + size, &frame_offset);
+ if (buf_end != data + size)
error ("Unexpected opcode after DW_OP_fbreg for symbol \"%s\".",
SYMBOL_PRINT_NAME (symbol));

Index: dwarf2loc.h
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/dwarf2loc.h,v
retrieving revision 1.1
diff -u -p -r1.1 dwarf2loc.h
--- dwarf2loc.h 21 Feb 2003 15:24:17 -0000 1.1
+++ dwarf2loc.h 8 Mar 2003 18:26:15 -0000
@@ -31,6 +31,8 @@ struct dwarf2_locexpr_baton
{
unsigned char *data;
unsigned short size;
+ unsigned char is_list;
+ CORE_ADDR base_address;
struct objfile *objfile;
};

Index: dwarf2read.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/dwarf2read.c,v
retrieving revision 1.88
diff -u -p -r1.88 dwarf2read.c
--- dwarf2read.c 25 Feb 2003 21:36:17 -0000 1.88
+++ dwarf2read.c 10 Mar 2003 05:01:11 -0000
@@ -220,9 +220,13 @@ struct comp_unit_head

struct abbrev_info *dwarf2_abbrevs[ABBREV_HASH_SIZE];

- /* Pointer to the DIE associated with the compilation unit. */
+ /* Base address of this compilation unit. */

- struct die_info *die;
+ CORE_ADDR base_address;
+
+ /* Flag representing whether base_address is accurate. */
+
+ int base_known;
};

/* The line number information for a compilation unit (found in the
@@ -395,6 +399,7 @@ static char *dwarf_line_buffer;
static char *dwarf_str_buffer;
static char *dwarf_macinfo_buffer;
static char *dwarf_ranges_buffer;
+static char *dwarf_loc_buffer;

/* A zeroed version of a partial die for initialization purposes. */
static struct partial_die_info zeroed_partial_die;
@@ -511,6 +516,13 @@ struct dwarf2_pinfo

unsigned int dwarf_ranges_size;

+ /* Pointer to start of dwarf locations buffer for the objfile. */
+
+ char *dwarf_loc_buffer;
+
+ /* Size of dwarf locations buffer for the objfile. */
+
+ unsigned int dwarf_loc_size;
};

#define PST_PRIVATE(p) ((struct dwarf2_pinfo *)(p)->read_symtab_private)
@@ -526,6 +538,8 @@ struct dwarf2_pinfo
#define DWARF_MACINFO_SIZE(p) (PST_PRIVATE(p)->dwarf_macinfo_size)
#define DWARF_RANGES_BUFFER(p) (PST_PRIVATE(p)->dwarf_ranges_buffer)
#define DWARF_RANGES_SIZE(p) (PST_PRIVATE(p)->dwarf_ranges_size)
+#define DWARF_LOC_BUFFER(p) (PST_PRIVATE(p)->dwarf_loc_buffer)
+#define DWARF_LOC_SIZE(p) (PST_PRIVATE(p)->dwarf_loc_size)

/* Maintain an array of referenced fundamental types for the current
compilation unit being read. For DWARF version 1, we have to construct
@@ -926,6 +940,7 @@ dwarf2_has_info (bfd *abfd)
dwarf_frame_offset = 0;
dwarf_eh_frame_offset = 0;
dwarf_ranges_offset = 0;
+ dwarf_loc_offset = 0;

bfd_map_over_sections (abfd, dwarf2_locate_sections, NULL);
if (dwarf_info_offset && dwarf_abbrev_offset)
@@ -1062,6 +1077,14 @@ dwarf2_build_psymtabs (struct objfile *o
else
dwarf_ranges_buffer = NULL;

+ if (dwarf_loc_offset)
+ dwarf_loc_buffer = dwarf2_read_section (objfile,
+ dwarf_loc_offset,
+ dwarf_loc_size,
+ dwarf_loc_section);
+ else
+ dwarf_loc_buffer = NULL;
+
if (mainline
|| (objfile->global_psymbols.size == 0
&& objfile->static_psymbols.size == 0))
@@ -1283,6 +1306,8 @@ dwarf2_build_psymtabs_hard (struct objfi
DWARF_MACINFO_SIZE (pst) = dwarf_macinfo_size;
DWARF_RANGES_BUFFER (pst) = dwarf_ranges_buffer;
DWARF_RANGES_SIZE (pst) = dwarf_ranges_size;
+ DWARF_LOC_BUFFER (pst) = dwarf_loc_buffer;
+ DWARF_LOC_SIZE (pst) = dwarf_loc_size;
baseaddr = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));

/* Store the function that reads in the rest of the symbol table */
@@ -1607,6 +1632,7 @@ psymtab_to_symtab_1 (struct partial_symt
char *info_ptr;
struct symtab *symtab;
struct cleanup *back_to;
+ struct attribute *attr;

/* Set local variables from the partial symbol table info. */
offset = DWARF_INFO_OFFSET (pst);
@@ -1621,6 +1647,8 @@ psymtab_to_symtab_1 (struct partial_symt
dwarf_macinfo_size = DWARF_MACINFO_SIZE (pst);
dwarf_ranges_buffer = DWARF_RANGES_BUFFER (pst);
dwarf_ranges_size = DWARF_RANGES_SIZE (pst);
+ dwarf_loc_buffer = DWARF_LOC_BUFFER (pst);
+ dwarf_loc_size = DWARF_LOC_SIZE (pst);
baseaddr = ANOFFSET (pst->section_offsets, SECT_OFF_TEXT (objfile));
cu_header_offset = offset;
info_ptr = dwarf_info_buffer + offset;
@@ -1642,8 +1670,32 @@ psymtab_to_symtab_1 (struct partial_symt

make_cleanup_free_die_list (dies);

+ /* Find the base address of the compilation unit for range lists and
+ location lists. It will normally be specified by DW_AT_low_pc.
+ In DWARF-3 draft 4, the base address could be overridden by
+ DW_AT_entry_pc. It's been removed, but GCC still uses this for
+ compilation units with discontinuous ranges. */
+
+ cu_header.base_known = 0;
+ cu_header.base_address = 0;
+
+ attr = dwarf_attr (dies, DW_AT_entry_pc);
+ if (attr)
+ {
+ cu_header.base_address = DW_ADDR (attr);
+ cu_header.base_known = 0;
+ }
+ else
+ {
+ attr = dwarf_attr (dies, DW_AT_low_pc);
+ if (attr)
+ {
+ cu_header.base_address = DW_ADDR (attr);
+ cu_header.base_known = 1;
+ }
+ }
+
/* 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, &cu_header))
@@ -2122,40 +2174,18 @@ dwarf2_get_pc_bounds (struct die_info *d
.debug_renges section. */
unsigned int offset = DW_UNSND (attr);
/* Base address selection entry. */
- CORE_ADDR base = 0;
- int found_base = 0;
+ CORE_ADDR base;
+ int found_base;
int dummy;
unsigned int i;
char *buffer;
CORE_ADDR marker;
int low_set;

- /* 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. */
-
- /* ??? 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);
- found_base = 1;
- }
-
- if (!found_base)
- {
- attr = dwarf_attr (cu_header->die, DW_AT_low_pc);
- if (attr)
- {
- base = DW_ADDR (attr);
- found_base = 1;
- }
- }
-
+ found_base = cu_header->base_known;
+ base = cu_header->base_address;
buffer = dwarf_ranges_buffer + offset;

-
/* Read in the largest possible address. */
marker = read_address (obfd, buffer, cu_header, &dummy);
if ((marker & mask) == mask)
@@ -6481,6 +6511,7 @@ dump_die (struct die_info *die)
case DW_FORM_ref1:
case DW_FORM_ref2:
case DW_FORM_ref4:
+ case DW_FORM_ref8:
case DW_FORM_udata:
case DW_FORM_sdata:
fprintf_unfiltered (gdb_stderr, "constant: %ld", DW_UNSND (&die->attrs[i]));
@@ -7330,23 +7361,39 @@ dwarf2_symbol_mark_computed (struct attr
{
struct dwarf2_locexpr_baton *baton;

- /* When support for location lists is added, this will go away. */
- if (!attr_form_is_block (attr))
- {
- dwarf2_complex_location_expr_complaint ();
- return;
- }
-
baton = obstack_alloc (&objfile->symbol_obstack,
sizeof (struct dwarf2_locexpr_baton));
baton->objfile = objfile;

- /* Note that we're just copying the block's data pointer here, not
- the actual data. We're still pointing into the dwarf_info_buffer
- for SYM's objfile; right now we never release that buffer, but
- when we do clean up properly this may need to change. */
- baton->size = DW_BLOCK (attr)->size;
- baton->data = DW_BLOCK (attr)->data;
+ /* When support for location lists is added, this will go away. */
+ if (attr_form_is_block (attr))
+ {
+ /* Note that we're just copying the block's data pointer here, not
+ the actual data. We're still pointing into the dwarf_info_buffer
+ for SYM's objfile; right now we never release that buffer, but
+ when we do clean up properly this may need to change. */
+ baton->size = DW_BLOCK (attr)->size;
+ baton->data = DW_BLOCK (attr)->data;
+ baton->is_list = 0;
+ }
+ else if (attr->form == DW_FORM_data4 || attr->form == DW_FORM_data8)
+ {
+ baton->size = 0;
+ baton->data = dwarf_loc_buffer + DW_UNSND (attr);
+ baton->is_list = 1;
+ baton->base_address = cu_header->base_address;
+ if (cu_header->base_known == 0)
+ complain (&symfile_complaints,
+ "Location list used without specifying the CU base address.");
+ }
+ else
+ {
+ dwarf2_invalid_attrib_class_complaint ("location description",
+ SYMBOL_NATURAL_NAME (sym));
+ baton->size = 0;
+ baton->data = NULL;
+ baton->is_list = 0;
+ }

SYMBOL_LOCATION_FUNCS (sym) = &dwarf2_locexpr_funcs;
SYMBOL_LOCATION_BATON (sym) = baton;