libdrgn: dwarf_info_cache: handle variables DW_AT_const_value

Compile-time constants have DW_AT_const_value instead of DW_AT_location.
We can translate those to a value object.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2020-07-13 15:21:55 -07:00
parent 213c148ce6
commit 6d4af7e17e
5 changed files with 217 additions and 21 deletions

View File

@ -10,6 +10,7 @@
#include "dwarf_index.h"
#include "dwarf_info_cache.h"
#include "hash_table.h"
#include "object.h"
#include "object_index.h"
#include "type_index.h"
#include "vector.h"
@ -1528,6 +1529,55 @@ drgn_object_from_dwarf_subprogram(struct drgn_dwarf_info_cache *dicache,
0, byte_order);
}
static struct drgn_error *
drgn_object_from_dwarf_constant(struct drgn_dwarf_info_cache *dicache,
Dwarf_Die *die,
struct drgn_qualified_type qualified_type,
Dwarf_Attribute *attr, struct drgn_object *ret)
{
struct drgn_object_type type;
enum drgn_object_kind kind;
uint64_t bit_size;
struct drgn_error *err = drgn_object_set_common(qualified_type, 0,
&type, &kind,
&bit_size);
if (err)
return err;
Dwarf_Block block;
if (dwarf_formblock(attr, &block) == 0) {
bool little_endian;
err = dwarf_die_is_little_endian(die, true, &little_endian);
if (err)
return err;
if (block.length < drgn_value_size(bit_size, 0)) {
return drgn_error_create(DRGN_ERROR_OTHER,
"DW_AT_const_value block is too small");
}
return drgn_object_set_buffer_internal(ret, &type, kind,
bit_size, block.data, 0,
little_endian);
} else if (kind == DRGN_OBJECT_SIGNED) {
Dwarf_Sword svalue;
if (dwarf_formsdata(attr, &svalue)) {
return drgn_error_create(DRGN_ERROR_OTHER,
"invalid DW_AT_const_value");
}
return drgn_object_set_signed_internal(ret, &type, bit_size,
svalue);
} else if (kind == DRGN_OBJECT_UNSIGNED) {
Dwarf_Word uvalue;
if (dwarf_formudata(attr, &uvalue)) {
return drgn_error_create(DRGN_ERROR_OTHER,
"invalid DW_AT_const_value");
}
return drgn_object_set_unsigned_internal(ret, &type, bit_size,
uvalue);
} else {
return drgn_error_create(DRGN_ERROR_OTHER,
"unknown DW_AT_const_value form");
}
}
static struct drgn_error *
drgn_object_from_dwarf_variable(struct drgn_dwarf_info_cache *dicache,
Dwarf_Die *die, uint64_t bias, const char *name,
@ -1541,26 +1591,32 @@ drgn_object_from_dwarf_variable(struct drgn_dwarf_info_cache *dicache,
if (err)
return err;
Dwarf_Attribute attr_mem, *attr;
if (!(attr = dwarf_attr_integrate(die, DW_AT_location, &attr_mem))) {
if ((attr = dwarf_attr_integrate(die, DW_AT_location, &attr_mem))) {
Dwarf_Op *loc;
size_t nloc;
if (dwarf_getlocation(attr, &loc, &nloc))
return drgn_error_libdw();
if (nloc != 1 || loc[0].atom != DW_OP_addr) {
return drgn_error_create(DRGN_ERROR_OTHER,
"DW_AT_location has unimplemented operation");
}
enum drgn_byte_order byte_order;
err = dwarf_die_byte_order(die, true, &byte_order);
if (err)
return err;
return drgn_object_set_reference(ret, qualified_type,
loc[0].number + bias, 0, 0,
byte_order);
} else if ((attr = dwarf_attr_integrate(die, DW_AT_const_value,
&attr_mem))) {
return drgn_object_from_dwarf_constant(dicache, die,
qualified_type, attr,
ret);
} else {
return drgn_error_format(DRGN_ERROR_LOOKUP,
"could not find address of '%s'",
"could not find address or value of '%s'",
name);
}
Dwarf_Op *loc;
size_t nloc;
if (dwarf_getlocation(attr, &loc, &nloc))
return drgn_error_libdw();
if (nloc != 1 || loc[0].atom != DW_OP_addr) {
return drgn_error_create(DRGN_ERROR_OTHER,
"DW_AT_location has unimplemented operation");
}
enum drgn_byte_order byte_order;
err = dwarf_die_byte_order(die, true, &byte_order);
if (err)
return err;
return drgn_object_set_reference(ret, qualified_type,
loc[0].number + bias, 0, 0,
byte_order);
}
struct drgn_error *

View File

@ -104,7 +104,7 @@ drgn_object_set_common(struct drgn_qualified_type qualified_type,
return drgn_object_type_kind_and_size(type_ret, kind_ret, bit_size_ret);
}
static struct drgn_error *
struct drgn_error *
drgn_object_set_signed_internal(struct drgn_object *res,
const struct drgn_object_type *type,
uint64_t bit_size, int64_t svalue)
@ -140,7 +140,7 @@ drgn_object_set_signed(struct drgn_object *res,
return drgn_object_set_signed_internal(res, &type, bit_size, svalue);
}
static struct drgn_error *
struct drgn_error *
drgn_object_set_unsigned_internal(struct drgn_object *res,
const struct drgn_object_type *type,
uint64_t bit_size, uint64_t uvalue)
@ -296,7 +296,7 @@ drgn_byte_order_to_little_endian(struct drgn_program *prog,
}
}
static struct drgn_error *
struct drgn_error *
drgn_object_set_buffer_internal(struct drgn_object *res,
const struct drgn_object_type *type,
enum drgn_object_kind kind, uint64_t bit_size,

View File

@ -129,6 +129,35 @@ struct drgn_error *sanity_check_object(enum drgn_object_kind kind,
uint64_t bit_field_size,
uint64_t bit_size);
/**
* Like @ref drgn_object_set_signed() but @ref drgn_object_set_common() was
* already called.
*/
struct drgn_error *
drgn_object_set_signed_internal(struct drgn_object *res,
const struct drgn_object_type *type,
uint64_t bit_size, int64_t svalue);
/**
* Like @ref drgn_object_set_unsigned() but @ref drgn_object_set_common() was
* already called.
*/
struct drgn_error *
drgn_object_set_unsigned_internal(struct drgn_object *res,
const struct drgn_object_type *type,
uint64_t bit_size, uint64_t uvalue);
/**
* Like @ref drgn_object_set_buffer() but @ref drgn_object_set_common() was
* already called.
*/
struct drgn_error *
drgn_object_set_buffer_internal(struct drgn_object *res,
const struct drgn_object_type *type,
enum drgn_object_kind kind, uint64_t bit_size,
const void *buf, uint8_t bit_offset,
bool little_endian);
/** Convert a @ref drgn_byte_order to a boolean. */
struct drgn_error *
drgn_byte_order_to_little_endian(struct drgn_program *prog,

View File

@ -90,10 +90,19 @@ def _compile_debug_info(cu_die, little_endian, bits):
buf.extend(value.to_bytes(bits // 8, byteorder))
elif attrib.form == DW_FORM.data1:
buf.append(value)
elif attrib.form == DW_FORM.data2:
buf.extend(value.to_bytes(2, byteorder))
elif attrib.form == DW_FORM.data4:
buf.extend(value.to_bytes(4, byteorder))
elif attrib.form == DW_FORM.data8:
buf.extend(value.to_bytes(8, byteorder))
elif attrib.form == DW_FORM.udata:
_append_uleb128(buf, value)
elif attrib.form == DW_FORM.sdata:
_append_sleb128(buf, value)
elif attrib.form == DW_FORM.block1:
buf.append(len(value))
buf.extend(value)
elif attrib.form == DW_FORM.string:
buf.extend(value.encode())
buf.append(0)

View File

@ -2033,12 +2033,114 @@ class TestObjects(ObjectTestCase):
del dies[1].attribs[2]
prog = dwarf_program(dies)
self.assertRaisesRegex(LookupError, "could not find address", prog.object, "x")
self.assertRaisesRegex(
LookupError, "could not find address or value", prog.object, "x"
)
dies[1].attribs.insert(2, DwarfAttrib(DW_AT.location, DW_FORM.exprloc, b"\xe0"))
prog = dwarf_program(dies)
self.assertRaisesRegex(Exception, "unimplemented operation", prog.object, "x")
def test_const_signed(self):
for form in (
DW_FORM.data1,
DW_FORM.data2,
DW_FORM.data4,
DW_FORM.data8,
DW_FORM.sdata,
):
dies = [
int_die,
DwarfDie(
DW_TAG.variable,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, "x"),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 0),
DwarfAttrib(DW_AT.const_value, form, 1,),
],
),
]
prog = dwarf_program(dies)
self.assertEqual(
prog["x"], Object(prog, int_type("int", 4, True), 1),
)
def test_const_unsigned(self):
for form in (
DW_FORM.data1,
DW_FORM.data2,
DW_FORM.data4,
DW_FORM.data8,
DW_FORM.udata,
):
dies = [
unsigned_int_die,
DwarfDie(
DW_TAG.variable,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, "x"),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 0),
DwarfAttrib(DW_AT.const_value, form, 1),
],
),
]
prog = dwarf_program(dies)
self.assertEqual(
prog["x"], Object(prog, int_type("unsigned int", 4, False), 1),
)
def test_const_block(self):
dies = [
int_die,
DwarfDie(
DW_TAG.structure_type,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, "point"),
DwarfAttrib(DW_AT.byte_size, DW_FORM.data1, 8),
],
[
DwarfDie(
DW_TAG.member,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, "x"),
DwarfAttrib(DW_AT.data_member_location, DW_FORM.data1, 0),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 0),
],
),
DwarfDie(
DW_TAG.member,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, "y"),
DwarfAttrib(DW_AT.data_member_location, DW_FORM.data1, 4),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 0),
],
),
],
),
DwarfDie(
DW_TAG.variable,
[
DwarfAttrib(DW_AT.name, DW_FORM.string, "p"),
DwarfAttrib(DW_AT.type, DW_FORM.ref4, 1),
DwarfAttrib(
DW_AT.const_value,
DW_FORM.block1,
b"\x01\x00\x00\x00\x02\x00\x00\x00",
),
],
),
]
prog = dwarf_program(dies)
self.assertEqual(
prog["p"], Object(prog, point_type, {"x": 1, "y": 2}),
)
dies[2].attribs[2] = DwarfAttrib(
DW_AT.const_value, DW_FORM.block1, b"\x01\x00\x00\x00\x02\x00\x00",
)
prog = dwarf_program(dies)
self.assertRaisesRegex(Exception, "too small", prog.variable, "p")
def test_not_found(self):
prog = dwarf_program([int_die])
self.assertRaisesRegex(LookupError, "could not find", prog.object, "y")