mirror of
https://github.com/openharmony/third_party_elfutils.git
synced 2026-07-20 23:45:11 -04:00
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:
@@ -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
@@ -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
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user