libdw: Add new functions dwarf_getlocation_attr and dwarf_getlocation_die.

Some location expression operations have a DIE associated with them.
Examples are some of the new GNU typed DWARF extensions, DW_OP_GNU_convert,
DW_OP_GNU_reinterpret, DW_OP_GNU_const_type, DW_OP_GNU_regval_type and
DW_OP_GNU_deref_type. Others have (block) values associated with them,
like DW_OP_GNU_entry_value and DW_OP_GNU_const_type.

It is not always easy to access these values. The DIE offset is given in
various formats either as global offset or CU relative offset. The (block)
value might be constant or a location description. And the block might be
encoded with a uleb128 or ubyte length. The new functions help to easily
get at the DIE or attribute value.

In theory dwarf_getlocation_attr could be used for all cases, since
besides returning DW_AT_const_value or DW_AT_location, it could also
return an attribute referencing a DIE. But at least one operation,
DW_OP_GNU_const_type, has both a (type) DIE and a constant (block)
value associated with it. And directly getting the DIE when needed
is easier than first having to retrieve a (synthesized) attribute
and then getting the actual (type) DIE.

Expression operations that reference an actual DIE for the
DW_AT_location or DW_AT_const_value, like DW_OP_call2, DW_OP_call4,
DW_OP_callref and DW_OP_GNU_implicit_pointer can be used with both
dwarf_getlocation_attr and dwarf_getlocation_die.

DW_OP_implicit_value and DW_OP_GNU_implicit_pointer already had
their own special accessors (dwarf_getlocation_implicit_value
and dwarf_getlocation_implicit_pointer), but it seemed consistent
to include them in the new more generic accessors too.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
This commit is contained in:
Mark Wielaard
2013-08-30 23:55:12 +02:00
parent 66eaae9bcc
commit b2535b6a6b
8 changed files with 250 additions and 18 deletions
+19
View File
@@ -1,3 +1,22 @@
2013-08-24 Mark Wielaard <mjw@redhat.com>
* dwarf_getlocation.c (store_implicit_value): Don't take data
as argument, get block data from op number2. Return false when
block data length and op number don't match up.
(__libdw_intern_expression): Store start of block for
DW_OP_implicit_value and DW_OP_GNU_entry_value instead of
relative data offset. Also store block start (including length)
for DW_OP_GNU_const_type. Don't pass data to store_implicit_value.
* dwarf_getlocation_attr.c: New file.
* dwarf_getlocation_die.c: Likewise.
* libdw.h (dwarf_getlocation_die): New function definition.
(dwarf_getlocation_attr): Likewise.
* libdwP.h: Declare internal dwarf_getlocation_die.
* libdw.map (ELFUTILS_0.157): Add dwarf_getlocation_die and
dwarf_getlocation_attr.
* Makefile.am (libdw_a_SOURCES): Add dwarf_getlocation_die.c and
dwarf_getlocation_attr.c.
2013-08-23 Mark Wielaard <mjw@redhat.com>
* dwarf_getlocation.c (attr_ok): Also accept DW_AT_segment.
+2 -1
View File
@@ -87,7 +87,8 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
dwarf_frame_info.c dwarf_frame_cfa.c dwarf_frame_register.c \
dwarf_cfi_addrframe.c \
dwarf_getcfi.c dwarf_getcfi_elf.c dwarf_cfi_end.c \
dwarf_aggregate_size.c dwarf_getlocation_implicit_pointer.c
dwarf_aggregate_size.c dwarf_getlocation_implicit_pointer.c \
dwarf_getlocation_die.c dwarf_getlocation_attr.c
if MAINTAINER_MODE
BUILT_SOURCES = $(srcdir)/known-dwarf.h
+21 -16
View File
@@ -95,13 +95,15 @@ loc_compare (const void *p1, const void *p2)
/* For each DW_OP_implicit_value, we store a special entry in the cache.
This points us directly to the block data for later fetching. */
static void
store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op,
unsigned char *data)
store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op)
{
struct loc_block_s *block = libdw_alloc (dbg, struct loc_block_s,
sizeof (struct loc_block_s), 1);
const unsigned char *data = (const unsigned char *) op->number2;
Dwarf_Word blength; // Ignored, equal to op->number.
get_uleb128 (blength, data);
block->addr = op;
block->data = data + op->number2;
block->data = (unsigned char *) data;
block->length = op->number;
(void) tsearch (block, cache, loc_compare);
}
@@ -412,11 +414,11 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
if (unlikely (dbg == NULL))
goto invalid;
newloc->number2 = (Dwarf_Word) data; /* start of block inc. len. */
/* XXX Check size. */
get_uleb128 (newloc->number, data); /* Block length. */
if (unlikely ((Dwarf_Word) (end_data - data) < newloc->number))
goto invalid;
newloc->number2 = data - block->data; /* Relative block offset. */
data += newloc->number; /* Skip the block. */
break;
@@ -437,17 +439,20 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
break;
case DW_OP_GNU_const_type:
/* XXX Check size. */
get_uleb128 (newloc->number, data);
if (unlikely (data >= end_data))
goto invalid;
newloc->number2 = *data++; /* Block length. */
if (unlikely ((Dwarf_Word) (end_data - data) < newloc->number2))
goto invalid;
/* The third operand is relative block offset:
newloc->number3 = data - block->data;
We don't support this at this point. */
data += newloc->number2; /* Skip the block. */
{
size_t size;
/* XXX Check size. */
get_uleb128 (newloc->number, data);
if (unlikely (data >= end_data))
goto invalid;
newloc->number2 = (Dwarf_Word) data; /* start of block inc. len. */
size = *data++;
if (unlikely ((Dwarf_Word) (end_data - data) < size))
goto invalid;
data += size; /* Skip the block. */
}
break;
default:
@@ -505,7 +510,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
result[n].offset = loclist->offset;
if (result[n].atom == DW_OP_implicit_value)
store_implicit_value (dbg, cache, &result[n], block->data);
store_implicit_value (dbg, cache, &result[n]);
loclist = loclist->next;
}
+103
View File
@@ -0,0 +1,103 @@
/* Return DWARF attribute associated with a location expression op.
Copyright (C) 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
it under the terms of either
* the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at
your option) any later version
or
* the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at
your option) any later version
or both in parallel, as here.
elfutils is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received copies of the GNU General Public License and
the GNU Lesser General Public License along with this program. If
not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <dwarf.h>
#include <libdwP.h>
int
dwarf_getlocation_attr (attr, op, result)
Dwarf_Attribute *attr;
const Dwarf_Op *op;
Dwarf_Attribute *result;
{
if (attr == NULL)
return -1;
result->cu = attr->cu;
switch (op->atom)
{
case DW_OP_implicit_value:
result->code = DW_AT_const_value;
result->form = DW_FORM_block;
result->valp = (unsigned char *) op->number2;
break;
case DW_OP_GNU_entry_value:
result->code = DW_AT_location;
result->form = DW_FORM_exprloc;
result->valp = (unsigned char *) op->number2;
break;
case DW_OP_GNU_const_type:
result->code = DW_AT_const_value;
result->form = DW_FORM_block1;
result->valp = (unsigned char *) op->number2;
break;
case DW_OP_call2:
case DW_OP_call4:
case DW_OP_call_ref:
{
Dwarf_Die die;
if (INTUSE(dwarf_getlocation_die) (attr, op, &die) != 0)
return -1;
if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL)
{
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
}
break;
case DW_OP_GNU_implicit_pointer:
{
Dwarf_Die die;
if (INTUSE(dwarf_getlocation_die) (attr, op, &die) != 0)
return -1;
if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL
&& INTUSE(dwarf_attr) (&die, DW_AT_const_value, result) == NULL)
{
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
}
break;
default:
__libdw_seterrno (DWARF_E_INVALID_ACCESS);
return -1;
}
return 0;
}
+78
View File
@@ -0,0 +1,78 @@
/* Return DIE associated with a location expression op.
Copyright (C) 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
it under the terms of either
* the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at
your option) any later version
or
* the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at
your option) any later version
or both in parallel, as here.
elfutils is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received copies of the GNU General Public License and
the GNU Lesser General Public License along with this program. If
not, see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <dwarf.h>
#include <libdwP.h>
int
dwarf_getlocation_die (attr, op, result)
Dwarf_Attribute *attr;
const Dwarf_Op *op;
Dwarf_Die *result;
{
if (attr == NULL)
return -1;
Dwarf_Off dieoff;
switch (op->atom)
{
case DW_OP_GNU_implicit_pointer:
case DW_OP_call_ref:
dieoff = op->number;
break;
case DW_OP_GNU_parameter_ref:
case DW_OP_GNU_convert:
case DW_OP_GNU_reinterpret:
case DW_OP_GNU_const_type:
case DW_OP_call2:
case DW_OP_call4:
dieoff = attr->cu->start + op->number;
break;
case DW_OP_GNU_regval_type:
case DW_OP_GNU_deref_type:
dieoff = attr->cu->start + op->number2;
break;
default:
__libdw_seterrno (DWARF_E_INVALID_ACCESS);
return -1;
}
if (__libdw_offdie (attr->cu->dbg, dieoff, result,
attr->cu->type_offset != 0) == NULL)
return -1;
return 0;
}
INTDEF(dwarf_getlocation_die);
+23
View File
@@ -664,6 +664,29 @@ extern int dwarf_getlocation_implicit_pointer (Dwarf_Attribute *attr,
Dwarf_Attribute *result)
__nonnull_attribute__ (2, 3);
/* Return the DIE associated with an operation such as
DW_OP_GNU_implicit_pointer, DW_OP_GNU_parameter_ref, DW_OP_GNU_convert,
DW_OP_GNU_reinterpret, DW_OP_GNU_const_type, DW_OP_GNU_regval_type or
DW_OP_GNU_deref_type. The OP pointer must point into an expression that
dwarf_getlocation or dwarf_getlocation_addr has returned given the same
ATTR. The RESULT is a DIE that expresses a type or value needed by the
given OP. */
extern int dwarf_getlocation_die (Dwarf_Attribute *attr,
const Dwarf_Op *op,
Dwarf_Die *result)
__nonnull_attribute__ (2, 3);
/* Return the attribute expressing a value associated with an operation such
as DW_OP_implicit_value, DW_OP_GNU_entry_value or DW_OP_GNU_const_type.
The OP pointer must point into an expression that dwarf_getlocation
or dwarf_getlocation_addr has returned given the same ATTR.
The RESULT is a value expressed by an attribute such as DW_AT_location
or DW_AT_const_value. */
extern int dwarf_getlocation_attr (Dwarf_Attribute *attr,
const Dwarf_Op *op,
Dwarf_Attribute *result)
__nonnull_attribute__ (2, 3);
/* Compute the byte-size of a type DIE according to DWARF rules.
For most types, this is just DW_AT_byte_size.
+2
View File
@@ -264,4 +264,6 @@ ELFUTILS_0.156 {
ELFUTILS_0.157 {
global:
dwarf_getlocations;
dwarf_getlocation_die;
dwarf_getlocation_attr;
} ELFUTILS_0.156;
+2 -1
View File
@@ -1,5 +1,5 @@
/* Internal definitions for libdwarf.
Copyright (C) 2002-2011 Red Hat, Inc.
Copyright (C) 2002-2011, 2013 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper@redhat.com>, 2002.
@@ -654,6 +654,7 @@ INTDECL (dwarf_formudata)
INTDECL (dwarf_getarange_addr)
INTDECL (dwarf_getarangeinfo)
INTDECL (dwarf_getaranges)
INTDECL (dwarf_getlocation_die)
INTDECL (dwarf_getsrcfiles)
INTDECL (dwarf_getsrclines)
INTDECL (dwarf_hasattr)