[PATCH] Fix section corruption bug
by Thilo Schulz
Hi,
When adding data to existing sections in ELF files, libelf may corrupt
those sections, i.e. overwrite the existing data if certain conditions are
met.
If an Elf_Scn structure has seen a call to elf_rawdata(scn) before but no
call to elf_getdata(scn), scn->read_data flag is set, but not
scn->data_list_rear.
Thus, elf_newdata(scn) incorrectly detects a "new user added section" when
really it is a section with live, valid data that will be overwritten by
elf_update(), corrupting the section.
This patch fixes this incorrect behaviour.
Signed-off-by: Thilo Schulz <thilo(a)tjps.eu>
---
libelf/elf_newdata.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libelf/elf_newdata.c b/libelf/elf_newdata.c
index 90d1813..f90eb0a 100644
--- a/libelf/elf_newdata.c
+++ b/libelf/elf_newdata.c
@@ -64,7 +64,7 @@ elf_newdata (Elf_Scn *scn)
rwlock_wrlock (scn->elf->lock);
- if (scn->data_read && scn->data_list_rear == NULL)
+ if (scn->data_read && scn->data_list_rear == NULL && !scn->rawdata.s)
{
/* This means the section was created by the user and this is the
first data. */
--
1.7.10.4
9 years
[PATCH][v2] Support .debug_macro
by Petr Machata
Hi there,
in comparison with the previous version, this doesn't invent custom
bounds-checking primitives and just does what the rest of libdw does.
It also uses the read_addr_unaligned_inc that I introduced in the
other patch that I posted for review.
Thanks,
PM
---8<-------------------------------------------------------------
- This code is based on the following proposal:
http://www.dwarfstd.org/ShowIssue.php?issue=110722.1
- dwarf_getmacros is left to support only .debug_macinfo. The only
problematic opcode really is DW_MACINFO_vendor_ext, that the new
standard lacks, and that could in theory be misinterpreted by extant
code as whatever operand a vendor decides to map to 0xff.
- Instead, two principial iteration interfaces are provided for access
to new-style sections: dwarf_getmacros_die and dwarf_getmacros_off.
And a suite of new helper interfaces for analysis of individual
opcodes: dwarf_macro_version, dwarf_macro_line_offset,
dwarf_macro_getparamcnt, dwarf_macro_param.
- The existing interfaces dwarf_macro_opcode, dwarf_macro_param1 and
dwarf_macro_param2 remain operational for old- as well as new-style
Dwarf macro sections, if applicable.
Signed-off-by: Petr Machata <pmachata(a)redhat.com>
---
libdw/ChangeLog | 32 +++
libdw/Makefile.am | 8 +-
libdw/dwarf_end.c | 1 +
libdw/dwarf_error.c | 3 +-
libdw/dwarf_getmacros.c | 496 ++++++++++++++++++++++++++++++++-------
libdw/dwarf_macro_getparamcnt.c | 43 ++++
libdw/dwarf_macro_line_offset.c | 45 ++++
libdw/dwarf_macro_param.c | 46 ++++
libdw/dwarf_macro_param1.c | 8 +-
libdw/dwarf_macro_param2.c | 18 +-
libdw/dwarf_macro_version.c | 44 ++++
libdw/libdw.h | 87 +++++++-
libdw/libdw.map | 11 +-
libdw/libdwP.h | 54 ++++-
14 files changed, 785 insertions(+), 111 deletions(-)
create mode 100644 libdw/dwarf_macro_getparamcnt.c
create mode 100644 libdw/dwarf_macro_line_offset.c
create mode 100644 libdw/dwarf_macro_param.c
create mode 100644 libdw/dwarf_macro_version.c
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 410b31a..a9d2166 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,35 @@
+2014-09-10 Petr Machata <pmachata(a)redhat.com>
+
+ * dwarf_macro_getparamcnt.c: New file.
+ * dwarf_macro_line_offset.c: New file.
+ * dwarf_macro_param.c: New file.
+ * dwarf_macro_version.c: New file.
+ * Makefile.am (libdw_a_SOURCES): Add the new files.
+ * libdwP.h (DWARF_E_INVALID_OPCODE): New enumerator.
+ (struct Dwarf): New field macro_ops.
+ (Dwarf_Macro_Op_Proto, Dwarf_Macro_Op_Table): New structures for
+ keeping macro opcode prototypes in.
+ (Dwarf_Macro_s): Drop all fields, add new ones.
+ * dwarf_error.c (errmsgs): Add a message for
+ DWARF_E_INVALID_OPCODE.
+ * dwarf_end.c (dwarf_end): Destroy it here.
+ * libdw.h (dwarf_getmacros_die, dwarf_getmacros_off)
+ (dwarf_macro_version, dwarf_macro_line_offset)
+ (dwarf_macro_getparamcnt)
+ (dwarf_macro_param): New public interfaces.
+ * dwarf_getmacros.c: Implement them here.
+ (get_offset_from, macro_op_compare, build_table)
+ (init_macinfo_table, get_macinfo_table, get_table_for_offset)
+ (read_macros, gnu_macros_getmacros_off)
+ (macro_info_getmacros_off): Helpers.
+ (dwarf_getmacros): Adjust to dispatch to the new interfaces.
+ * dwarf_macro_param1.c (dwarf_macro_param1): Adjust to dispatch to
+ the new interfaces.
+ * dwarf_macro_param2.c (dwarf_macro_param2): Likewise.
+ * libdw.map (ELFUTILS_0.161): New. Add dwarf_getmacros_die,
+ dwarf_getmacros_off, dwarf_macro_version, dwarf_macro_getparamcnt,
+ dwarf_macro_param.
+
2014-09-12 Petr Machata <pmachata(a)redhat.com>
* memory-access.h (read_ubyte_unaligned_inc): Allow only 4- and
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 2e42a37..51b6a0c 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -71,9 +71,11 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
dwarf_getlocation.c dwarf_getstring.c dwarf_offabbrev.c \
dwarf_getaranges.c dwarf_onearange.c dwarf_getarangeinfo.c \
dwarf_getarange_addr.c dwarf_getattrs.c dwarf_formflag.c \
- dwarf_getmacros.c dwarf_macro_opcode.c dwarf_macro_param1.c \
- dwarf_macro_param2.c dwarf_addrdie.c \
- dwarf_getfuncs.c \
+ dwarf_getmacros.c dwarf_macro_getparamcnt.c \
+ dwarf_macro_line_offset.c dwarf_macro_opcode.c \
+ dwarf_macro_param.c dwarf_macro_param1.c \
+ dwarf_macro_param2.c dwarf_macro_version.c \
+ dwarf_addrdie.c dwarf_getfuncs.c \
dwarf_decl_file.c dwarf_decl_line.c dwarf_decl_column.c \
dwarf_func_inline.c dwarf_getsrc_file.c \
libdw_findcu.c libdw_form.c libdw_alloc.c \
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 241a257..26c38cb 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -92,6 +92,7 @@ dwarf_end (dwarf)
to be handled. */
tdestroy (dwarf->cu_tree, cu_free);
tdestroy (dwarf->tu_tree, cu_free);
+ tdestroy (dwarf->macro_ops, noop_free);
struct libdw_memblock *memp = dwarf->mem_tail;
/* The first block is allocated together with the Dwarf object. */
diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c
index 2292914..08b691a 100644
--- a/libdw/dwarf_error.c
+++ b/libdw/dwarf_error.c
@@ -1,5 +1,5 @@
/* Retrieve ELF descriptor used for DWARF access.
- Copyright (C) 2002, 2003, 2004, 2005, 2009 Red Hat, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005, 2009, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2002.
@@ -92,6 +92,7 @@ static const char *errmsgs[] =
[DWARF_E_NO_DEBUG_RANGES] = N_(".debug_ranges section missing"),
[DWARF_E_INVALID_CFI] = N_("invalid CFI section"),
[DWARF_E_NO_ALT_DEBUGLINK] = N_("no alternative debug link found"),
+ [DWARF_E_INVALID_OPCODE] = N_("invalid opcode"),
};
#define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0]))
diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c
index a3d2c81..52ba512 100644
--- a/libdw/dwarf_getmacros.c
+++ b/libdw/dwarf_getmacros.c
@@ -1,5 +1,5 @@
/* Get macro information.
- Copyright (C) 2002-2009 Red Hat, Inc.
+ Copyright (C) 2002-2009, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2002.
@@ -33,112 +33,446 @@
#include <dwarf.h>
#include <string.h>
+#include <search.h>
#include <libdwP.h>
+#include <stdio.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <stdlib.h>
-ptrdiff_t
-dwarf_getmacros (die, callback, arg, offset)
- Dwarf_Die *die;
- int (*callback) (Dwarf_Macro *, void *);
- void *arg;
- ptrdiff_t offset;
+static int
+get_offset_from (Dwarf_Die *die, int name, Dwarf_Word *retp)
{
- if (die == NULL)
+ /* Get the appropriate attribute. */
+ Dwarf_Attribute attr;
+ if (INTUSE(dwarf_attr) (die, name, &attr) == NULL)
+ return -1;
+
+ /* Offset into the corresponding section. */
+ return INTUSE(dwarf_formudata) (&attr, retp);
+}
+
+static int
+macro_op_compare (const void *p1, const void *p2)
+{
+ const Dwarf_Macro_Op_Table *t1 = (const Dwarf_Macro_Op_Table *) p1;
+ const Dwarf_Macro_Op_Table *t2 = (const Dwarf_Macro_Op_Table *) p2;
+
+ if (t1->offset < t2->offset)
return -1;
+ if (t1->offset > t2->offset + t2->read)
+ return 1;
+
+ return 0;
+}
+
+static void
+build_table (Dwarf_Macro_Op_Table *table,
+ Dwarf_Macro_Op_Proto op_protos[static 255])
+{
+ unsigned ct = 0;
+ for (unsigned i = 1; i < 256; ++i)
+ if (op_protos[i - 1].forms != NULL)
+ table->table[table->opcodes[i - 1] = ct++] = op_protos[i - 1];
+ else
+ table->opcodes[i] = 0xff;
+}
+
+#define MACRO_PROTO(NAME, ...) \
+ Dwarf_Macro_Op_Proto NAME = ({ \
+ static const uint8_t proto[] = {__VA_ARGS__}; \
+ (Dwarf_Macro_Op_Proto) {sizeof proto, proto}; \
+ })
+
+static Dwarf_Macro_Op_Table *
+init_macinfo_table (void)
+{
+ MACRO_PROTO (p_udata_str, DW_FORM_udata, DW_FORM_string);
+ MACRO_PROTO (p_udata_udata, DW_FORM_udata, DW_FORM_udata);
+ MACRO_PROTO (p_none);
+
+ Dwarf_Macro_Op_Proto op_protos[255] =
+ {
+ [DW_MACINFO_define - 1] = p_udata_str,
+ [DW_MACINFO_undef - 1] = p_udata_str,
+ [DW_MACINFO_vendor_ext - 1] = p_udata_str,
+ [DW_MACINFO_start_file - 1] = p_udata_udata,
+ [DW_MACINFO_end_file - 1] = p_none,
+ };
+
+ static Dwarf_Macro_Op_Table table;
+ memset (&table, 0, sizeof table);
+
+ build_table (&table, op_protos);
+ return &table;
+}
+
+static inline Dwarf_Macro_Op_Table *
+get_macinfo_table (void)
+{
+ static Dwarf_Macro_Op_Table *ret = NULL;
+ if (unlikely (ret == NULL))
+ ret = init_macinfo_table ();
+ return ret;
+}
+
+static Dwarf_Macro_Op_Table *
+get_table_for_offset (Dwarf *dbg, Dwarf_Word macoff,
+ const unsigned char *readp,
+ const unsigned char *const endp)
+{
+ Dwarf_Macro_Op_Table fake = { .offset = macoff };
+ Dwarf_Macro_Op_Table **found = tfind (&fake, &dbg->macro_ops,
+ macro_op_compare);
+ if (found != NULL)
+ return *found;
+
+ const unsigned char *startp = readp;
+
+ /* Request at least 3 bytes for header. */
+ if (readp + 3 > endp)
+ {
+ invalid_dwarf:
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return NULL;
+ }
- Elf_Data *d = die->cu->dbg->sectiondata[IDX_debug_macinfo];
- if (unlikely (d == NULL) || unlikely (d->d_buf == NULL))
+ uint16_t version = read_2ubyte_unaligned_inc (dbg, readp);
+ if (version != 4)
+ {
+ __libdw_seterrno (DWARF_E_INVALID_VERSION);
+ return NULL;
+ }
+
+ uint8_t flags = *readp++;
+ bool is_64bit = (flags & 0x1) != 0;
+
+ Dwarf_Off line_offset = (Dwarf_Off) -1;
+ if ((flags & 0x2) != 0)
+ {
+ line_offset = read_addr_unaligned_inc (is_64bit ? 8 : 4, dbg, readp);
+ if (readp > endp)
+ goto invalid_dwarf;
+ }
+
+ /* """The macinfo entry types defined in this standard may, but
+ might not, be described in the table""".
+
+ I.e. these may be present. It's tempting to simply skip them,
+ but it's probably more correct to tolerate that a producer tweaks
+ the way certain opcodes are encoded, for whatever reasons. */
+
+ MACRO_PROTO (p_udata_str, DW_FORM_udata, DW_FORM_string);
+ MACRO_PROTO (p_udata_strp, DW_FORM_udata, DW_FORM_strp);
+ MACRO_PROTO (p_udata_udata, DW_FORM_udata, DW_FORM_udata);
+ MACRO_PROTO (p_secoffset, DW_FORM_sec_offset);
+ MACRO_PROTO (p_none);
+
+ Dwarf_Macro_Op_Proto op_protos[255] =
+ {
+ [DW_MACRO_GNU_define - 1] = p_udata_str,
+ [DW_MACRO_GNU_undef - 1] = p_udata_str,
+ [DW_MACRO_GNU_define_indirect - 1] = p_udata_strp,
+ [DW_MACRO_GNU_undef_indirect - 1] = p_udata_strp,
+ [DW_MACRO_GNU_start_file - 1] = p_udata_udata,
+ [DW_MACRO_GNU_end_file - 1] = p_none,
+ [DW_MACRO_GNU_transparent_include - 1] = p_secoffset,
+ /* N.B. DW_MACRO_undef_indirectx, DW_MACRO_define_indirectx
+ should be added when 130313.1 is supported. */
+ };
+
+ if ((flags & 0x4) != 0)
+ {
+ unsigned count = *readp++;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ unsigned opcode = *readp++;
+
+ Dwarf_Macro_Op_Proto e;
+ get_uleb128 (e.nforms, readp); // XXX checking
+ e.forms = readp;
+ op_protos[opcode] = e;
+
+ readp += e.nforms;
+ if (readp > endp)
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return NULL;
+ }
+ }
+ }
+
+ size_t ct = 0;
+ for (unsigned i = 1; i < 256; ++i)
+ if (op_protos[i - 1].forms != NULL)
+ ++ct;
+
+ /* We support at most 0xfe opcodes defined in the table, as 0xff is
+ a value that means that given opcode is not stored at all. But
+ that should be fine, as opcode 0 is not allocated. */
+ assert (ct < 0xff);
+
+ size_t macop_table_size = offsetof (Dwarf_Macro_Op_Table, table[ct]);
+
+ Dwarf_Macro_Op_Table *table = libdw_alloc (dbg, Dwarf_Macro_Op_Table,
+ macop_table_size, 1);
+
+ *table = (Dwarf_Macro_Op_Table) {
+ .offset = macoff,
+ .line_offset = line_offset,
+ .header_len = readp - startp,
+ .version = version,
+ .is_64bit = is_64bit,
+ };
+ build_table (table, op_protos);
+
+ Dwarf_Macro_Op_Table **ret = tsearch (table, &dbg->macro_ops,
+ macro_op_compare);
+ if (unlikely (ret == NULL))
+ {
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return NULL;
+ }
+
+ return *ret;
+}
+
+static ptrdiff_t
+read_macros (Dwarf *dbg, Dwarf_Macro_Op_Table *table, int secindex,
+ Dwarf_Off macoff, int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t offset)
+{
+ Elf_Data *d = dbg->sectiondata[secindex];
+ if (unlikely (d == NULL || d->d_buf == NULL))
{
__libdw_seterrno (DWARF_E_NO_ENTRY);
return -1;
}
- if (offset == 0)
+ if (unlikely (macoff >= d->d_size))
{
- /* Get the appropriate attribute. */
- Dwarf_Attribute attr;
- if (INTUSE(dwarf_attr) (die, DW_AT_macro_info, &attr) == NULL)
- return -1;
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
- /* Offset into the .debug_macinfo section. */
- Dwarf_Word macoff;
- if (INTUSE(dwarf_formudata) (&attr, &macoff) != 0)
- return -1;
+ const unsigned char *const startp = d->d_buf + macoff;
+ const unsigned char *const endp = d->d_buf + d->d_size;
- offset = macoff;
+ if (table == NULL)
+ {
+ table = get_table_for_offset (dbg, macoff, startp, endp);
+ if (table == NULL)
+ return -1;
}
- if (unlikely (offset > (ptrdiff_t) d->d_size))
- goto invalid;
- const unsigned char *readp = d->d_buf + offset;
- const unsigned char *readendp = d->d_buf + d->d_size;
+ if (offset == 0)
+ offset = table->header_len;
- if (readp == readendp)
- return 0;
+ assert (offset >= 0);
+ assert (offset < endp - startp);
+ const unsigned char *readp = startp + offset;
- while (readp < readendp)
+ while (readp < endp)
{
unsigned int opcode = *readp++;
- unsigned int u128;
- unsigned int u128_2 = 0;
- const char *str = NULL;
- const unsigned char *endp;
+ if (opcode == 0)
+ /* Nothing more to do. */
+ return 0;
+
+ unsigned int idx = table->opcodes[opcode - 1];
+ if (idx == 0xff)
+ {
+ __libdw_seterrno (DWARF_E_INVALID_OPCODE);
+ return -1;
+ }
+
+ Dwarf_Macro_Op_Proto *proto = &table->table[idx];
- switch (opcode)
+ /* A fake CU with bare minimum data to fool dwarf_formX into
+ doing the right thing with the attributes that we put
+ out. */
+ Dwarf_CU fake_cu = {
+ .dbg = dbg,
+ .version = 4,
+ .offset_size = table->is_64bit ? 8 : 4,
+ };
+
+ Dwarf_Attribute attributes[proto->nforms];
+ for (Dwarf_Word i = 0; i < proto->nforms; ++i)
{
- case DW_MACINFO_define:
- case DW_MACINFO_undef:
- case DW_MACINFO_vendor_ext:
- /* For the first two opcodes the parameters are
- line, string
- For the latter
- number, string.
- We can treat these cases together. */
- get_uleb128 (u128, readp);
-
- endp = memchr (readp, '\0', readendp - readp);
- if (endp == NULL)
- goto invalid;
-
- str = (char *) readp;
- readp = endp + 1;
- break;
-
- case DW_MACINFO_start_file:
- /* The two parameters are line and file index. */
- get_uleb128 (u128, readp);
- get_uleb128 (u128_2, readp);
- break;
-
- case DW_MACINFO_end_file:
- /* No parameters for this one. */
- u128 = 0;
- break;
-
- case 0:
- /* Nothing more to do. */
- return 0;
-
- default:
- goto invalid;
+ /* We pretend this is a DW_AT_GNU_macros attribute so that
+ DW_FORM_sec_offset forms get correctly interpreted as
+ offset into .debug_macro. */
+ attributes[i].code = DW_AT_GNU_macros;
+ attributes[i].form = proto->forms[i];
+ attributes[i].valp = (void *) readp;
+ attributes[i].cu = &fake_cu;
+
+ readp += __libdw_form_val_len (dbg, &fake_cu,
+ proto->forms[i], readp);
}
- Dwarf_Macro mac;
- mac.opcode = opcode;
- mac.param1 = u128;
- if (str == NULL)
- mac.param2.u = u128_2;
- else
- mac.param2.s = str;
+ Dwarf_Macro macro = {
+ .line_offset = table->line_offset,
+ .version = table->version,
+ .opcode = opcode,
+ .nargs = proto->nforms,
+ .attributes = attributes,
+ };
+
+ Dwarf_Off nread = readp - startp;
+ if (nread > table->read)
+ table->read = nread;
+
+ if (callback (¯o, arg) != DWARF_CB_OK)
+ return readp - startp;
+ }
+
+ return 0;
+}
+
+static ptrdiff_t
+gnu_macros_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+{
+ assert (token <= 0);
- if (callback (&mac, arg) != DWARF_CB_OK)
- return readp - (const unsigned char *) d->d_buf;
+ ptrdiff_t ret = read_macros (dbg, NULL, IDX_debug_macro,
+ macoff, callback, arg, -token);
+ if (ret == -1)
+ return -1;
+ else
+ return -ret;
+}
+
+static ptrdiff_t
+macro_info_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+{
+ assert (token >= 0);
+
+ Dwarf_Macro_Op_Table *table = get_macinfo_table ();
+ assert (table != NULL);
+
+ return read_macros (dbg, table, IDX_debug_macinfo,
+ macoff, callback, arg, token);
+}
+
+ptrdiff_t
+dwarf_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+{
+ if (dbg == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NO_DWARF);
+ return -1;
+ }
+
+ /* We use token values > 0 for iteration through .debug_macinfo and
+ values < 0 for iteration through .debug_macro. Return value of
+ -1 also signifies an error, but that's fine, because .debug_macro
+ always contains at least three bytes of headers and after
+ iterating one opcode, we should never see anything above -4. */
+
+ if (token > 0)
+ /* A continuation call from DW_AT_macro_info iteration. */
+ return macro_info_getmacros_off (dbg, macoff, callback, arg, token);
+
+ /* Either a DW_AT_GNU_macros continuation, or a fresh start
+ thereof. */
+ return gnu_macros_getmacros_off (dbg, macoff, callback, arg, token);
+}
+
+ptrdiff_t
+dwarf_getmacros_die (Dwarf_Die *cudie, Dwarf_Off *macoffp,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+{
+ if (cudie == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NO_DWARF);
+ return -1;
+ }
+
+ if (token > 0 && macoffp != NULL)
+ /* A continuation call from DW_AT_macro_info iteration, meaning
+ *MACOFF contains previously-cached offset. */
+ return macro_info_getmacros_off (cudie->cu->dbg, *macoffp,
+ callback, arg, token);
+
+ /* A fresh start of DW_AT_macro_info iteration, or a continuation
+ thereof without a cache. */
+ if (token > 0
+ || (token == 0 && dwarf_hasattr (cudie, DW_AT_macro_info)))
+ {
+ Dwarf_Word macoff;
+ if (macoffp == NULL)
+ macoffp = &macoff;
+ if (get_offset_from (cudie, DW_AT_macro_info, macoffp) != 0)
+ return -1;
+ return macro_info_getmacros_off (cudie->cu->dbg, *macoffp,
+ callback, arg, token);
+ }
+
+ if (token < 0 && macoffp != NULL)
+ /* A continuation call from DW_AT_GNU_macros iteration. */
+ return gnu_macros_getmacros_off (cudie->cu->dbg, *macoffp,
+ callback, arg, token);
+
+ /* Likewise without cache, or iteration start. */
+ Dwarf_Word macoff;
+ if (macoffp == NULL)
+ macoffp = &macoff;
+ if (get_offset_from (cudie, DW_AT_GNU_macros, macoffp) != 0)
+ return -1;
+ return gnu_macros_getmacros_off (cudie->cu->dbg, *macoffp,
+ callback, arg, token);
+}
+
+ptrdiff_t
+dwarf_getmacros (die, callback, arg, offset)
+ Dwarf_Die *die;
+ int (*callback) (Dwarf_Macro *, void *);
+ void *arg;
+ ptrdiff_t offset;
+{
+ if (die == NULL)
+ return -1;
+
+ if (offset == 0)
+ {
+ /* We can't support .debug_macro transparently by
+ dwarf_getmacros, because extant callers would think that the
+ returned macro opcodes come from DW_MACINFO_* domain and be
+ confused. */
+ if (unlikely (! dwarf_hasattr (die, DW_AT_macro_info)))
+ {
+ __libdw_seterrno (DWARF_E_NO_ENTRY);
+ return -1;
+ }
+
+ /* DIE's with both DW_AT_GNU_macros and DW_AT_macro_info are
+ disallowed by the proposal that DW_AT_GNU_macros support is
+ based on, and this attribute would derail us above, so check
+ for it now. */
+ if (unlikely (dwarf_hasattr (die, DW_AT_GNU_macros)))
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
}
+ else
+ /* If non-zero, this ought to be a continuation from previous
+ DW_AT_macro_info iteration, meaning offset can't be
+ negative. */
+ assert (offset > 0);
- /* If we come here the termination of the data for the CU is not
- present. */
- invalid:
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return -1;
+ /* At this point we can safely piggy-back on existing new-style
+ interfaces. */
+ return dwarf_getmacros_die (die, NULL, callback, arg, offset);
}
diff --git a/libdw/dwarf_macro_getparamcnt.c b/libdw/dwarf_macro_getparamcnt.c
new file mode 100644
index 0000000..294134d
--- /dev/null
+++ b/libdw/dwarf_macro_getparamcnt.c
@@ -0,0 +1,43 @@
+/* Return number of parameters of a macro.
+ Copyright (C) 2014 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 "libdwP.h"
+
+int
+dwarf_macro_getparamcnt (Dwarf_Macro *macro, size_t *paramcntp)
+{
+ if (macro == NULL)
+ return -1;
+
+ *paramcntp = macro->nargs;
+ return 0;
+}
diff --git a/libdw/dwarf_macro_line_offset.c b/libdw/dwarf_macro_line_offset.c
new file mode 100644
index 0000000..531085c
--- /dev/null
+++ b/libdw/dwarf_macro_line_offset.c
@@ -0,0 +1,45 @@
+/* Return .debug_line offset associated with given macro.
+ Copyright (C) 2014 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
+
+ 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 "libdwP.h"
+
+
+int
+dwarf_macro_line_offset (Dwarf_Macro *macro, Dwarf_Off *offp)
+{
+ if (macro == NULL)
+ return -1;
+
+ *offp = macro->line_offset;
+ return 0;
+}
diff --git a/libdw/dwarf_macro_param.c b/libdw/dwarf_macro_param.c
new file mode 100644
index 0000000..b66b8ac
--- /dev/null
+++ b/libdw/dwarf_macro_param.c
@@ -0,0 +1,46 @@
+/* Return a given parameter of a macro.
+ Copyright (C) 2014 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 "libdwP.h"
+
+int
+dwarf_macro_param (Dwarf_Macro *macro, size_t idx, Dwarf_Attribute *ret)
+{
+ if (macro == NULL)
+ return -1;
+
+ if (idx >= macro->nargs)
+ return -1;
+
+ *ret = macro->attributes[idx];
+ return 0;
+}
diff --git a/libdw/dwarf_macro_param1.c b/libdw/dwarf_macro_param1.c
index 35d4a71..337ad6f 100644
--- a/libdw/dwarf_macro_param1.c
+++ b/libdw/dwarf_macro_param1.c
@@ -1,5 +1,5 @@
/* Return first macro parameter.
- Copyright (C) 2005 Red Hat, Inc.
+ Copyright (C) 2005, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
@@ -40,7 +40,9 @@ dwarf_macro_param1 (Dwarf_Macro *macro, Dwarf_Word *paramp)
if (macro == NULL)
return -1;
- *paramp = macro->param1;
+ Dwarf_Attribute param;
+ if (dwarf_macro_param (macro, 1, ¶m) != 0)
+ return -1;
- return 0;
+ return dwarf_formudata (¶m, paramp);
}
diff --git a/libdw/dwarf_macro_param2.c b/libdw/dwarf_macro_param2.c
index b49e60e..cc902c9 100644
--- a/libdw/dwarf_macro_param2.c
+++ b/libdw/dwarf_macro_param2.c
@@ -1,5 +1,5 @@
/* Return second macro parameter.
- Copyright (C) 2005 Red Hat, Inc.
+ Copyright (C) 2005, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
@@ -40,10 +40,16 @@ dwarf_macro_param2 (Dwarf_Macro *macro, Dwarf_Word *paramp, const char **strp)
if (macro == NULL)
return -1;
- if (paramp != NULL)
- *paramp = macro->param2.u;
- if (strp != NULL)
- *strp = macro->param2.s;
+ Dwarf_Attribute param;
+ if (dwarf_macro_param (macro, 1, ¶m) != 0)
+ return -1;
- return 0;
+ if (param.form == DW_FORM_string
+ || param.form == DW_FORM_strp)
+ {
+ *strp = dwarf_formstring (¶m);
+ return 0;
+ }
+ else
+ return dwarf_formudata (¶m, paramp);
}
diff --git a/libdw/dwarf_macro_version.c b/libdw/dwarf_macro_version.c
new file mode 100644
index 0000000..0b4b189
--- /dev/null
+++ b/libdw/dwarf_macro_version.c
@@ -0,0 +1,44 @@
+/* Return a version of a macro.
+ Copyright (C) 2014 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 "libdwP.h"
+
+int
+dwarf_macro_version (Dwarf_Macro *macro, unsigned int *versionp)
+{
+ if (macro == NULL)
+ return -1;
+
+ *versionp = macro->version;
+
+ return 0;
+}
diff --git a/libdw/libdw.h b/libdw/libdw.h
index 196d54a..bd5a0c6 100644
--- a/libdw/libdw.h
+++ b/libdw/libdw.h
@@ -827,25 +827,98 @@ extern int dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts);
/* Call callback function for each of the macro information entry for
- the CU. */
+ the CU. Similar in operation to dwarf_getmacros_die, but only
+ works for .debug_macinfo style macro entries (those where
+ dwarf_macro_version returns 0.) */
extern ptrdiff_t dwarf_getmacros (Dwarf_Die *cudie,
int (*callback) (Dwarf_Macro *, void *),
- void *arg, ptrdiff_t offset)
- __nonnull_attribute__ (2);
+ void *arg, ptrdiff_t token)
+ __nonnull_attribute__ (2);
+
+/* Iterate through the macro section referenced by CUDIE and call
+ CALLBACK for each macro information entry. Keeps iterating while
+ CALLBACK returns DWARF_CB_OK. If the callback returns
+ DWARF_CB_ABORT, it stops iterating and returns a continuation
+ token, which can be used to restart the iteration at the point
+ where it ended. Returns -1 for errors or 0 if there are no more
+ macro entries.
+
+ If MACOFFP is non-NULL, offset to macro section referenced by CUDIE
+ is stored to *MACOFFP. That can later be passed together with a
+ continuation token to dwarf_getmacros_off.
+
+ If this is called with non-0 TOKEN (i.e. it is a continuation
+ call), MACOFFP shall be either NULL, or point to a location that
+ contains the same section offset as was at *MACOFFP of the call
+ that the passed-in continuation token came from. */
+extern ptrdiff_t dwarf_getmacros_die (Dwarf_Die *cudie, Dwarf_Off *macoffp,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+ __nonnull_attribute__ (3);
+
+/* This is similar in operation to dwarf_getmacros_die, but selects
+ the section to iterate through by offset instead of by CU. This
+ can be used for handling DW_MACRO_GNU_transparent_include's or
+ similar opcodes, or for continuation calls seeded with MACOFF and
+ TOKEN that came back from dwarf_getmacros_die (or a previous call
+ to dwarf_getmacros_off). Note that with TOKEN of 0, this will
+ always iterate through DW_AT_GNU_macros style section. */
+extern ptrdiff_t dwarf_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+ __nonnull_attribute__ (3);
+
+/* Return Dwarf version of this macro opcode. The versions are 0 for
+ macro elements coming from DW_AT_macro_info, and 4 for macro
+ elements coming from DW_AT_GNU_macros. It is expected that 5 and
+ further will be for macro elements coming from standardized
+ DW_AT_macros. */
+extern int dwarf_macro_version (Dwarf_Macro *macro, unsigned int *versionp)
+ __nonnull_attribute__ (2);
+
+/* Set *OFFP to .debug_line offset associated with the Dwarf macro
+ section that this MACRO comes from. Returns -1 for errors or 0 for
+ success.
-/* Return macro opcode. */
+ The offset is considered to be 0xffffffffffffffff (or (Dwarf_Off)
+ -1) if .debug_line offset was not set in the section header, which
+ will always be the case for .debug_macinfo macro sections, and may
+ be the case for .debug_macro sections as well. This condition is
+ not considered an error. */
+extern int dwarf_macro_line_offset (Dwarf_Macro *macro, Dwarf_Off *offp)
+ __nonnull_attribute__ (2);
+
+/* Return macro opcode. That's a constant that can be either from
+ DW_MACINFO_* domain if version of MACRO is 0, or from
+ DW_MACRO_GNU_* domain if the version is 4. */
extern int dwarf_macro_opcode (Dwarf_Macro *macro, unsigned int *opcodep)
__nonnull_attribute__ (2);
-/* Return first macro parameter. */
+/* Get number of parameters of MACRO and store it to *PARAMCNTP. */
+extern int dwarf_macro_getparamcnt (Dwarf_Macro *macro, size_t *paramcntp);
+
+/* Get IDX-th parameter of MACRO, and stores it to *ATTRIBUTE.
+ Returns 0 on success or -1 for errors.
+
+ After a successful call, you can query ATTRIBUTE by dwarf_whatform
+ to determine which of the dwarf_formX calls to make to get actual
+ value out of ATTRIBUTE. Note that calling dwarf_whatattr is not
+ meaningful for pseudo-attributes formed this way. */
+extern int dwarf_macro_param (Dwarf_Macro *macro, size_t idx,
+ Dwarf_Attribute *attribute);
+
+/* Return first macro parameter. This will return -1 if the parameter
+ is not an integral value. Use dwarf_macro_param for more general
+ access. */
extern int dwarf_macro_param1 (Dwarf_Macro *macro, Dwarf_Word *paramp)
__nonnull_attribute__ (2);
-/* Return second macro parameter. */
+/* Return second macro parameter. This will return -1 if the
+ parameter is not an integral or string value. Use
+ dwarf_macro_param for more general access. */
extern int dwarf_macro_param2 (Dwarf_Macro *macro, Dwarf_Word *paramp,
const char **strp);
-
/* Compute what's known about a call frame when the PC is at ADDRESS.
Returns 0 for success or -1 for errors.
On success, *FRAME is a malloc'd pointer. */
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 55bc537..5a88bdc 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -306,4 +306,13 @@ ELFUTILS_0.160 {
global:
dwarf_cu_getdwarf;
dwarf_cu_die;
-} ELFUTILS_0.159;
\ No newline at end of file
+} ELFUTILS_0.159;
+
+ELFUTILS_0.161 {
+ global:
+ dwarf_getmacros_die;
+ dwarf_getmacros_off;
+ dwarf_macro_version;
+ dwarf_macro_getparamcnt;
+ dwarf_macro_param;
+} ELFUTILS_0.160;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index ce8a83d..47c2cc1 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -118,7 +118,8 @@ enum
DWARF_E_INVALID_OFFSET,
DWARF_E_NO_DEBUG_RANGES,
DWARF_E_INVALID_CFI,
- DWARF_E_NO_ALT_DEBUGLINK
+ DWARF_E_NO_ALT_DEBUGLINK,
+ DWARF_E_INVALID_OPCODE,
};
@@ -167,6 +168,9 @@ struct Dwarf
Dwarf_Off next_tu_offset;
Dwarf_Sig8_Hash sig8_hash;
+ /* Search tree for .debug_macro operator tables. */
+ void *macro_ops;
+
/* Address ranges. */
Dwarf_Aranges *aranges;
@@ -326,16 +330,48 @@ struct Dwarf_CU
}) \
-/* Macro information. */
+/* Prototype of a single .debug_macro operator. */
+typedef struct
+{
+ Dwarf_Word nforms;
+ unsigned char const *forms;
+} Dwarf_Macro_Op_Proto;
+
+/* Prototype table. */
+typedef struct
+{
+ /* Offset of .debug_macro section. */
+ Dwarf_Off offset;
+
+ /* Offset of associated .debug_line section. */
+ Dwarf_Off line_offset;
+
+ /* How far have we read in this section. This starts as 0 and is
+ increased as the section is worked through. */
+ Dwarf_Off read;
+
+ /* Header length. */
+ Dwarf_Half header_len;
+
+ uint16_t version;
+ bool is_64bit;
+
+ /* Shows where in TABLE each opcode is defined. Since opcode 0 is
+ never used, it stores index of opcode X in X-1'th element. The
+ value of 0xff means not stored at all. */
+ unsigned char opcodes[255];
+
+ /* Individual opcode prototypes. */
+ Dwarf_Macro_Op_Proto table[];
+} Dwarf_Macro_Op_Table;
+
struct Dwarf_Macro_s
{
- unsigned int opcode;
- Dwarf_Word param1;
- union
- {
- Dwarf_Word u;
- const char *s;
- } param2;
+ Dwarf_Off line_offset;
+ Dwarf_Word nargs;
+ uint16_t version;
+ uint8_t opcode;
+ Dwarf_Attribute *attributes;
};
--
1.7.6.5
9 years, 5 months
Re: [PATCH 2/2] Support note NT_FILE for locating files.
by Mark Wielaard
On Tue, 2014-09-09 at 23:00 +0200, Jan Kratochvil wrote:
> Martin Milata:
> ------------------------------------------------------------------------------
> RFE: dwfl_core_file_report: use NT_FILE core note if the link_map chain is broken
> https://bugzilla.redhat.com/show_bug.cgi?id=1129777
>
> The dwfl_core_file_report function follows dynamic linker's link_map chain in
> order to determine the shared libraries used by the executable. As this data
> structure is located in writable memory it can be overwritten by garbage, which
> is sometimes the case.
> https://github.com/abrt/satyr/issues/127#issuecomment-46957546
>
> Since version 3.7 (commit 2aa362c49), Linux kernel adds NT_FILE note to core
> files which contains the files mapped by the process, including shared
> libraries.
> ------------------------------------------------------------------------------
>
> dwfl_core_file_report now tries to fall back on NT_FILE if the link_map chain
> is broken.
>
> elfutils would already find the appropriate binary file from
> /usr/lib/debug/.build-id/ symbolic links. But those symbolic links do not have
> to be present on the system while NT_FILE still points to the correct binaries.
>
> Filenames from the note NT_FILE are used only if link_map filenames failed to
> locate matching binaries.
This makes sense to me.
> tests/test-core.core.bz2 had to have its NT_FILE disabled as run-unstrip-n.sh
> otherwise FAILs:
> FAIL: 0x7f67f2aaf000+0x202000 - . - /home/jkratoch/redhat/elfutils-libregr/test-core-lib.so
> PASS: 0x7f67f2aaf000+0x202000 - . - test-core-lib.so
> As test-core-lib.so is found in link_map but it is not present on the disk
> elfutils now chooses the more reliable filename from NT_FILE (although that
> filename is also not found on the disk). Updating the expected text would be
> also sufficient.
I think it is better to just change the expected text instead of
updating the binary core file.
> libdwfl/
> 2014-09-09 Jan Kratochvil <jan.kratochvil(a)redhat.com>
>
> Support NT_FILE for locating files.
> * core-file.c (dwfl_core_file_report): New variables note_file and
> note_file_size, set them and pass them to dwfl_segment_report_module.
OK.
> * dwfl_segment_report_module.c: Include common.h and fcntl.h.
> (buf_has_data, buf_read_ulong, handle_file_note): New functions.
OK, with some small comments, see below.
> (invalid_elf): New function from code of dwfl_segment_report_module.
> (dwfl_segment_report_module): Add parameters note_file and
> note_file_size. New variables elf and fd, clean them up in finish.
> Move some code to invalid_elf. Call handle_file_note, if it found
> a name verify the file by invalid_elf. Protect elf and fd against
> cleanup by finish if we found the file for new Dwfl_Module.
> * libdwflP.h (dwfl_segment_report_module): Add parameters note_file and
> note_file_size.
OK.
> tests/
> 2014-09-09 Jan Kratochvil <jan.kratochvil(a)redhat.com>
>
> Support NT_FILE for locating files.
> * Makefile.am (TESTS): Add run-linkmap-cut.sh.
> (EXTRA_DIST): Add run-linkmap-cut.sh, linkmap-cut-lib.so.bz2,
> linkmap-cut.bz2 and linkmap-cut.core.bz2 .
> * linkmap-cut-lib.so.bz2: New file.
> * linkmap-cut.bz2: New file.
> * linkmap-cut.core.bz2: New file.
> * run-linkmap-cut.sh: New file.
OK, with one comment.
> * test-core.core.bz2: Disable its NT_FILE note.
Please just adjust the expected output.
> diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
> index 4ce63c4..50031ae 100644
> --- a/libdwfl/core-file.c
> +++ b/libdwfl/core-file.c
> @@ -451,7 +451,9 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
> /* Next, we should follow the chain from DT_DEBUG. */
>
> const void *auxv = NULL;
> + const void *note_file = NULL;
> size_t auxv_size = 0;
> + size_t note_file_size = 0;
> if (likely (notes_phdr.p_type == PT_NOTE))
> {
> /* PT_NOTE -> NT_AUXV -> AT_PHDR -> PT_DYNAMIC -> DT_DEBUG */
> @@ -468,13 +470,19 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
> size_t desc_pos;
> while ((pos = gelf_getnote (notes, pos, &nhdr,
> &name_pos, &desc_pos)) > 0)
> - if (nhdr.n_type == NT_AUXV
> - && nhdr.n_namesz == sizeof "CORE"
> + if (nhdr.n_namesz == sizeof "CORE"
> && !memcmp (notes->d_buf + name_pos, "CORE", sizeof "CORE"))
> {
> - auxv = notes->d_buf + desc_pos;
> - auxv_size = nhdr.n_descsz;
> - break;
> + if (nhdr.n_type == NT_AUXV)
> + {
> + auxv = notes->d_buf + desc_pos;
> + auxv_size = nhdr.n_descsz;
> + }
> + if (nhdr.n_type == NT_FILE)
> + {
> + note_file = notes->d_buf + desc_pos;
> + note_file_size = nhdr.n_descsz;
> + }
> }
> }
> }
> @@ -498,6 +506,7 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
> int seg = dwfl_segment_report_module (dwfl, ndx, NULL,
> &dwfl_elf_phdr_memory_callback, elf,
> core_file_read_eagerly, elf,
> + note_file, note_file_size,
> &r_debug_info);
> if (unlikely (seg < 0))
> {
> diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c
> index 3393b08..3822e32 100644
> --- a/libdwfl/dwfl_segment_report_module.c
> +++ b/libdwfl/dwfl_segment_report_module.c
> @@ -30,6 +30,7 @@
> #include "../libelf/libelfP.h" /* For NOTE_ALIGN. */
> #undef _
> #include "libdwflP.h"
> +#include "common.h"
>
> #include <elf.h>
> #include <gelf.h>
> @@ -38,6 +39,7 @@
> #include <alloca.h>
> #include <endian.h>
> #include <unistd.h>
> +#include <fcntl.h>
>
>
> /* A good size for the initial read from memory, if it's not too costly.
> @@ -79,12 +81,161 @@ addr_segndx (Dwfl *dwfl, size_t segment, GElf_Addr addr, bool next)
> return ndx;
> }
>
> +/* Return whether there is SZ bytes available at PTR till END.
> + Function comes from src/readelf.c . */
function is small enough that comment where it came from is not really
necessary.
> +static bool
> +buf_has_data (const void *ptr, const void *end, size_t sz)
> +{
> + return ptr < end && (size_t) (end - ptr) >= sz;
> +}
> +
> +/* Read SZ bytes into *RETP from *PTRP (limited by END) in format E32.
> + Function comes from src/readelf.c . */
> +static bool
> +buf_read_ulong (const Elf32_Ehdr *e32, size_t sz,
> + const void **ptrp, const void *end, uint64_t *retp)
I appreciate the old code did this, but since this is new code lets
clean it up here. Passing in a pointer to an Elf32_Ehdr is slightly
confusing. Just pass in the ei_data byte since that is all you are
using.
> +{
> + if (! buf_has_data (*ptrp, end, sz))
> + return false;
> +
> + union
> + {
> + uint64_t u64;
> + uint32_t u32;
> + } u;
> +
> + memcpy (&u, *ptrp, sz);
> + (*ptrp) += sz;
> +
> + if (retp == NULL)
> + return true;
> +
> + if (MY_ELFDATA != e32->e_ident[EI_DATA])
And just check ei_data as passed in here.
> + {
> + if (sz == 4)
> + CONVERT (u.u32);
> + else
> + CONVERT (u.u64);
> + }
> + if (sz == 4)
> + *retp = u.u32;
> + else
> + *retp = u.u64;
> + return true;
> +}
> +
> +/* Try to find matching entry for module from address MODULE_START to
> + MODULE_END in NT_FILE note located at NOTE_FILE of NOTE_FILE_SIZE
> + bytes in format E32. */
> +
> +static const char *
> +handle_file_note (GElf_Addr module_start, GElf_Addr module_end,
> + const Elf32_Ehdr *e32,
> + const void *note_file, size_t note_file_size)
Same here. Just pass in the ei_class (or ptrsize) and ei_data or a
pointer to the e_ident and use that.
> +{
> + if (note_file == NULL)
> + return NULL;
> +
> + size_t sz;
> + switch (e32->e_ident[EI_CLASS])
use passed in size or ei_class here.
> + {
> + case ELFCLASS32:
> + sz = 4;
> + break;
> + case ELFCLASS64:
> + sz = 8;
> + break;
> + default:
> + return NULL;
> + }
> +
> + const void *ptr = note_file;
> + const void *end = note_file + note_file_size;
> + uint64_t count;
> + if (! buf_read_ulong (e32, sz, &ptr, end, &count))
> + return NULL;
> + if (! buf_read_ulong (e32, sz, &ptr, end, NULL)) // page_size
> + return NULL;
Pass ei_data to buf_read_ulong here.
> + /* Where file names are stored. */
> + const char *fptr = ptr + 3 * count * sz;
> +
> + ssize_t firstix = -1;
> + ssize_t lastix = -1;
> + for (size_t mix = 0; mix < count; mix++)
> + {
> + uint64_t mstart, mend, moffset;
> + if (! buf_read_ulong (e32, sz, &ptr, fptr, &mstart)
> + || ! buf_read_ulong (e32, sz, &ptr, fptr, &mend)
> + || ! buf_read_ulong (e32, sz, &ptr, fptr, &moffset))
> + return NULL;
Likewise.
> + if (mstart == module_start && moffset == 0)
> + firstix = lastix = mix;
> + if (firstix != -1 && mstart < module_end)
> + lastix = mix;
> + if (mend >= module_end)
> + break;
> + }
> + if (firstix == -1)
> + return NULL;
> +
> + const char *retval = NULL;
> + for (ssize_t mix = 0; mix <= lastix; mix++)
> + {
> + const char *fnext = memchr (fptr, 0, (const char *) end - fptr);
> + if (fnext == NULL)
> + return NULL;
> + if (mix == firstix)
> + retval = fptr;
> + if (firstix < mix && mix <= lastix && strcmp (fptr, retval) != 0)
> + return NULL;
> + fptr = fnext + 1;
> + }
> + return retval;
> +}
OK.
> +/* Return true iff we are certain ELF cannot match BUILD_ID of
> + BUILD_ID_LEN bytes. Pass DISK_FILE_HAS_BUILD_ID as false if it is
> + certain ELF does not contain build-id (it is only a performance hit
> + to pass it always as true). */
> +
> +static bool
> +invalid_elf (Elf *elf, bool disk_file_has_build_id,
> + const void *build_id, size_t build_id_len)
> +{
> + if (! disk_file_has_build_id && build_id_len > 0)
> + {
> + /* Module found in segments with build-id is more reliable
> + than a module found via DT_DEBUG on disk without any
> + build-id. */
> + return true;
> + }
> + if (disk_file_has_build_id && build_id_len > 0)
> + {
> + const void *elf_build_id;
> + ssize_t elf_build_id_len;
> +
> + /* If there is a build id in the elf file, check it. */
> + elf_build_id_len = INTUSE(dwelf_elf_gnu_build_id) (elf, &elf_build_id);
> + if (elf_build_id_len > 0)
> + {
> + if (build_id_len != (size_t) elf_build_id_len
> + || memcmp (build_id, elf_build_id, build_id_len) != 0)
> + return true;
> + }
> + }
> + return false;
> +}
> +
> int
> dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
> Dwfl_Memory_Callback *memory_callback,
> void *memory_callback_arg,
> Dwfl_Module_Callback *read_eagerly,
> void *read_eagerly_arg,
> + const void *note_file, size_t note_file_size,
> const struct r_debug_info *r_debug_info)
> {
> size_t segment = ndx;
> @@ -121,10 +272,16 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
>
> void *buffer = NULL;
> size_t buffer_available = INITIAL_READ;
> + Elf *elf = NULL;
> + int fd = -1;
>
> inline int finish (void)
> {
> release_buffer (&buffer, &buffer_available);
> + if (elf != NULL)
> + elf_end (elf);
> + if (fd != -1)
> + close (fd);
> return ndx;
> }
>
> @@ -481,32 +638,9 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
> if ((module_end > module->start && module_start < module->end)
> || dyn_vaddr == module->l_ld)
> {
> - bool close_elf = false;
> - if (! module->disk_file_has_build_id && build_id_len > 0)
> - {
> - /* Module found in segments with build-id is more reliable
> - than a module found via DT_DEBUG on disk without any
> - build-id. */
> - if (module->elf != NULL)
> - close_elf = true;
> - }
> if (module->elf != NULL
> - && module->disk_file_has_build_id && build_id_len > 0)
> - {
> - const void *elf_build_id;
> - ssize_t elf_build_id_len;
> -
> - /* If there is a build id in the elf file, check it. */
> - elf_build_id_len = INTUSE(dwelf_elf_gnu_build_id) (module->elf,
> - &elf_build_id);
> - if (elf_build_id_len > 0)
> - {
> - if (build_id_len != (size_t) elf_build_id_len
> - || memcmp (build_id, elf_build_id, build_id_len) != 0)
> - close_elf = true;
> - }
> - }
> - if (close_elf)
> + && invalid_elf (module->elf, module->disk_file_has_build_id,
> + build_id, build_id_len))
> {
> elf_end (module->elf);
> close (module->fd);
> @@ -527,6 +661,29 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
> }
> }
>
> + const char *file_note_name = handle_file_note (module_start, module_end,
> + &ehdr.e32,
> + note_file, note_file_size);
> + if (file_note_name)
> + {
> + name = file_note_name;
> + name_is_final = true;
> + bool invalid = false;
> + fd = open64 (name, O_RDONLY);
> + if (fd >= 0)
> + {
> + Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
> + if (error == DWFL_E_NOERROR)
> + invalid = invalid_elf (elf, true /* disk_file_has_build_id */,
> + build_id, build_id_len);
> + }
> + if (invalid)
> + {
> + free (build_id);
> + return finish ();
> + }
> + }
> +
> /* Our return value now says to skip the segments contained
> within the module. */
> ndx = addr_segndx (dwfl, segment, module_end, true);
> @@ -683,10 +840,10 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
> : dynstr_vaddr + dynstrsz - start);
> const GElf_Off whole = MAX (file_trimmed_end, shdrs_end);
>
> - Elf *elf = NULL;
> - if ((*read_eagerly) (MODCB_ARGS (mod), &buffer, &buffer_available,
> - cost, worthwhile, whole, contiguous,
> - read_eagerly_arg, &elf)
> + if (elf == NULL
> + && (*read_eagerly) (MODCB_ARGS (mod), &buffer, &buffer_available,
> + cost, worthwhile, whole, contiguous,
> + read_eagerly_arg, &elf)
> && elf == NULL)
> {
> /* The caller wants to read the whole file in right now, but hasn't
> @@ -748,6 +905,8 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
> {
> /* Install the file in the module. */
> mod->main.elf = elf;
> + elf = NULL;
> + fd = -1;
> mod->main.vaddr = module_start - bias;
> mod->main.address_sync = module_address_sync;
> mod->main_bias = bias;
OK.
> +testfiles linkmap-cut-lib.so linkmap-cut linkmap-cut.core
> +tempfiles bt
> +# It may have non-zero exit code with:
> +# .../elfutils/src/stack: dwfl_thread_getframes tid 3130 at 0x3fdf821d64 in /usr/lib64/libc-2.18.so: no matching address range
> +testrun ${abs_top_builddir}/src/stack --core=linkmap-cut.core -e linkmap-cut >bt || true
> +cat bt
> +grep -qw libfunc bt
> +grep -qw main bt
Maybe you want to use -m to check it got the module name correct?
The NT_FILE notes seem to have lots of slashed in them, is that
deliberate? The comments don't say how the binaries were generated,
please add that in case someone wants to regenerate them one day.
Looks good with those small issues fixed.
Thanks,
Mark
9 years, 5 months
[PATCH] readelf: Fix typo in a check in handle_relocs_rela
by Petr Machata
- Testing shdr makes no sense, that pointer was dereferenced several
times throughout the section. destshdr on the other hand is not
tested at all.
Signed-off-by: Petr Machata <pmachata(a)redhat.com>
---
src/ChangeLog | 4 ++++
src/readelf.c | 2 +-
2 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index aa98608..a252cdc 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,7 @@
+2014-09-14 Petr Machata <pmachata(a)redhat.com>
+
+ * readelf.c (handle_relocs_rela): Typo fix, test DESTSHDR properly.
+
2014-09-12 Petr Machata <pmachata(a)redhat.com>
* readelf.c (encoded_ptr_size): In the switch statement, change
diff --git a/src/readelf.c b/src/readelf.c
index 7b43a65..4d3bb36 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -2090,7 +2090,7 @@ handle_relocs_rela (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, GElf_Shdr *shdr)
? xndx : sym->st_shndx),
&destshdr_mem);
- if (unlikely (shdr == NULL))
+ if (unlikely (destshdr == NULL))
printf (" %#0*" PRIx64 " %-15s <%s %ld>\n",
class == ELFCLASS32 ? 10 : 18, rel->r_offset,
ebl_reloc_type_check (ebl, GELF_R_TYPE (rel->r_info))
--
1.7.6.5
9 years, 5 months
elfutils arm support
by Kurt Roeckx
Hi,
We currently use the attached patch in Debian. This is based on a
patch from Eugeniy Meshcheryakov in 2006 that has been modified by
me over the years.
Kurt
9 years, 6 months
[PATCH] Rename read_ubyte_unaligned_inc to read_addr_unaligned_inc
by Petr Machata
- The behavior of this function has been changed to read only
address-sized quantities, i.e. the size of 2 is no longer allowed.
(Size of 1 never was.) The two outstanding uses, both in readelf.c,
which actually do use this call to read address-sized units, were
updated, rejecting fields that indicate weird address sizes.
- The three related calls, read_sbyte_unaligned_inc,
read_ubyte_unaligned and read_sbyte_unaligned were dropped. There
are currently no uses for these calls.
Signed-off-by: Petr Machata <pmachata(a)redhat.com>
---
libdw/ChangeLog | 8 ++++++++
libdw/memory-access.h | 24 ++++--------------------
src/ChangeLog | 6 ++++++
src/readelf.c | 16 +++++++++-------
4 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index cc44004..410b31a 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,11 @@
+2014-09-12 Petr Machata <pmachata(a)redhat.com>
+
+ * memory-access.h (read_ubyte_unaligned_inc): Allow only 4- and
+ 8-byte quantities. Consequently, rename to...
+ (read_addr_unaligned_inc): ... this.
+ (read_sbyte_unaligned_inc, read_ubyte_unaligned): Drop.
+ (read_sbyte_unaligned): Drop.
+
2014-09-10 Petr Machata <pmachata(a)redhat.com>
* dwarf_getlocation.c (attr_ok): Also accept
diff --git a/libdw/memory-access.h b/libdw/memory-access.h
index f41f783..6a4ce88 100644
--- a/libdw/memory-access.h
+++ b/libdw/memory-access.h
@@ -219,17 +219,6 @@ read_8sbyte_unaligned_1 (bool other_byte_order, const void *p)
#endif /* allow unaligned */
-#define read_ubyte_unaligned(Nbytes, Dbg, Addr) \
- ((Nbytes) == 2 ? read_2ubyte_unaligned (Dbg, Addr) \
- : (Nbytes) == 4 ? read_4ubyte_unaligned (Dbg, Addr) \
- : read_8ubyte_unaligned (Dbg, Addr))
-
-#define read_sbyte_unaligned(Nbytes, Dbg, Addr) \
- ((Nbytes) == 2 ? read_2sbyte_unaligned (Dbg, Addr) \
- : (Nbytes) == 4 ? read_4sbyte_unaligned (Dbg, Addr) \
- : read_8sbyte_unaligned (Dbg, Addr))
-
-
#define read_2ubyte_unaligned_inc(Dbg, Addr) \
({ uint16_t t_ = read_2ubyte_unaligned (Dbg, Addr); \
Addr = (__typeof (Addr)) (((uintptr_t) (Addr)) + 2); \
@@ -258,14 +247,9 @@ read_8sbyte_unaligned_1 (bool other_byte_order, const void *p)
t_; })
-#define read_ubyte_unaligned_inc(Nbytes, Dbg, Addr) \
- ((Nbytes) == 2 ? read_2ubyte_unaligned_inc (Dbg, Addr) \
- : (Nbytes) == 4 ? read_4ubyte_unaligned_inc (Dbg, Addr) \
- : read_8ubyte_unaligned_inc (Dbg, Addr))
-
-#define read_sbyte_unaligned_inc(Nbytes, Dbg, Addr) \
- ((Nbytes) == 2 ? read_2sbyte_unaligned_inc (Dbg, Addr) \
- : (Nbytes) == 4 ? read_4sbyte_unaligned_inc (Dbg, Addr) \
- : read_8sbyte_unaligned_inc (Dbg, Addr))
+#define read_addr_unaligned_inc(Nbytes, Dbg, Addr) \
+ (assert ((Nbytes) == 4 || (Nbytes) == 8), \
+ ((Nbytes) == 4 ? read_4ubyte_unaligned_inc (Dbg, Addr) \
+ : read_8ubyte_unaligned_inc (Dbg, Addr)))
#endif /* memory-access.h */
diff --git a/src/ChangeLog b/src/ChangeLog
index 986b341..aa98608 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,9 @@
+2014-09-12 Petr Machata <pmachata(a)redhat.com>
+
+ * readelf.c (encoded_ptr_size): In the switch statement, change
+ magic constants 3 and 4 to DW_EH_PE_* counterparts. Still accept
+ 0. Print diagnostic for anything else.
+
2014-08-25 Josh Stone <jistone(a)redhat.com>
* Makefile.am: Prevent premature @AR@ replacement in a sed expression.
diff --git a/src/readelf.c b/src/readelf.c
index 6bbe436..7b43a65 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -5026,15 +5026,17 @@ encoded_ptr_size (int encoding, unsigned int ptr_size)
{
switch (encoding & 7)
{
- case 2:
- return 2;
- case 3:
+ case DW_EH_PE_udata4:
return 4;
- case 4:
+ case DW_EH_PE_udata8:
return 8;
- default:
+ case 0:
return ptr_size;
}
+
+ fprintf (stderr, "Unsupported pointer encoding: %#x, "
+ "assuming pointer size of %d.\n", encoding, ptr_size);
+ return ptr_size;
}
@@ -5455,9 +5457,9 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
const unsigned char *base = readp;
// XXX There are sometimes relocations for this value
- initial_location = read_ubyte_unaligned_inc (ptr_size, dbg, readp);
+ initial_location = read_addr_unaligned_inc (ptr_size, dbg, readp);
Dwarf_Word address_range
- = read_ubyte_unaligned_inc (ptr_size, dbg, readp);
+ = read_addr_unaligned_inc (ptr_size, dbg, readp);
/* pcrel for an FDE address is relative to the runtime
address of the start_address field itself. Sign extend
--
1.7.6.5
9 years, 6 months
[PATCH] Support 1-sized reads in read_ubyte_unaligned_inc and read_sbyte_unaligned_inc
by Petr Machata
I propose this change mostly for consistency. Having one entry point
for reading all (reasonable) widths is very convenient, and I make use
of this interface in my upcoming patch for .debug_macro support.
---8<-----------------------------------------------------------------
Signed-off-by: Petr Machata <pmachata(a)redhat.com>
---
libdw/ChangeLog | 5 +++++
libdw/memory-access.h | 10 ++++++----
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 5f9b097..b8c5acd 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,8 @@
+2014-09-10 Petr Machata <pmachata(a)redhat.com>
+
+ * memory-access.h (read_ubyte_unaligned_inc)
+ (read_sbyte_unaligned_inc): Read 1-sized fields as well.
+
2014-08-15 Mark Wielaard <mjw(a)redhat.com>
* dwarf_cu_die.c: New file.
diff --git a/libdw/memory-access.h b/libdw/memory-access.h
index f41f783..caa92f6 100644
--- a/libdw/memory-access.h
+++ b/libdw/memory-access.h
@@ -259,13 +259,15 @@ read_8sbyte_unaligned_1 (bool other_byte_order, const void *p)
#define read_ubyte_unaligned_inc(Nbytes, Dbg, Addr) \
- ((Nbytes) == 2 ? read_2ubyte_unaligned_inc (Dbg, Addr) \
- : (Nbytes) == 4 ? read_4ubyte_unaligned_inc (Dbg, Addr) \
+ ((Nbytes) == 1 ? (uint8_t) *Addr++ \
+ : (Nbytes) == 2 ? read_2ubyte_unaligned_inc (Dbg, Addr) \
+ : (Nbytes) == 4 ? read_4ubyte_unaligned_inc (Dbg, Addr) \
: read_8ubyte_unaligned_inc (Dbg, Addr))
#define read_sbyte_unaligned_inc(Nbytes, Dbg, Addr) \
- ((Nbytes) == 2 ? read_2sbyte_unaligned_inc (Dbg, Addr) \
- : (Nbytes) == 4 ? read_4sbyte_unaligned_inc (Dbg, Addr) \
+ ((Nbytes) == 1 ? (int8_t) *Addr++ \
+ : (Nbytes) == 2 ? read_2sbyte_unaligned_inc (Dbg, Addr) \
+ : (Nbytes) == 4 ? read_4sbyte_unaligned_inc (Dbg, Addr) \
: read_8sbyte_unaligned_inc (Dbg, Addr))
#endif /* memory-access.h */
--
1.7.6.5
9 years, 6 months
[PATCH] elf_begin sets elf_errno for cases that the file is completely unreadable
by Petr Machata
- The problem this is trying to solve can be seen for example thus:
$ eu-readelf -a /
eu-readelf: failed reading '/': (null)
With the fix we see a proper error number and get a reasonable error
message:
$ eu-readelf -a /
eu-readelf: failed reading '/': invalid file descriptor
Signed-off-by: Petr Machata <pmachata(a)redhat.com>
---
libelf/ChangeLog | 5 +++++
libelf/elf_begin.c | 9 ++++++---
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/libelf/ChangeLog b/libelf/ChangeLog
index 4bc8f56..5ad20a6 100644
--- a/libelf/ChangeLog
+++ b/libelf/ChangeLog
@@ -1,3 +1,8 @@
+2014-09-10 Petr Machata <pmachata(a)redhat.com>
+
+ * elf_begin (read_unmmaped_file): Call __libelf_seterrno if the
+ file is unreadable.
+
2014-07-07 Mark Wielaard <mjw(a)redhat.com>
* elf.h: Update from glibc.
diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c
index 1ef70ac..3ee195d 100644
--- a/libelf/elf_begin.c
+++ b/libelf/elf_begin.c
@@ -550,9 +550,12 @@ read_unmmaped_file (int fildes, off_t offset, size_t maxsize, Elf_Cmd cmd,
maxsize),
offset);
if (unlikely (nread == -1))
- /* We cannot even read the head of the file. Maybe FILDES is associated
- with an unseekable device. This is nothing we can handle. */
- return NULL;
+ {
+ __libelf_seterrno (ELF_E_INVALID_FILE);
+ /* We cannot even read the head of the file. Maybe FILDES is associated
+ with an unseekable device. This is nothing we can handle. */
+ return NULL;
+ }
/* See what kind of object we have here. */
Elf_Kind kind = determine_kind (mem.header, nread);
--
1.7.6.5
9 years, 6 months
[PATCH 1/2] Support .debug_macro
by Petr Machata
- This code is based on the following proposal:
http://www.dwarfstd.org/ShowIssue.php?issue=110722.1
- dwarf_getmacros is left to support only .debug_macinfo. The only
problematic opcode really is DW_MACINFO_vendor_ext, that the new
standard lacks, and that could in theory be misinterpreted by extant
code as whatever operand a vendor decides to map to 0xff.
- Instead, two principial iteration interfaces are provided for access
to new-style sections: dwarf_getmacros_die and dwarf_getmacros_off.
And a suite of new helper interfaces for analysis of individual
opcodes: dwarf_macro_version, dwarf_macro_line_offset,
dwarf_macro_getparamcnt, dwarf_macro_param.
- The existing interfaces dwarf_macro_opcode, dwarf_macro_param1 and
dwarf_macro_param2 remain operational for old- as well as new-style
Dwarf macro sections, if applicable.
Signed-off-by: Petr Machata <pmachata(a)redhat.com>
---
libdw/ChangeLog | 32 +++
libdw/Makefile.am | 8 +-
libdw/dwarf_end.c | 1 +
libdw/dwarf_error.c | 3 +-
libdw/dwarf_getmacros.c | 514 +++++++++++++++++++++++++++++++++------
libdw/dwarf_macro_getparamcnt.c | 43 ++++
libdw/dwarf_macro_line_offset.c | 45 ++++
libdw/dwarf_macro_param.c | 46 ++++
libdw/dwarf_macro_param1.c | 8 +-
libdw/dwarf_macro_param2.c | 18 +-
libdw/dwarf_macro_version.c | 44 ++++
libdw/libdw.h | 87 ++++++-
libdw/libdw.map | 11 +-
libdw/libdwP.h | 54 ++++-
14 files changed, 803 insertions(+), 111 deletions(-)
create mode 100644 libdw/dwarf_macro_getparamcnt.c
create mode 100644 libdw/dwarf_macro_line_offset.c
create mode 100644 libdw/dwarf_macro_param.c
create mode 100644 libdw/dwarf_macro_version.c
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index b8c5acd..5e0b577 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,5 +1,37 @@
2014-09-10 Petr Machata <pmachata(a)redhat.com>
+ * dwarf_macro_getparamcnt.c: New file.
+ * dwarf_macro_line_offset.c: New file.
+ * dwarf_macro_param.c: New file.
+ * dwarf_macro_version.c: New file.
+ * Makefile.am (libdw_a_SOURCES): Add the new files.
+ * libdwP.h (DWARF_E_INVALID_OPCODE): New enumerator.
+ (struct Dwarf): New field macro_ops.
+ (Dwarf_Macro_Op_Proto, Dwarf_Macro_Op_Table): New structures for
+ keeping macro opcode prototypes in.
+ (Dwarf_Macro_s): Drop all fields, add new ones.
+ * dwarf_error.c (errmsgs): Add a message for
+ DWARF_E_INVALID_OPCODE.
+ * dwarf_end.c (dwarf_end): Destroy it here.
+ * libdw.h (dwarf_getmacros_die, dwarf_getmacros_off)
+ (dwarf_macro_version, dwarf_macro_line_offset)
+ (dwarf_macro_getparamcnt)
+ (dwarf_macro_param): New public interfaces.
+ * dwarf_getmacros.c: Implement them here.
+ (get_offset_from, demand, ck_read_ubyte_unaligned_inc)
+ (macro_op_compare, build_table, init_macinfo_table)
+ (get_macinfo_table, get_table_for_offset, read_macros)
+ (gnu_macros_getmacros_off, macro_info_getmacros_off): Helpers.
+ (dwarf_getmacros): Adjust to dispatch to the new interfaces.
+ * dwarf_macro_param1.c (dwarf_macro_param1): Adjust to dispatch to
+ the new interfaces.
+ * dwarf_macro_param2.c (dwarf_macro_param2): Likewise.
+ * libdw.map (ELFUTILS_0.161): New. Add dwarf_getmacros_die,
+ dwarf_getmacros_off, dwarf_macro_version, dwarf_macro_getparamcnt,
+ dwarf_macro_param.
+
+2014-09-10 Petr Machata <pmachata(a)redhat.com>
+
* memory-access.h (read_ubyte_unaligned_inc)
(read_sbyte_unaligned_inc): Read 1-sized fields as well.
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 2e42a37..51b6a0c 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -71,9 +71,11 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \
dwarf_getlocation.c dwarf_getstring.c dwarf_offabbrev.c \
dwarf_getaranges.c dwarf_onearange.c dwarf_getarangeinfo.c \
dwarf_getarange_addr.c dwarf_getattrs.c dwarf_formflag.c \
- dwarf_getmacros.c dwarf_macro_opcode.c dwarf_macro_param1.c \
- dwarf_macro_param2.c dwarf_addrdie.c \
- dwarf_getfuncs.c \
+ dwarf_getmacros.c dwarf_macro_getparamcnt.c \
+ dwarf_macro_line_offset.c dwarf_macro_opcode.c \
+ dwarf_macro_param.c dwarf_macro_param1.c \
+ dwarf_macro_param2.c dwarf_macro_version.c \
+ dwarf_addrdie.c dwarf_getfuncs.c \
dwarf_decl_file.c dwarf_decl_line.c dwarf_decl_column.c \
dwarf_func_inline.c dwarf_getsrc_file.c \
libdw_findcu.c libdw_form.c libdw_alloc.c \
diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
index 241a257..26c38cb 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -92,6 +92,7 @@ dwarf_end (dwarf)
to be handled. */
tdestroy (dwarf->cu_tree, cu_free);
tdestroy (dwarf->tu_tree, cu_free);
+ tdestroy (dwarf->macro_ops, noop_free);
struct libdw_memblock *memp = dwarf->mem_tail;
/* The first block is allocated together with the Dwarf object. */
diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c
index 2292914..08b691a 100644
--- a/libdw/dwarf_error.c
+++ b/libdw/dwarf_error.c
@@ -1,5 +1,5 @@
/* Retrieve ELF descriptor used for DWARF access.
- Copyright (C) 2002, 2003, 2004, 2005, 2009 Red Hat, Inc.
+ Copyright (C) 2002, 2003, 2004, 2005, 2009, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2002.
@@ -92,6 +92,7 @@ static const char *errmsgs[] =
[DWARF_E_NO_DEBUG_RANGES] = N_(".debug_ranges section missing"),
[DWARF_E_INVALID_CFI] = N_("invalid CFI section"),
[DWARF_E_NO_ALT_DEBUGLINK] = N_("no alternative debug link found"),
+ [DWARF_E_INVALID_OPCODE] = N_("invalid opcode"),
};
#define nerrmsgs (sizeof (errmsgs) / sizeof (errmsgs[0]))
diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c
index a3d2c81..be13409 100644
--- a/libdw/dwarf_getmacros.c
+++ b/libdw/dwarf_getmacros.c
@@ -1,5 +1,5 @@
/* Get macro information.
- Copyright (C) 2002-2009 Red Hat, Inc.
+ Copyright (C) 2002-2009, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2002.
@@ -33,112 +33,464 @@
#include <dwarf.h>
#include <string.h>
+#include <search.h>
#include <libdwP.h>
+#include <stdio.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <stdlib.h>
-ptrdiff_t
-dwarf_getmacros (die, callback, arg, offset)
- Dwarf_Die *die;
- int (*callback) (Dwarf_Macro *, void *);
- void *arg;
- ptrdiff_t offset;
+static int
+get_offset_from (Dwarf_Die *die, int name, Dwarf_Word *retp)
{
- if (die == NULL)
+ /* Get the appropriate attribute. */
+ Dwarf_Attribute attr;
+ if (INTUSE(dwarf_attr) (die, name, &attr) == NULL)
+ return -1;
+
+ /* Offset into the corresponding section. */
+ return INTUSE(dwarf_formudata) (&attr, retp);
+}
+
+static bool
+demand (size_t nbytes, const unsigned char *readp, const unsigned char *endp)
+{
+ bool ret = readp < endp && (size_t) (endp - readp) >= nbytes;
+ if (! ret)
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return ret;
+}
+
+#define ck_read_ubyte_unaligned_inc(Nbytes, Dbg, Addr, AddrEnd, Ret) \
+ (demand ((Nbytes), (Addr), (AddrEnd)) \
+ ? ((Ret = read_ubyte_unaligned_inc ((Nbytes), (Dbg), (Addr))), true) \
+ : false)
+
+static int
+macro_op_compare (const void *p1, const void *p2)
+{
+ const Dwarf_Macro_Op_Table *t1 = (const Dwarf_Macro_Op_Table *) p1;
+ const Dwarf_Macro_Op_Table *t2 = (const Dwarf_Macro_Op_Table *) p2;
+
+ if (t1->offset < t2->offset)
return -1;
+ if (t1->offset > t2->offset + t2->read)
+ return 1;
+
+ return 0;
+}
+
+static void
+build_table (Dwarf_Macro_Op_Table *table,
+ Dwarf_Macro_Op_Proto op_protos[static 255])
+{
+ unsigned ct = 0;
+ for (unsigned i = 1; i < 256; ++i)
+ if (op_protos[i - 1].forms != NULL)
+ table->table[table->opcodes[i - 1] = ct++] = op_protos[i - 1];
+ else
+ table->opcodes[i] = 0xff;
+}
+
+#define MACRO_PROTO(NAME, ...) \
+ Dwarf_Macro_Op_Proto NAME = ({ \
+ static const uint8_t proto[] = {__VA_ARGS__}; \
+ (Dwarf_Macro_Op_Proto) {sizeof proto, proto}; \
+ })
+
+static Dwarf_Macro_Op_Table *
+init_macinfo_table (void)
+{
+ MACRO_PROTO (p_udata_str, DW_FORM_udata, DW_FORM_string);
+ MACRO_PROTO (p_udata_udata, DW_FORM_udata, DW_FORM_udata);
+ MACRO_PROTO (p_none);
+
+ Dwarf_Macro_Op_Proto op_protos[255] =
+ {
+ [DW_MACINFO_define - 1] = p_udata_str,
+ [DW_MACINFO_undef - 1] = p_udata_str,
+ [DW_MACINFO_vendor_ext - 1] = p_udata_str,
+ [DW_MACINFO_start_file - 1] = p_udata_udata,
+ [DW_MACINFO_end_file - 1] = p_none,
+ };
+
+ static Dwarf_Macro_Op_Table table;
+ memset (&table, 0, sizeof table);
+
+ build_table (&table, op_protos);
+ return &table;
+}
+
+static inline Dwarf_Macro_Op_Table *
+get_macinfo_table (void)
+{
+ static Dwarf_Macro_Op_Table *ret = NULL;
+ if (unlikely (ret == NULL))
+ ret = init_macinfo_table ();
+ return ret;
+}
+
+static Dwarf_Macro_Op_Table *
+get_table_for_offset (Dwarf *dbg, Dwarf_Word macoff,
+ const unsigned char *readp,
+ const unsigned char *const endp)
+{
+ Dwarf_Macro_Op_Table fake = { .offset = macoff };
+ Dwarf_Macro_Op_Table **found = tfind (&fake, &dbg->macro_ops,
+ macro_op_compare);
+ if (found != NULL)
+ return *found;
- Elf_Data *d = die->cu->dbg->sectiondata[IDX_debug_macinfo];
- if (unlikely (d == NULL) || unlikely (d->d_buf == NULL))
+ const unsigned char *startp = readp;
+ uint16_t version;
+ if (! ck_read_ubyte_unaligned_inc (2, dbg, readp, endp, version))
+ return NULL;
+ if (version != 4)
+ {
+ __libdw_seterrno (DWARF_E_INVALID_VERSION);
+ return NULL;
+ }
+
+ uint8_t flags;
+ if (! ck_read_ubyte_unaligned_inc (1, dbg, readp, endp, flags))
+ return NULL;
+
+ bool is_64bit = (flags & 0x1) != 0;
+
+ Dwarf_Off line_offset = (Dwarf_Off) -1;
+ if ((flags & 0x2) != 0
+ && ! ck_read_ubyte_unaligned_inc (is_64bit ? 8 : 4, dbg,
+ readp, endp, line_offset))
+ return NULL;
+
+ /* """The macinfo entry types defined in this standard may, but
+ might not, be described in the table""".
+
+ I.e. these may be present. It's tempting to simply skip them,
+ but it's probably more correct to tolerate that a producer tweaks
+ the way certain opcodes are encoded, for whatever reasons. */
+
+ MACRO_PROTO (p_udata_str, DW_FORM_udata, DW_FORM_string);
+ MACRO_PROTO (p_udata_strp, DW_FORM_udata, DW_FORM_strp);
+ MACRO_PROTO (p_udata_udata, DW_FORM_udata, DW_FORM_udata);
+ MACRO_PROTO (p_secoffset, DW_FORM_sec_offset);
+ MACRO_PROTO (p_none);
+
+ Dwarf_Macro_Op_Proto op_protos[255] =
+ {
+ [DW_MACRO_GNU_define - 1] = p_udata_str,
+ [DW_MACRO_GNU_undef - 1] = p_udata_str,
+ [DW_MACRO_GNU_define_indirect - 1] = p_udata_strp,
+ [DW_MACRO_GNU_undef_indirect - 1] = p_udata_strp,
+ [DW_MACRO_GNU_start_file - 1] = p_udata_udata,
+ [DW_MACRO_GNU_end_file - 1] = p_none,
+ [DW_MACRO_GNU_transparent_include - 1] = p_secoffset,
+ /* N.B. DW_MACRO_undef_indirectx, DW_MACRO_define_indirectx
+ should be added when 130313.1 is supported. */
+ };
+
+ if ((flags & 0x4) != 0)
+ {
+ unsigned count;
+ if (! ck_read_ubyte_unaligned_inc (1, dbg, readp, endp, count))
+ return NULL;
+ for (unsigned i = 0; i < count; ++i)
+ {
+ unsigned opcode;
+ if (! ck_read_ubyte_unaligned_inc (1, dbg, readp, endp, opcode))
+ return NULL;
+
+ Dwarf_Macro_Op_Proto e;
+ get_uleb128 (e.nforms, readp); // XXX checking
+ e.forms = readp;
+ op_protos[opcode] = e;
+
+ demand (e.nforms, readp, endp);
+ readp += e.nforms;
+ }
+ }
+
+ size_t ct = 0;
+ for (unsigned i = 1; i < 256; ++i)
+ if (op_protos[i - 1].forms != NULL)
+ ++ct;
+
+ /* We support at most 0xfe opcodes defined in the table, as 0xff is
+ a value that means that given opcode is not stored at all. But
+ that should be fine, as opcode 0 is not allocated. */
+ assert (ct < 0xff);
+
+ size_t macop_table_size = offsetof (Dwarf_Macro_Op_Table, table[ct]);
+
+ Dwarf_Macro_Op_Table *table = libdw_alloc (dbg, Dwarf_Macro_Op_Table,
+ macop_table_size, 1);
+
+ *table = (Dwarf_Macro_Op_Table) {
+ .offset = macoff,
+ .line_offset = line_offset,
+ .header_len = readp - startp,
+ .version = version,
+ .is_64bit = is_64bit,
+ };
+ build_table (table, op_protos);
+
+ Dwarf_Macro_Op_Table **ret = tsearch (table, &dbg->macro_ops,
+ macro_op_compare);
+ if (unlikely (ret == NULL))
+ {
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return NULL;
+ }
+
+ return *ret;
+}
+
+static ptrdiff_t
+read_macros (Dwarf *dbg, Dwarf_Macro_Op_Table *table, int secindex,
+ Dwarf_Off macoff, int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t offset)
+{
+ Elf_Data *d = dbg->sectiondata[secindex];
+ if (unlikely (d == NULL || d->d_buf == NULL))
{
__libdw_seterrno (DWARF_E_NO_ENTRY);
return -1;
}
- if (offset == 0)
+ if (unlikely (macoff >= d->d_size))
{
- /* Get the appropriate attribute. */
- Dwarf_Attribute attr;
- if (INTUSE(dwarf_attr) (die, DW_AT_macro_info, &attr) == NULL)
- return -1;
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
- /* Offset into the .debug_macinfo section. */
- Dwarf_Word macoff;
- if (INTUSE(dwarf_formudata) (&attr, &macoff) != 0)
- return -1;
+ const unsigned char *const startp = d->d_buf + macoff;
+ const unsigned char *const endp = d->d_buf + d->d_size;
- offset = macoff;
+ if (table == NULL)
+ {
+ table = get_table_for_offset (dbg, macoff, startp, endp);
+ if (table == NULL)
+ return -1;
}
- if (unlikely (offset > (ptrdiff_t) d->d_size))
- goto invalid;
- const unsigned char *readp = d->d_buf + offset;
- const unsigned char *readendp = d->d_buf + d->d_size;
+ if (offset == 0)
+ offset = table->header_len;
- if (readp == readendp)
- return 0;
+ assert (offset >= 0);
+ assert (offset < endp - startp);
+ const unsigned char *readp = startp + offset;
- while (readp < readendp)
+ while (readp < endp)
{
unsigned int opcode = *readp++;
- unsigned int u128;
- unsigned int u128_2 = 0;
- const char *str = NULL;
- const unsigned char *endp;
+ if (opcode == 0)
+ /* Nothing more to do. */
+ return 0;
+
+ unsigned int idx = table->opcodes[opcode - 1];
+ if (idx == 0xff)
+ {
+ __libdw_seterrno (DWARF_E_INVALID_OPCODE);
+ return -1;
+ }
+
+ Dwarf_Macro_Op_Proto *proto = &table->table[idx];
+
+ /* A fake CU with bare minimum data to fool dwarf_formX into
+ doing the right thing with the attributes that we put
+ out. */
+ Dwarf_CU fake_cu = {
+ .dbg = dbg,
+ .version = 4,
+ .offset_size = table->is_64bit ? 8 : 4,
+ };
- switch (opcode)
+#define SKIP(N) \
+ do \
+ { \
+ __typeof ((N)) n = (N); \
+ if (! demand (n, readp, endp)) \
+ return -1; \
+ readp += n; \
+ } \
+ while (false)
+
+ Dwarf_Attribute attributes[proto->nforms];
+ for (Dwarf_Word i = 0; i < proto->nforms; ++i)
{
- case DW_MACINFO_define:
- case DW_MACINFO_undef:
- case DW_MACINFO_vendor_ext:
- /* For the first two opcodes the parameters are
- line, string
- For the latter
- number, string.
- We can treat these cases together. */
- get_uleb128 (u128, readp);
-
- endp = memchr (readp, '\0', readendp - readp);
- if (endp == NULL)
- goto invalid;
-
- str = (char *) readp;
- readp = endp + 1;
- break;
-
- case DW_MACINFO_start_file:
- /* The two parameters are line and file index. */
- get_uleb128 (u128, readp);
- get_uleb128 (u128_2, readp);
- break;
-
- case DW_MACINFO_end_file:
- /* No parameters for this one. */
- u128 = 0;
- break;
-
- case 0:
- /* Nothing more to do. */
- return 0;
-
- default:
- goto invalid;
+ /* We pretend this is a DW_AT_GNU_macros attribute so that
+ DW_FORM_sec_offset forms get correctly interpreted as
+ offset into .debug_macro. */
+ attributes[i].code = DW_AT_GNU_macros;
+ attributes[i].form = proto->forms[i];
+ attributes[i].valp = (void *) readp;
+ attributes[i].cu = &fake_cu;
+
+ readp += __libdw_form_val_len (dbg, &fake_cu,
+ proto->forms[i], readp);
}
- Dwarf_Macro mac;
- mac.opcode = opcode;
- mac.param1 = u128;
- if (str == NULL)
- mac.param2.u = u128_2;
- else
- mac.param2.s = str;
+ Dwarf_Macro macro = {
+ .line_offset = table->line_offset,
+ .version = table->version,
+ .opcode = opcode,
+ .nargs = proto->nforms,
+ .attributes = attributes,
+ };
+
+ Dwarf_Off nread = readp - startp;
+ if (nread > table->read)
+ table->read = nread;
+
+ if (callback (¯o, arg) != DWARF_CB_OK)
+ return readp - startp;
+ }
+
+ return 0;
+}
+
+static ptrdiff_t
+gnu_macros_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+{
+ assert (token <= 0);
- if (callback (&mac, arg) != DWARF_CB_OK)
- return readp - (const unsigned char *) d->d_buf;
+ ptrdiff_t ret = read_macros (dbg, NULL, IDX_debug_macro,
+ macoff, callback, arg, -token);
+ if (ret == -1)
+ return -1;
+ else
+ return -ret;
+}
+
+static ptrdiff_t
+macro_info_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+{
+ assert (token >= 0);
+
+ Dwarf_Macro_Op_Table *table = get_macinfo_table ();
+ assert (table != NULL);
+
+ return read_macros (dbg, table, IDX_debug_macinfo,
+ macoff, callback, arg, token);
+}
+
+ptrdiff_t
+dwarf_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+{
+ if (dbg == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NO_DWARF);
+ return -1;
+ }
+
+ /* We use token values > 0 for iteration through .debug_macinfo and
+ values < 0 for iteration through .debug_macro. Return value of
+ -1 also signifies an error, but that's fine, because .debug_macro
+ always contains at least three bytes of headers and after
+ iterating one opcode, we should never see anything above -4. */
+
+ if (token > 0)
+ /* A continuation call from DW_AT_macro_info iteration. */
+ return macro_info_getmacros_off (dbg, macoff, callback, arg, token);
+
+ /* Either a DW_AT_GNU_macros continuation, or a fresh start
+ thereof. */
+ return gnu_macros_getmacros_off (dbg, macoff, callback, arg, token);
+}
+
+ptrdiff_t
+dwarf_getmacros_die (Dwarf_Die *cudie, Dwarf_Off *macoffp,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+{
+ if (cudie == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NO_DWARF);
+ return -1;
+ }
+
+ if (token > 0 && macoffp != NULL)
+ /* A continuation call from DW_AT_macro_info iteration, meaning
+ *MACOFF contains previously-cached offset. */
+ return macro_info_getmacros_off (cudie->cu->dbg, *macoffp,
+ callback, arg, token);
+
+ /* A fresh start of DW_AT_macro_info iteration, or a continuation
+ thereof without a cache. */
+ if (token > 0
+ || (token == 0 && dwarf_hasattr (cudie, DW_AT_macro_info)))
+ {
+ Dwarf_Word macoff;
+ if (macoffp == NULL)
+ macoffp = &macoff;
+ if (get_offset_from (cudie, DW_AT_macro_info, macoffp) != 0)
+ return -1;
+ return macro_info_getmacros_off (cudie->cu->dbg, *macoffp,
+ callback, arg, token);
+ }
+
+ if (token < 0 && macoffp != NULL)
+ /* A continuation call from DW_AT_GNU_macros iteration. */
+ return gnu_macros_getmacros_off (cudie->cu->dbg, *macoffp,
+ callback, arg, token);
+
+ /* Likewise without cache, or iteration start. */
+ Dwarf_Word macoff;
+ if (macoffp == NULL)
+ macoffp = &macoff;
+ if (get_offset_from (cudie, DW_AT_GNU_macros, macoffp) != 0)
+ return -1;
+ return gnu_macros_getmacros_off (cudie->cu->dbg, *macoffp,
+ callback, arg, token);
+}
+
+ptrdiff_t
+dwarf_getmacros (die, callback, arg, offset)
+ Dwarf_Die *die;
+ int (*callback) (Dwarf_Macro *, void *);
+ void *arg;
+ ptrdiff_t offset;
+{
+ if (die == NULL)
+ return -1;
+
+ if (offset == 0)
+ {
+ /* We can't support .debug_macro transparently by
+ dwarf_getmacros, because extant callers would think that the
+ returned macro opcodes come from DW_MACINFO_* domain and be
+ confused. */
+ if (unlikely (! dwarf_hasattr (die, DW_AT_macro_info)))
+ {
+ __libdw_seterrno (DWARF_E_NO_ENTRY);
+ return -1;
+ }
+
+ /* DIE's with both DW_AT_GNU_macros and DW_AT_macro_info are
+ disallowed by the proposal that DW_AT_GNU_macros support is
+ based on, and this attribute would derail us above, so check
+ for it now. */
+ if (unlikely (dwarf_hasattr (die, DW_AT_GNU_macros)))
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return -1;
+ }
}
+ else
+ /* If non-zero, this ought to be a continuation from previous
+ DW_AT_macro_info iteration, meaning offset can't be
+ negative. */
+ assert (offset > 0);
- /* If we come here the termination of the data for the CU is not
- present. */
- invalid:
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return -1;
+ /* At this point we can safely piggy-back on existing new-style
+ interfaces. */
+ return dwarf_getmacros_die (die, NULL, callback, arg, offset);
}
diff --git a/libdw/dwarf_macro_getparamcnt.c b/libdw/dwarf_macro_getparamcnt.c
new file mode 100644
index 0000000..294134d
--- /dev/null
+++ b/libdw/dwarf_macro_getparamcnt.c
@@ -0,0 +1,43 @@
+/* Return number of parameters of a macro.
+ Copyright (C) 2014 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 "libdwP.h"
+
+int
+dwarf_macro_getparamcnt (Dwarf_Macro *macro, size_t *paramcntp)
+{
+ if (macro == NULL)
+ return -1;
+
+ *paramcntp = macro->nargs;
+ return 0;
+}
diff --git a/libdw/dwarf_macro_line_offset.c b/libdw/dwarf_macro_line_offset.c
new file mode 100644
index 0000000..531085c
--- /dev/null
+++ b/libdw/dwarf_macro_line_offset.c
@@ -0,0 +1,45 @@
+/* Return .debug_line offset associated with given macro.
+ Copyright (C) 2014 Red Hat, Inc.
+ This file is part of elfutils.
+ Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
+
+ 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 "libdwP.h"
+
+
+int
+dwarf_macro_line_offset (Dwarf_Macro *macro, Dwarf_Off *offp)
+{
+ if (macro == NULL)
+ return -1;
+
+ *offp = macro->line_offset;
+ return 0;
+}
diff --git a/libdw/dwarf_macro_param.c b/libdw/dwarf_macro_param.c
new file mode 100644
index 0000000..b66b8ac
--- /dev/null
+++ b/libdw/dwarf_macro_param.c
@@ -0,0 +1,46 @@
+/* Return a given parameter of a macro.
+ Copyright (C) 2014 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 "libdwP.h"
+
+int
+dwarf_macro_param (Dwarf_Macro *macro, size_t idx, Dwarf_Attribute *ret)
+{
+ if (macro == NULL)
+ return -1;
+
+ if (idx >= macro->nargs)
+ return -1;
+
+ *ret = macro->attributes[idx];
+ return 0;
+}
diff --git a/libdw/dwarf_macro_param1.c b/libdw/dwarf_macro_param1.c
index 35d4a71..337ad6f 100644
--- a/libdw/dwarf_macro_param1.c
+++ b/libdw/dwarf_macro_param1.c
@@ -1,5 +1,5 @@
/* Return first macro parameter.
- Copyright (C) 2005 Red Hat, Inc.
+ Copyright (C) 2005, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
@@ -40,7 +40,9 @@ dwarf_macro_param1 (Dwarf_Macro *macro, Dwarf_Word *paramp)
if (macro == NULL)
return -1;
- *paramp = macro->param1;
+ Dwarf_Attribute param;
+ if (dwarf_macro_param (macro, 1, ¶m) != 0)
+ return -1;
- return 0;
+ return dwarf_formudata (¶m, paramp);
}
diff --git a/libdw/dwarf_macro_param2.c b/libdw/dwarf_macro_param2.c
index b49e60e..cc902c9 100644
--- a/libdw/dwarf_macro_param2.c
+++ b/libdw/dwarf_macro_param2.c
@@ -1,5 +1,5 @@
/* Return second macro parameter.
- Copyright (C) 2005 Red Hat, Inc.
+ Copyright (C) 2005, 2014 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
@@ -40,10 +40,16 @@ dwarf_macro_param2 (Dwarf_Macro *macro, Dwarf_Word *paramp, const char **strp)
if (macro == NULL)
return -1;
- if (paramp != NULL)
- *paramp = macro->param2.u;
- if (strp != NULL)
- *strp = macro->param2.s;
+ Dwarf_Attribute param;
+ if (dwarf_macro_param (macro, 1, ¶m) != 0)
+ return -1;
- return 0;
+ if (param.form == DW_FORM_string
+ || param.form == DW_FORM_strp)
+ {
+ *strp = dwarf_formstring (¶m);
+ return 0;
+ }
+ else
+ return dwarf_formudata (¶m, paramp);
}
diff --git a/libdw/dwarf_macro_version.c b/libdw/dwarf_macro_version.c
new file mode 100644
index 0000000..0b4b189
--- /dev/null
+++ b/libdw/dwarf_macro_version.c
@@ -0,0 +1,44 @@
+/* Return a version of a macro.
+ Copyright (C) 2014 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 "libdwP.h"
+
+int
+dwarf_macro_version (Dwarf_Macro *macro, unsigned int *versionp)
+{
+ if (macro == NULL)
+ return -1;
+
+ *versionp = macro->version;
+
+ return 0;
+}
diff --git a/libdw/libdw.h b/libdw/libdw.h
index 196d54a..bd5a0c6 100644
--- a/libdw/libdw.h
+++ b/libdw/libdw.h
@@ -827,25 +827,98 @@ extern int dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts);
/* Call callback function for each of the macro information entry for
- the CU. */
+ the CU. Similar in operation to dwarf_getmacros_die, but only
+ works for .debug_macinfo style macro entries (those where
+ dwarf_macro_version returns 0.) */
extern ptrdiff_t dwarf_getmacros (Dwarf_Die *cudie,
int (*callback) (Dwarf_Macro *, void *),
- void *arg, ptrdiff_t offset)
- __nonnull_attribute__ (2);
+ void *arg, ptrdiff_t token)
+ __nonnull_attribute__ (2);
+
+/* Iterate through the macro section referenced by CUDIE and call
+ CALLBACK for each macro information entry. Keeps iterating while
+ CALLBACK returns DWARF_CB_OK. If the callback returns
+ DWARF_CB_ABORT, it stops iterating and returns a continuation
+ token, which can be used to restart the iteration at the point
+ where it ended. Returns -1 for errors or 0 if there are no more
+ macro entries.
+
+ If MACOFFP is non-NULL, offset to macro section referenced by CUDIE
+ is stored to *MACOFFP. That can later be passed together with a
+ continuation token to dwarf_getmacros_off.
+
+ If this is called with non-0 TOKEN (i.e. it is a continuation
+ call), MACOFFP shall be either NULL, or point to a location that
+ contains the same section offset as was at *MACOFFP of the call
+ that the passed-in continuation token came from. */
+extern ptrdiff_t dwarf_getmacros_die (Dwarf_Die *cudie, Dwarf_Off *macoffp,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+ __nonnull_attribute__ (3);
+
+/* This is similar in operation to dwarf_getmacros_die, but selects
+ the section to iterate through by offset instead of by CU. This
+ can be used for handling DW_MACRO_GNU_transparent_include's or
+ similar opcodes, or for continuation calls seeded with MACOFF and
+ TOKEN that came back from dwarf_getmacros_die (or a previous call
+ to dwarf_getmacros_off). Note that with TOKEN of 0, this will
+ always iterate through DW_AT_GNU_macros style section. */
+extern ptrdiff_t dwarf_getmacros_off (Dwarf *dbg, Dwarf_Off macoff,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token)
+ __nonnull_attribute__ (3);
+
+/* Return Dwarf version of this macro opcode. The versions are 0 for
+ macro elements coming from DW_AT_macro_info, and 4 for macro
+ elements coming from DW_AT_GNU_macros. It is expected that 5 and
+ further will be for macro elements coming from standardized
+ DW_AT_macros. */
+extern int dwarf_macro_version (Dwarf_Macro *macro, unsigned int *versionp)
+ __nonnull_attribute__ (2);
+
+/* Set *OFFP to .debug_line offset associated with the Dwarf macro
+ section that this MACRO comes from. Returns -1 for errors or 0 for
+ success.
-/* Return macro opcode. */
+ The offset is considered to be 0xffffffffffffffff (or (Dwarf_Off)
+ -1) if .debug_line offset was not set in the section header, which
+ will always be the case for .debug_macinfo macro sections, and may
+ be the case for .debug_macro sections as well. This condition is
+ not considered an error. */
+extern int dwarf_macro_line_offset (Dwarf_Macro *macro, Dwarf_Off *offp)
+ __nonnull_attribute__ (2);
+
+/* Return macro opcode. That's a constant that can be either from
+ DW_MACINFO_* domain if version of MACRO is 0, or from
+ DW_MACRO_GNU_* domain if the version is 4. */
extern int dwarf_macro_opcode (Dwarf_Macro *macro, unsigned int *opcodep)
__nonnull_attribute__ (2);
-/* Return first macro parameter. */
+/* Get number of parameters of MACRO and store it to *PARAMCNTP. */
+extern int dwarf_macro_getparamcnt (Dwarf_Macro *macro, size_t *paramcntp);
+
+/* Get IDX-th parameter of MACRO, and stores it to *ATTRIBUTE.
+ Returns 0 on success or -1 for errors.
+
+ After a successful call, you can query ATTRIBUTE by dwarf_whatform
+ to determine which of the dwarf_formX calls to make to get actual
+ value out of ATTRIBUTE. Note that calling dwarf_whatattr is not
+ meaningful for pseudo-attributes formed this way. */
+extern int dwarf_macro_param (Dwarf_Macro *macro, size_t idx,
+ Dwarf_Attribute *attribute);
+
+/* Return first macro parameter. This will return -1 if the parameter
+ is not an integral value. Use dwarf_macro_param for more general
+ access. */
extern int dwarf_macro_param1 (Dwarf_Macro *macro, Dwarf_Word *paramp)
__nonnull_attribute__ (2);
-/* Return second macro parameter. */
+/* Return second macro parameter. This will return -1 if the
+ parameter is not an integral or string value. Use
+ dwarf_macro_param for more general access. */
extern int dwarf_macro_param2 (Dwarf_Macro *macro, Dwarf_Word *paramp,
const char **strp);
-
/* Compute what's known about a call frame when the PC is at ADDRESS.
Returns 0 for success or -1 for errors.
On success, *FRAME is a malloc'd pointer. */
diff --git a/libdw/libdw.map b/libdw/libdw.map
index 55bc537..5a88bdc 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -306,4 +306,13 @@ ELFUTILS_0.160 {
global:
dwarf_cu_getdwarf;
dwarf_cu_die;
-} ELFUTILS_0.159;
\ No newline at end of file
+} ELFUTILS_0.159;
+
+ELFUTILS_0.161 {
+ global:
+ dwarf_getmacros_die;
+ dwarf_getmacros_off;
+ dwarf_macro_version;
+ dwarf_macro_getparamcnt;
+ dwarf_macro_param;
+} ELFUTILS_0.160;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index ce8a83d..47c2cc1 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -118,7 +118,8 @@ enum
DWARF_E_INVALID_OFFSET,
DWARF_E_NO_DEBUG_RANGES,
DWARF_E_INVALID_CFI,
- DWARF_E_NO_ALT_DEBUGLINK
+ DWARF_E_NO_ALT_DEBUGLINK,
+ DWARF_E_INVALID_OPCODE,
};
@@ -167,6 +168,9 @@ struct Dwarf
Dwarf_Off next_tu_offset;
Dwarf_Sig8_Hash sig8_hash;
+ /* Search tree for .debug_macro operator tables. */
+ void *macro_ops;
+
/* Address ranges. */
Dwarf_Aranges *aranges;
@@ -326,16 +330,48 @@ struct Dwarf_CU
}) \
-/* Macro information. */
+/* Prototype of a single .debug_macro operator. */
+typedef struct
+{
+ Dwarf_Word nforms;
+ unsigned char const *forms;
+} Dwarf_Macro_Op_Proto;
+
+/* Prototype table. */
+typedef struct
+{
+ /* Offset of .debug_macro section. */
+ Dwarf_Off offset;
+
+ /* Offset of associated .debug_line section. */
+ Dwarf_Off line_offset;
+
+ /* How far have we read in this section. This starts as 0 and is
+ increased as the section is worked through. */
+ Dwarf_Off read;
+
+ /* Header length. */
+ Dwarf_Half header_len;
+
+ uint16_t version;
+ bool is_64bit;
+
+ /* Shows where in TABLE each opcode is defined. Since opcode 0 is
+ never used, it stores index of opcode X in X-1'th element. The
+ value of 0xff means not stored at all. */
+ unsigned char opcodes[255];
+
+ /* Individual opcode prototypes. */
+ Dwarf_Macro_Op_Proto table[];
+} Dwarf_Macro_Op_Table;
+
struct Dwarf_Macro_s
{
- unsigned int opcode;
- Dwarf_Word param1;
- union
- {
- Dwarf_Word u;
- const char *s;
- } param2;
+ Dwarf_Off line_offset;
+ Dwarf_Word nargs;
+ uint16_t version;
+ uint8_t opcode;
+ Dwarf_Attribute *attributes;
};
--
1.7.6.5
9 years, 6 months