libdw: Add new function dwarf_getlocations.

Using dwarf_getlocation it is possible to get single location
descriptions and with dwarf_getlocation_addr it is possible to
get a location list covering a specific address.  But sometimes
it is more convenient to get all ranges covered by a location
list.  For example when a specific address isn't covered and
you want to find alternative addresses where a location
description is defined.

dwarf_getlocations is modelled after dwarf_ranges. It enumerates
the location ranges and descriptions covered by the given
attribute.  In the first call OFFSET should be zero and *BASEP
need not be initialized.  Returns -1 for errors, zero when
there are no more locations to report, or a nonzero OFFSET
value to pass to the next call.  Each subsequent call must
preserve *BASEP from the prior call.  Successful calls fill in
*STARTP and *ENDP with a contiguous address range and *EXPR with
a pointer to an array of operations with length *EXPRLEN.  If
the attribute describes a single location description and not a
location list the first call (with OFFSET zero) will return the
location description in *EXPR with *STARTP set to zero and *ENDP
set to minus one.

ptrdiff_t dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset,
                              Dwarf_Addr *basep, Dwarf_Addr *startp,
                              Dwarf_Addr *endp, Dwarf_Op **expr,
                              size_t *exprlen);

Signed-off-by: Mark Wielaard <mjw@redhat.com>
This commit is contained in:
Mark Wielaard
2013-08-23 16:12:37 +02:00
parent 92e47d616d
commit 66eaae9bcc
4 changed files with 250 additions and 77 deletions
+12
View File
@@ -1,3 +1,15 @@
2013-08-23 Mark Wielaard <mjw@redhat.com>
* dwarf_getlocation.c (attr_ok): Also accept DW_AT_segment.
(attr_base_address): New function.
(initial_offset_base): New function.
(getlocations_addr): New function. Taken from...
(dwarf_getlocation_addr): here. Use new initial_offset_base and
getlocations_addr.
(dwarf_getlocations): New function.
* libdw.h (dwarf_getlocations): New function definition.
* libdw.map (ELFUTILS_0.157): New.
2013-07-02 Mark Wielaard <mjw@redhat.com>
* dwarf_getsrclines.c (dwarf_getsrclines): Add new stack allocation
+216 -76
View File
@@ -1,5 +1,5 @@
/* Return location expression list.
Copyright (C) 2000-2010 Red Hat, Inc.
Copyright (C) 2000-2010, 2013 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2000.
@@ -56,6 +56,7 @@ attr_ok (Dwarf_Attribute *attr)
case DW_AT_frame_base:
case DW_AT_return_addr:
case DW_AT_static_link:
case DW_AT_segment:
break;
default:
@@ -567,6 +568,121 @@ dwarf_getlocation (attr, llbuf, listlen)
return getlocation (attr->cu, &block, llbuf, listlen, cu_sec_idx (attr->cu));
}
static int
attr_base_address (attr, basep)
Dwarf_Attribute *attr;
Dwarf_Addr *basep;
{
/* Fetch the CU's base address. */
Dwarf_Die cudie = CUDIE (attr->cu);
/* Find the base address of the compilation unit. 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 emits DW_AT_entry_pc and not DW_AT_lowpc
for compilation units with discontinuous ranges. */
Dwarf_Attribute attr_mem;
if (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0)
&& INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
DW_AT_entry_pc,
&attr_mem),
basep) != 0)
{
if (INTUSE(dwarf_errno) () != 0)
return -1;
/* The compiler provided no base address when it should
have. Buggy GCC does this when it used absolute
addresses in the location list and no DW_AT_ranges. */
*basep = 0;
}
return 0;
}
static int
initial_offset_base (attr, offset, basep)
Dwarf_Attribute *attr;
ptrdiff_t *offset;
Dwarf_Addr *basep;
{
if (attr_base_address (attr, basep) != 0)
return -1;
Dwarf_Word start_offset;
if (__libdw_formptr (attr, IDX_debug_loc,
DWARF_E_NO_LOCLIST,
NULL, &start_offset) == NULL)
return -1;
*offset = start_offset;
return 0;
}
static ptrdiff_t
getlocations_addr (attr, offset, basep, startp, endp, address,
locs, expr, exprlen)
Dwarf_Attribute *attr;
ptrdiff_t offset;
Dwarf_Addr *basep;
Dwarf_Addr *startp;
Dwarf_Addr *endp;
Dwarf_Addr address;
Elf_Data *locs;
Dwarf_Op **expr;
size_t *exprlen;
{
unsigned char *readp = locs->d_buf + offset;
unsigned char *readendp = locs->d_buf + locs->d_size;
next:
if (readendp - readp < attr->cu->address_size * 2)
{
invalid:
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
Dwarf_Addr begin;
Dwarf_Addr end;
switch (__libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
&readp, attr->cu->address_size,
&begin, &end, basep))
{
case 0: /* got location range. */
break;
case 1: /* base address setup. */
goto next;
case 2: /* end of loclist */
return 0;
default: /* error */
return -1;
}
if (readendp - readp < 2)
goto invalid;
/* We have a location expression. */
Dwarf_Block block;
block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
block.data = readp;
if (readendp - readp < (ptrdiff_t) block.length)
goto invalid;
readp += block.length;
*startp = *basep + begin;
*endp = *basep + end;
/* If address is minus one we want them all, otherwise only matching. */
if (address != (Dwarf_Word) -1 && (address < *startp || address >= *endp))
goto next;
if (getlocation (attr->cu, &block, expr, exprlen, IDX_debug_loc) != 0)
return -1;
return readp - (unsigned char *) locs->d_buf;
}
int
dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
Dwarf_Attribute *attr;
@@ -605,85 +721,109 @@ dwarf_getlocation_addr (attr, address, llbufs, listlens, maxlocs)
if (result != 1)
return result ?: 1;
unsigned char *endp;
unsigned char *readp = __libdw_formptr (attr, IDX_debug_loc,
DWARF_E_NO_LOCLIST, &endp, NULL);
if (readp == NULL)
Dwarf_Addr base, start, end;
Dwarf_Op *expr;
size_t expr_len;
ptrdiff_t off = 0;
size_t got = 0;
/* This is a true loclistptr, fetch the initial base address and offset. */
if (initial_offset_base (attr, &off, &base) != 0)
return -1;
Dwarf_Addr base = (Dwarf_Addr) -1;
size_t got = 0;
while (got < maxlocs)
const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
if (d == NULL)
{
if (endp - readp < attr->cu->address_size * 2)
{
invalid:
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
Dwarf_Addr begin;
Dwarf_Addr end;
int status
= __libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
&readp, attr->cu->address_size,
&begin, &end, &base);
if (status == 2) /* End of list entry. */
break;
else if (status == 1) /* Base address selected. */
continue;
else if (status < 0)
return status;
if (endp - readp < 2)
goto invalid;
/* We have a location expression. */
block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
block.data = readp;
if (endp - readp < (ptrdiff_t) block.length)
goto invalid;
readp += block.length;
if (base == (Dwarf_Addr) -1)
{
/* Fetch the CU's base address. */
Dwarf_Die cudie = CUDIE (attr->cu);
/* Find the base address of the compilation unit. 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 emits DW_AT_entry_pc and not DW_AT_lowpc
for compilation units with discontinuous ranges. */
Dwarf_Attribute attr_mem;
if (unlikely (INTUSE(dwarf_lowpc) (&cudie, &base) != 0)
&& INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
DW_AT_entry_pc,
&attr_mem),
&base) != 0)
{
if (INTUSE(dwarf_errno) () != 0)
return -1;
/* The compiler provided no base address when it should
have. Buggy GCC does this when it used absolute
addresses in the location list and no DW_AT_ranges. */
base = 0;
}
}
if (address >= base + begin && address < base + end)
{
/* This one matches the address. */
if (llbufs != NULL
&& unlikely (getlocation (attr->cu, &block,
&llbufs[got], &listlens[got],
IDX_debug_loc) != 0))
return -1;
++got;
}
__libdw_seterrno (DWARF_E_NO_LOCLIST);
return -1;
}
while (got < maxlocs
&& (off = getlocations_addr (attr, off, &base, &start, &end,
address, d, &expr, &expr_len)) > 0)
{
/* This one matches the address. */
if (llbufs != NULL)
{
llbufs[got] = expr;
listlens[got] = expr_len;
}
++got;
}
/* We might stop early, so off can be zero or positive on success. */
if (off < 0)
return -1;
return got;
}
ptrdiff_t
dwarf_getlocations (attr, offset, basep, startp, endp, expr, exprlen)
Dwarf_Attribute *attr;
ptrdiff_t offset;
Dwarf_Addr *basep;
Dwarf_Addr *startp;
Dwarf_Addr *endp;
Dwarf_Op **expr;
size_t *exprlen;
{
if (! attr_ok (attr))
return -1;
/* 1 is an invalid offset, meaning no more locations. */
if (offset == 1)
return 0;
if (offset == 0)
{
/* If it has a block form, it's a single location expression. */
Dwarf_Block block;
if (INTUSE(dwarf_formblock) (attr, &block) == 0)
{
if (getlocation (attr->cu, &block, expr, exprlen,
cu_sec_idx (attr->cu)) != 0)
return -1;
/* This is the one and only location covering everything. */
*startp = 0;
*endp = -1;
return 1;
}
int error = INTUSE(dwarf_errno) ();
if (unlikely (error != DWARF_E_NO_BLOCK))
{
__libdw_seterrno (error);
return -1;
}
int result = check_constant_offset (attr, expr, exprlen);
if (result != 1)
{
if (result == 0)
{
/* This is the one and only location covering everything. */
*startp = 0;
*endp = -1;
return 1;
}
return result;
}
/* We must be looking at a true loclistptr, fetch the initial
base address and offset. */
if (initial_offset_base (attr, &offset, basep) != 0)
return -1;
}
const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
if (d == NULL)
{
__libdw_seterrno (DWARF_E_NO_LOCLIST);
return -1;
}
return getlocations_addr (attr, offset, basep, startp, endp,
(Dwarf_Word) -1, d, expr, exprlen);
}
+17 -1
View File
@@ -1,5 +1,5 @@
/* Interfaces for libdw.
Copyright (C) 2002-2010 Red Hat, Inc.
Copyright (C) 2002-2010, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -630,6 +630,22 @@ extern int dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address,
Dwarf_Op **exprs, size_t *exprlens,
size_t nlocs);
/* Enumerate the locations ranges and descriptions covered by the
given attribute. In the first call OFFSET should be zero and
*BASEP need not be initialized. Returns -1 for errors, zero when
there are no more locations to report, or a nonzero OFFSET
value to pass to the next call. Each subsequent call must preserve
*BASEP from the prior call. Successful calls fill in *STARTP and
*ENDP with a contiguous address range and *EXPR with a pointer to
an array of operations with length *EXPRLEN. If the attribute
describes a single location description and not a location list the
first call (with OFFSET zero) will return the location description
in *EXPR with *STARTP set to zero and *ENDP set to minus one. */
extern ptrdiff_t dwarf_getlocations (Dwarf_Attribute *attr,
ptrdiff_t offset, Dwarf_Addr *basep,
Dwarf_Addr *startp, Dwarf_Addr *endp,
Dwarf_Op **expr, size_t *exprlen);
/* Return the block associated with a DW_OP_implicit_value operation.
The OP pointer must point into an expression that dwarf_getlocation
or dwarf_getlocation_addr has returned given the same ATTR. */
+5
View File
@@ -260,3 +260,8 @@ ELFUTILS_0.156 {
# Replaced ELFUTILS_0.122 version, which has a wrapper without add_p_vaddr.
dwfl_report_elf;
} ELFUTILS_0.149;
ELFUTILS_0.157 {
global:
dwarf_getlocations;
} ELFUTILS_0.156;