summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPetr Machata <pmachata@redhat.com>2014-10-17 02:47:03 +0200
committerPetr Machata <pmachata@redhat.com>2014-10-17 02:51:22 +0200
commitb9e9711ef018915262a878ea7510387f0845c389 (patch)
tree24243b1daf0ec015d67a66c852019091b37e9475
parent564cfbe20beb077c3441400c4b0c406478043d76 (diff)
downloadelfutils-b9e9711ef018915262a878ea7510387f0845c389.tar.gz
Support .debug_macro
- This code is based on the following proposal: http://www.dwarfstd.org/ShowIssue.php?issue=110722.1 - dwarf_getmacros serves either of .debug_macinfo or .debug_macro transparently, but if the latter uses opcode 0xff, it bails out with an error. The reason is that in .debug_macro, 0xff is a custom code that can mean anything, while in .debug_macinfo there's fixed semantics associated with 0xff. - dwarf_getmacros_off is a new interface used for requesting iteration through transparently included units. - dwarf_macro_getparamcnt and dwarf_macro_param are new interfaces used for requesting number of parameters of an opcode and individual parameters. dwarf_macro_getsrcfiles is a new interface used for requesting a file part of .debug_line unit associated with macro unit that the opcode comes from. - 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. - dwarf_getsrclines was made into a light wrapper around a worker function that loads line unit given its offset. The worker also caches loaded units in an offset-keyed search tree, so that we don't end up re-reading units even though they were read in a different domain (e.g. a macro unit request can prime cache for later CU lookup). dwarf_macro_getsrcfiles calls the worker function under covers. Signed-off-by: Petr Machata <pmachata@redhat.com>
-rw-r--r--libdw/ChangeLog39
-rw-r--r--libdw/Makefile.am8
-rw-r--r--libdw/dwarf_end.c6
-rw-r--r--libdw/dwarf_error.c3
-rw-r--r--libdw/dwarf_getmacros.c545
-rw-r--r--libdw/dwarf_getsrclines.c1275
-rw-r--r--libdw/dwarf_macro_param1.c8
-rw-r--r--libdw/dwarf_macro_param2.c18
-rw-r--r--libdw/libdw.h76
-rw-r--r--libdw/libdw.map10
-rw-r--r--libdw/libdwP.h87
11 files changed, 1364 insertions, 711 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 89b2735f..37e8700a 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,42 @@
+2014-09-10 Petr Machata <pmachata@redhat.com>
+
+ * dwarf_macro_getparamcnt.c: New file.
+ * dwarf_macro_param.c: New file.
+ * dwarf_macro_getsrcfiles.c: New file.
+ * Makefile.am (libdw_a_SOURCES): Add the new files.
+ * libdwP.h (struct files_lines_s): New structure.
+ (DWARF_E_INVALID_OPCODE): New enumerator.
+ (struct Dwarf): New fields macro_ops, files_lines.
+ (Dwarf_Macro_Op_Proto, Dwarf_Macro_Op_Table): New structures for
+ keeping macro opcode prototypes in.
+ (Dwarf_Macro_s): Redefine from scratch.
+ (__libdw_getsrclines, __libdw_getcompdir, libdw_macro_nforms): New
+ internal interfaces.
+ * dwarf_error.c (errmsgs): Add a message for
+ DWARF_E_INVALID_OPCODE.
+ * dwarf_end.c (dwarf_end): Destroy struct Dwarf.macro_ops and
+ files_lines.
+ * libdw.h (dwarf_getmacros_off, dwarf_macro_getparamcnt)
+ (dwarf_macro_getsrcfiles, dwarf_macro_param): New public
+ interfaces.
+ * dwarf_getmacros.c (dwarf_getmacros_off): New function,
+ (get_offset_from, macro_op_compare, build_table)
+ (init_macinfo_table, get_macinfo_table, get_table_for_offset)
+ (cache_op_table, read_macros, gnu_macros_getmacros_off)
+ (macro_info_getmacros_off, do_dwarf_getmacros_die): New helper
+ functions.
+ (dwarf_getmacros): Adjust to dispatch to the new interfaces.
+ * dwarf_getsrclines.c (read_srclines): New function with guts
+ taken from dwarf_getsrclines.
+ (__libdw_getsrclines): Likewise.
+ (__libdw_getcompdir, files_lines_compare): New functions.
+ (dwarf_getsrclines): Make it 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_off,
+ dwarf_macro_getsrcfiles, dwarf_macro_getparamcnt, dwarf_macro_param.
+
2014-10-15 Petr Machata <pmachata@redhat.com>
* libdwP.h (struct Dwarf_Files_s.cu): Drop field.
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 2e42a376..12e0fff1 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_opcode.c dwarf_macro_param.c \
+ dwarf_macro_param1.c dwarf_macro_param2.c \
+ dwarf_macro_getsrcfiles.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 241a257a..647a1b8d 100644
--- a/libdw/dwarf_end.c
+++ b/libdw/dwarf_end.c
@@ -93,6 +93,12 @@ dwarf_end (dwarf)
tdestroy (dwarf->cu_tree, cu_free);
tdestroy (dwarf->tu_tree, cu_free);
+ /* Search tree for macro opcode tables. */
+ tdestroy (dwarf->macro_ops, noop_free);
+
+ /* Search tree for decoded .debug_lines units. */
+ tdestroy (dwarf->files_lines, noop_free);
+
struct libdw_memblock *memp = dwarf->mem_tail;
/* The first block is allocated together with the Dwarf object. */
while (memp->prev != NULL)
diff --git a/libdw/dwarf_error.c b/libdw/dwarf_error.c
index 22929141..08b691aa 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@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 a3d2c816..7f889a91 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@redhat.com>, 2002.
@@ -31,114 +31,495 @@
# include <config.h>
#endif
+#include <assert.h>
#include <dwarf.h>
+#include <search.h>
+#include <stdlib.h>
#include <string.h>
#include <libdwP.h>
+static int
+get_offset_from (Dwarf_Die *die, int name, Dwarf_Word *retp)
+{
+ /* Get the appropriate attribute. */
+ Dwarf_Attribute attr;
+ if (INTUSE(dwarf_attr) (die, name, &attr) == NULL)
+ return -1;
-ptrdiff_t
-dwarf_getmacros (die, callback, arg, offset)
- Dwarf_Die *die;
- int (*callback) (Dwarf_Macro *, void *);
- void *arg;
- ptrdiff_t offset;
+ /* Offset into the corresponding section. */
+ return INTUSE(dwarf_formudata) (&attr, retp);
+}
+
+static int
+macro_op_compare (const void *p1, const void *p2)
{
- if (die == NULL)
+ 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)
+ return 1;
+
+ if (t1->sec_index < t2->sec_index)
+ return -1;
+ if (t1->sec_index > t2->sec_index)
+ 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 - 1] = 0xff;
+}
+
+#define MACRO_PROTO(NAME, ...) \
+ Dwarf_Macro_Op_Proto NAME = ({ \
+ static const uint8_t proto[] = {__VA_ARGS__}; \
+ (Dwarf_Macro_Op_Proto) {sizeof proto, proto}; \
+ })
+
+enum { macinfo_data_size = offsetof (Dwarf_Macro_Op_Table, table[5]) };
+static unsigned char macinfo_data[macinfo_data_size]
+ __attribute__ ((aligned (__alignof (Dwarf_Macro_Op_Table))));
+
+static __attribute__ ((constructor)) void
+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,
+ /* If you are adding more elements to this array, increase
+ MACINFO_DATA_SIZE above. */
+ };
+
+ Dwarf_Macro_Op_Table *macinfo_table = (void *) macinfo_data;
+ memset (macinfo_table, 0, sizeof macinfo_data);
+ build_table (macinfo_table, op_protos);
+ macinfo_table->sec_index = IDX_debug_macinfo;
+}
+
+static Dwarf_Macro_Op_Table *
+get_macinfo_table (Dwarf *dbg, Dwarf_Word macoff,
+ __attribute__ ((unused)) const unsigned char *readp,
+ __attribute__ ((unused)) const unsigned char *const endp,
+ Dwarf_Die *cudie)
+{
+ assert (cudie != NULL);
+
+ Dwarf_Attribute attr_mem, *attr
+ = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list, &attr_mem);
+ Dwarf_Off line_offset = (Dwarf_Off) -1;
+ if (attr != NULL)
+ INTUSE(dwarf_formudata) (attr, &line_offset);
+
+ Dwarf_Macro_Op_Table *table = libdw_alloc (dbg, Dwarf_Macro_Op_Table,
+ macinfo_data_size, 1);
+ memcpy (table, macinfo_data, macinfo_data_size);
+
+ table->offset = macoff;
+ table->sec_index = IDX_debug_macinfo;
+ table->line_offset = line_offset;
+ table->is_64bit = cudie->cu->address_size == 8;
+ table->comp_dir = __libdw_getcompdir (cudie);
+
+ return table;
+}
+
+static Dwarf_Macro_Op_Table *
+get_table_for_offset (Dwarf *dbg, Dwarf_Word macoff,
+ const unsigned char *readp,
+ const unsigned char *const endp,
+ Dwarf_Die *cudie)
+{
+ 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;
+ }
+
+ 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;
+ }
+ else if (cudie != NULL)
+ {
+ Dwarf_Attribute attr_mem, *attr
+ = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list, &attr_mem);
+ if (attr != NULL)
+ INTUSE(dwarf_formudata) (attr, &line_offset);
+ }
+
+ /* """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 - 1] = 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,
+ .sec_index = IDX_debug_macro,
+ .line_offset = line_offset,
+ .header_len = readp - startp,
+ .version = version,
+ .is_64bit = is_64bit,
+
+ /* NULL if CUDIE is NULL or DW_AT_comp_dir is absent. */
+ .comp_dir = __libdw_getcompdir (cudie),
+ };
+ build_table (table, op_protos);
+
+ return table;
+}
+
+static Dwarf_Macro_Op_Table *
+cache_op_table (Dwarf *dbg, int sec_index, Dwarf_Off macoff,
+ const unsigned char *startp,
+ const unsigned char *const endp,
+ Dwarf_Die *cudie)
+{
+ Dwarf_Macro_Op_Table fake = { .offset = macoff, .sec_index = sec_index };
+ Dwarf_Macro_Op_Table **found = tfind (&fake, &dbg->macro_ops,
+ macro_op_compare);
+ if (found != NULL)
+ return *found;
+
+ Dwarf_Macro_Op_Table *table = sec_index == IDX_debug_macro
+ ? get_table_for_offset (dbg, macoff, startp, endp, cudie)
+ : get_macinfo_table (dbg, macoff, startp, endp, cudie);
+
+ if (table == NULL)
+ return NULL;
+
+ Dwarf_Macro_Op_Table **ret = tsearch (table, &dbg->macro_ops,
+ macro_op_compare);
+ if (unlikely (ret == NULL))
+ {
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return NULL;
+ }
- Elf_Data *d = die->cu->dbg->sectiondata[IDX_debug_macinfo];
- if (unlikely (d == NULL) || unlikely (d->d_buf == NULL))
+ return *ret;
+}
+
+static ptrdiff_t
+read_macros (Dwarf *dbg, int sec_index,
+ Dwarf_Off macoff, int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t offset, bool accept_0xff,
+ Dwarf_Die *cudie)
+{
+ Elf_Data *d = dbg->sectiondata[sec_index];
+ 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 (unlikely (offset > (ptrdiff_t) d->d_size))
- goto invalid;
+ Dwarf_Macro_Op_Table *table = cache_op_table (dbg, sec_index, macoff,
+ startp, endp, cudie);
+ if (table == NULL)
+ return -1;
- 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;
+
+ if (unlikely (opcode == 0xff && ! accept_0xff))
+ {
+ /* See comment below at dwarf_getmacros for explanation of
+ why we are doing this. */
+ __libdw_seterrno (DWARF_E_INVALID_OPCODE);
+ return -1;
+ }
+
+ 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.
+ We arbitrarily pretend it's version 4. */
+ 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 = {
+ .table = table,
+ .opcode = opcode,
+ .attributes = attributes,
+ };
+
+ if (callback (&macro, 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, bool accept_0xff,
+ Dwarf_Die *cudie)
+{
+ assert (token <= 0);
+
+ if (macoff >= dbg->sectiondata[IDX_debug_macro]->d_size)
+ {
+ __libdw_seterrno (DWARF_E_INVALID_OFFSET);
+ return -1;
+ }
+
+ ptrdiff_t ret = read_macros (dbg, IDX_debug_macro, macoff,
+ callback, arg, -token, accept_0xff, cudie);
+ 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, Dwarf_Die *cudie)
+{
+ assert (token >= 0);
+
+ return read_macros (dbg, IDX_debug_macinfo, macoff,
+ callback, arg, token, true, cudie);
+}
+
+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, NULL);
+
+ /* Either a DW_AT_GNU_macros continuation, or a fresh start
+ thereof. */
+ return gnu_macros_getmacros_off (dbg, macoff, callback, arg, token, true,
+ NULL);
+}
+
+static ptrdiff_t
+do_dwarf_getmacros_die (Dwarf_Die *cudie, Dwarf_Off *macoffp,
+ int (*callback) (Dwarf_Macro *, void *),
+ void *arg, ptrdiff_t token, bool accept_0xff)
+{
+ 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, cudie);
- if (callback (&mac, arg) != DWARF_CB_OK)
- return readp - (const unsigned char *) d->d_buf;
+ /* 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, cudie);
}
- /* If we come here the termination of the data for the CU is not
- present. */
- invalid:
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return -1;
+ 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, accept_0xff,
+ cudie);
+
+ /* 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, accept_0xff,
+ cudie);
+}
+
+ptrdiff_t
+dwarf_getmacros (die, callback, arg, offset)
+ Dwarf_Die *die;
+ int (*callback) (Dwarf_Macro *, void *);
+ void *arg;
+ ptrdiff_t offset;
+{
+ /* This function might be called from a code that expects to see
+ DW_MACINFO_* opcodes, not DW_MACRO_{GNU_,}* ones. It is fine to
+ serve most DW_MACRO_{GNU_,}* opcodes to such code, because those
+ whose values are the same as DW_MACINFO_* ones also have the same
+ behavior. It is not very likely that a .debug_macro section
+ would only use the part of opcode space that it shares with
+ .debug_macinfo, but it is possible. Serving the opcodes that are
+ only valid in DW_MACRO_{GNU_,}* domain is OK as well, because
+ clients in general need to be ready that newer standards define
+ more opcodes, and have coping mechanisms for unfamiliar opcodes.
+
+ The one exception to the above rule is opcode 0xff, which has
+ concrete semantics in .debug_macinfo, but falls into vendor block
+ in .debug_macro, and can be assigned to do whatever. There is
+ some small probability that the two opcodes would look
+ superficially similar enough that a client would be confused and
+ misbehave as a result. For this reason, we refuse to serve
+ through this interface 0xff's originating from .debug_macro. */
+
+ return do_dwarf_getmacros_die (die, NULL, callback, arg, offset, false);
}
diff --git a/libdw/dwarf_getsrclines.c b/libdw/dwarf_getsrclines.c
index 74d626cf..1f460ec2 100644
--- a/libdw/dwarf_getsrclines.c
+++ b/libdw/dwarf_getsrclines.c
@@ -34,6 +34,8 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include <search.h>
+
#include "dwarf.h"
#include "libdwP.h"
@@ -65,14 +67,12 @@ compare_lines (const void *a, const void *b)
return (*p1)->addr - (*p2)->addr;
}
-int
-dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
+static int
+read_srclines (Dwarf *dbg,
+ const unsigned char *linep, const unsigned char *lineendp,
+ const char *comp_dir, unsigned address_size,
+ Dwarf_Lines **linesp, Dwarf_Files **filesp)
{
- if (unlikely (cudie == NULL
- || (INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit
- && INTUSE(dwarf_tag) (cudie) != DW_TAG_partial_unit)))
- return -1;
-
int res = -1;
struct linelist *linelist = NULL;
@@ -84,329 +84,428 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
#define MAX_STACK_ALLOC 4096
struct linelist *malloc_linelist = NULL;
- /* Get the information if it is not already known. */
- struct Dwarf_CU *const cu = cudie->cu;
- if (cu->lines == NULL)
+ if (unlikely (linep + 4 > lineendp))
{
- /* Failsafe mode: no data found. */
- cu->lines = (void *) -1l;
- cu->files = (void *) -1l;
-
- /* The die must have a statement list associated. */
- Dwarf_Attribute stmt_list_mem;
- Dwarf_Attribute *stmt_list = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list,
- &stmt_list_mem);
-
- /* Get the offset into the .debug_line section. NB: this call
- also checks whether the previous dwarf_attr call failed. */
- const unsigned char *lineendp;
- const unsigned char *linep
- = __libdw_formptr (stmt_list, IDX_debug_line, DWARF_E_NO_DEBUG_LINE,
- (unsigned char **) &lineendp, NULL);
- if (linep == NULL)
- goto out;
-
- /* Get the compilation directory. */
- Dwarf_Attribute compdir_attr_mem;
- Dwarf_Attribute *compdir_attr = INTUSE(dwarf_attr) (cudie,
- DW_AT_comp_dir,
- &compdir_attr_mem);
- const char *comp_dir = INTUSE(dwarf_formstring) (compdir_attr);
-
- if (unlikely (linep + 4 > lineendp))
- {
- invalid_data:
- __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE);
- goto out;
- }
-
- Dwarf *dbg = cu->dbg;
- Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep);
- unsigned int length = 4;
- if (unlikely (unit_length == DWARF3_LENGTH_64_BIT))
- {
- if (unlikely (linep + 8 > lineendp))
- goto invalid_data;
- unit_length = read_8ubyte_unaligned_inc (dbg, linep);
- length = 8;
- }
+ invalid_data:
+ __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE);
+ goto out;
+ }
- /* Check whether we have enough room in the section. */
- if (unit_length < 2 + length + 5 * 1
- || unlikely (linep + unit_length > lineendp))
+ Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep);
+ unsigned int length = 4;
+ if (unlikely (unit_length == DWARF3_LENGTH_64_BIT))
+ {
+ if (unlikely (linep + 8 > lineendp))
goto invalid_data;
- lineendp = linep + unit_length;
+ unit_length = read_8ubyte_unaligned_inc (dbg, linep);
+ length = 8;
+ }
- /* The next element of the header is the version identifier. */
- uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
- if (unlikely (version < 2) || unlikely (version > 4))
- {
- __libdw_seterrno (DWARF_E_VERSION);
- goto out;
- }
+ /* Check whether we have enough room in the section. */
+ if (unit_length < 2 + length + 5 * 1
+ || unlikely (linep + unit_length > lineendp))
+ goto invalid_data;
+ lineendp = linep + unit_length;
- /* Next comes the header length. */
- Dwarf_Word header_length;
- if (length == 4)
- header_length = read_4ubyte_unaligned_inc (dbg, linep);
- else
- header_length = read_8ubyte_unaligned_inc (dbg, linep);
- const unsigned char *header_start = linep;
+ /* The next element of the header is the version identifier. */
+ uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
+ if (unlikely (version < 2) || unlikely (version > 4))
+ {
+ __libdw_seterrno (DWARF_E_VERSION);
+ goto out;
+ }
- /* Next the minimum instruction length. */
- uint_fast8_t minimum_instr_len = *linep++;
+ /* Next comes the header length. */
+ Dwarf_Word header_length;
+ if (length == 4)
+ header_length = read_4ubyte_unaligned_inc (dbg, linep);
+ else
+ header_length = read_8ubyte_unaligned_inc (dbg, linep);
+ const unsigned char *header_start = linep;
- /* Next the maximum operations per instruction, in version 4 format. */
- uint_fast8_t max_ops_per_instr = 1;
- if (version >= 4)
- {
- if (unlikely (lineendp - linep < 5))
- goto invalid_data;
- max_ops_per_instr = *linep++;
- if (unlikely (max_ops_per_instr == 0))
- goto invalid_data;
- }
+ /* Next the minimum instruction length. */
+ uint_fast8_t minimum_instr_len = *linep++;
- /* Then the flag determining the default value of the is_stmt
- register. */
- uint_fast8_t default_is_stmt = *linep++;
+ /* Next the maximum operations per instruction, in version 4 format. */
+ uint_fast8_t max_ops_per_instr = 1;
+ if (version >= 4)
+ {
+ if (unlikely (lineendp - linep < 5))
+ goto invalid_data;
+ max_ops_per_instr = *linep++;
+ if (unlikely (max_ops_per_instr == 0))
+ goto invalid_data;
+ }
- /* Now the line base. */
- int_fast8_t line_base = (int8_t) *linep++;
+ /* Then the flag determining the default value of the is_stmt
+ register. */
+ uint_fast8_t default_is_stmt = *linep++;
+
+ /* Now the line base. */
+ int_fast8_t line_base = (int8_t) *linep++;
+
+ /* And the line range. */
+ uint_fast8_t line_range = *linep++;
+
+ /* The opcode base. */
+ uint_fast8_t opcode_base = *linep++;
+
+ /* Remember array with the standard opcode length (-1 to account for
+ the opcode with value zero not being mentioned). */
+ const uint8_t *standard_opcode_lengths = linep - 1;
+ if (unlikely (lineendp - linep < opcode_base - 1))
+ goto invalid_data;
+ linep += opcode_base - 1;
+
+ /* First comes the list of directories. Add the compilation
+ directory first since the index zero is used for it. */
+ struct dirlist
+ {
+ const char *dir;
+ size_t len;
+ struct dirlist *next;
+ } comp_dir_elem =
+ {
+ .dir = comp_dir,
+ .len = comp_dir ? strlen (comp_dir) : 0,
+ .next = NULL
+ };
+ struct dirlist *dirlist = &comp_dir_elem;
+ unsigned int ndirlist = 1;
+
+ // XXX Directly construct array to conserve memory?
+ while (*linep != 0)
+ {
+ struct dirlist *new_dir =
+ (struct dirlist *) alloca (sizeof (*new_dir));
- /* And the line range. */
- uint_fast8_t line_range = *linep++;
+ new_dir->dir = (char *) linep;
+ uint8_t *endp = memchr (linep, '\0', lineendp - linep);
+ if (endp == NULL)
+ goto invalid_data;
+ new_dir->len = endp - linep;
+ new_dir->next = dirlist;
+ dirlist = new_dir;
+ ++ndirlist;
+ linep = endp + 1;
+ }
+ /* Skip the final NUL byte. */
+ ++linep;
- /* The opcode base. */
- uint_fast8_t opcode_base = *linep++;
+ /* Rearrange the list in array form. */
+ struct dirlist **dirarray
+ = (struct dirlist **) alloca (ndirlist * sizeof (*dirarray));
+ for (unsigned int n = ndirlist; n-- > 0; dirlist = dirlist->next)
+ dirarray[n] = dirlist;
- /* Remember array with the standard opcode length (-1 to account for
- the opcode with value zero not being mentioned). */
- const uint8_t *standard_opcode_lengths = linep - 1;
- if (unlikely (lineendp - linep < opcode_base - 1))
+ /* Now read the files. */
+ struct filelist null_file =
+ {
+ .info =
+ {
+ .name = "???",
+ .mtime = 0,
+ .length = 0
+ },
+ .next = NULL
+ };
+ struct filelist *filelist = &null_file;
+ unsigned int nfilelist = 1;
+
+ if (unlikely (linep >= lineendp))
+ goto invalid_data;
+ while (*linep != 0)
+ {
+ struct filelist *new_file =
+ (struct filelist *) alloca (sizeof (*new_file));
+
+ /* First comes the file name. */
+ char *fname = (char *) linep;
+ uint8_t *endp = memchr (fname, '\0', lineendp - linep);
+ if (endp == NULL)
goto invalid_data;
- linep += opcode_base - 1;
+ size_t fnamelen = endp - (uint8_t *) fname;
+ linep = endp + 1;
- /* First comes the list of directories. Add the compilation
- directory first since the index zero is used for it. */
- struct dirlist
- {
- const char *dir;
- size_t len;
- struct dirlist *next;
- } comp_dir_elem =
- {
- .dir = comp_dir,
- .len = comp_dir ? strlen (comp_dir) : 0,
- .next = NULL
- };
- struct dirlist *dirlist = &comp_dir_elem;
- unsigned int ndirlist = 1;
-
- // XXX Directly construct array to conserve memory?
- while (*linep != 0)
+ /* Then the index. */
+ Dwarf_Word diridx;
+ get_uleb128 (diridx, linep);
+ if (unlikely (diridx >= ndirlist))
{
- struct dirlist *new_dir =
- (struct dirlist *) alloca (sizeof (*new_dir));
-
- new_dir->dir = (char *) linep;
- uint8_t *endp = memchr (linep, '\0', lineendp - linep);
- if (endp == NULL)
- goto invalid_data;
- new_dir->len = endp - linep;
- new_dir->next = dirlist;
- dirlist = new_dir;
- ++ndirlist;
- linep = endp + 1;
+ __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
+ goto out;
}
- /* Skip the final NUL byte. */
- ++linep;
- /* Rearrange the list in array form. */
- struct dirlist **dirarray
- = (struct dirlist **) alloca (ndirlist * sizeof (*dirarray));
- for (unsigned int n = ndirlist; n-- > 0; dirlist = dirlist->next)
- dirarray[n] = dirlist;
-
- /* Now read the files. */
- struct filelist null_file =
- {
- .info =
- {
- .name = "???",
- .mtime = 0,
- .length = 0
- },
- .next = NULL
- };
- struct filelist *filelist = &null_file;
- unsigned int nfilelist = 1;
-
- if (unlikely (linep >= lineendp))
- goto invalid_data;
- while (*linep != 0)
+ if (*fname == '/')
+ /* It's an absolute path. */
+ new_file->info.name = fname;
+ else
{
- struct filelist *new_file =
- (struct filelist *) alloca (sizeof (*new_file));
-
- /* First comes the file name. */
- char *fname = (char *) linep;
- uint8_t *endp = memchr (fname, '\0', lineendp - linep);
- if (endp == NULL)
- goto invalid_data;
- size_t fnamelen = endp - (uint8_t *) fname;
- linep = endp + 1;
+ new_file->info.name = libdw_alloc (dbg, char, 1,
+ dirarray[diridx]->len + 1
+ + fnamelen + 1);
+ char *cp = new_file->info.name;
- /* Then the index. */
- Dwarf_Word diridx;
- get_uleb128 (diridx, linep);
- if (unlikely (diridx >= ndirlist))
+ if (dirarray[diridx]->dir != NULL)
{
- __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
- goto out;
+ /* This value could be NULL in case the DW_AT_comp_dir
+ was not present. We cannot do much in this case.
+ The easiest thing is to convert the path in an
+ absolute path. */
+ cp = stpcpy (cp, dirarray[diridx]->dir);
}
-
- if (*fname == '/')
- /* It's an absolute path. */
- new_file->info.name = fname;
- else
- {
- new_file->info.name = libdw_alloc (dbg, char, 1,
- dirarray[diridx]->len + 1
- + fnamelen + 1);
- char *cp = new_file->info.name;
-
- if (dirarray[diridx]->dir != NULL)
- {
- /* This value could be NULL in case the DW_AT_comp_dir
- was not present. We cannot do much in this case.
- The easiest thing is to convert the path in an
- absolute path. */
- cp = stpcpy (cp, dirarray[diridx]->dir);
- }
- *cp++ = '/';
- strcpy (cp, fname);
- assert (strlen (new_file->info.name)
- < dirarray[diridx]->len + 1 + fnamelen + 1);
- }
-
- /* Next comes the modification time. */
- get_uleb128 (new_file->info.mtime, linep);
-
- /* Finally the length of the file. */
- get_uleb128 (new_file->info.length, linep);
-
- new_file->next = filelist;
- filelist = new_file;
- ++nfilelist;
+ *cp++ = '/';
+ strcpy (cp, fname);
+ assert (strlen (new_file->info.name)
+ < dirarray[diridx]->len + 1 + fnamelen + 1);
}
- /* Skip the final NUL byte. */
- ++linep;
- /* Consistency check. */
- if (unlikely (linep != header_start + header_length))
- {
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- goto out;
- }
+ /* Next comes the modification time. */
+ get_uleb128 (new_file->info.mtime, linep);
- /* We are about to process the statement program. Initialize the
- state machine registers (see 6.2.2 in the v2.1 specification). */
- Dwarf_Word addr = 0;
- unsigned int op_index = 0;
- unsigned int file = 1;
- int line = 1;
- unsigned int column = 0;
- uint_fast8_t is_stmt = default_is_stmt;
- bool basic_block = false;
- bool prologue_end = false;
- bool epilogue_begin = false;
- unsigned int isa = 0;
- unsigned int discriminator = 0;
-
- /* Apply the "operation advance" from a special opcode
- or DW_LNS_advance_pc (as per DWARF4 6.2.5.1). */
- inline void advance_pc (unsigned int op_advance)
- {
- addr += minimum_instr_len * ((op_index + op_advance)
- / max_ops_per_instr);
- op_index = (op_index + op_advance) % max_ops_per_instr;
- }
+ /* Finally the length of the file. */
+ get_uleb128 (new_file->info.length, linep);
- /* Process the instructions. */
+ new_file->next = filelist;
+ filelist = new_file;
+ ++nfilelist;
+ }
+ /* Skip the final NUL byte. */
+ ++linep;
- /* Adds a new line to the matrix.
- We cannot simply define a function because we want to use alloca. */
+ /* Consistency check. */
+ if (unlikely (linep != header_start + header_length))
+ {
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ goto out;
+ }
+
+ /* We are about to process the statement program. Initialize the
+ state machine registers (see 6.2.2 in the v2.1 specification). */
+ Dwarf_Word addr = 0;
+ unsigned int op_index = 0;
+ unsigned int file = 1;
+ int line = 1;
+ unsigned int column = 0;
+ uint_fast8_t is_stmt = default_is_stmt;
+ bool basic_block = false;
+ bool prologue_end = false;
+ bool epilogue_begin = false;
+ unsigned int isa = 0;
+ unsigned int discriminator = 0;
+
+ /* Apply the "operation advance" from a special opcode or
+ DW_LNS_advance_pc (as per DWARF4 6.2.5.1). */
+ inline void advance_pc (unsigned int op_advance)
+ {
+ addr += minimum_instr_len * ((op_index + op_advance)
+ / max_ops_per_instr);
+ op_index = (op_index + op_advance) % max_ops_per_instr;
+ }
+
+ /* Process the instructions. */
+
+ /* Adds a new line to the matrix.
+ We cannot simply define a function because we want to use alloca. */
#define NEW_LINE(end_seq) \
- do { \
- struct linelist *ll = (nlinelist < MAX_STACK_ALLOC \
- ? alloca (sizeof (struct linelist)) \
- : malloc (sizeof (struct linelist))); \
- if (nlinelist >= MAX_STACK_ALLOC) \
- malloc_linelist = ll; \
- if (unlikely (add_new_line (ll, end_seq))) \
- goto invalid_data; \
- } while (0)
-
- inline bool add_new_line (struct linelist *new_line, bool end_sequence)
- {
- new_line->next = linelist;
- linelist = new_line;
- ++nlinelist;
-
- /* Set the line information. For some fields we use bitfields,
- so we would lose information if the encoded values are too large.
- Check just for paranoia, and call the data "invalid" if it
- violates our assumptions on reasonable limits for the values. */
+ do { \
+ struct linelist *ll = (nlinelist < MAX_STACK_ALLOC \
+ ? alloca (sizeof (struct linelist)) \
+ : malloc (sizeof (struct linelist))); \
+ if (nlinelist >= MAX_STACK_ALLOC) \
+ malloc_linelist = ll; \
+ if (unlikely (add_new_line (ll, end_seq))) \
+ goto invalid_data; \
+ } while (0)
+
+ inline bool add_new_line (struct linelist *new_line, bool end_sequence)
+ {
+ new_line->next = linelist;
+ linelist = new_line;
+ ++nlinelist;
+
+ /* Set the line information. For some fields we use bitfields,
+ so we would lose information if the encoded values are too large.
+ Check just for paranoia, and call the data "invalid" if it
+ violates our assumptions on reasonable limits for the values. */
#define SET(field) \
- do { \
- new_line->line.field = field; \
- if (unlikely (new_line->line.field != field)) \
- return true; \
- } while (0)
-
- SET (addr);
- SET (op_index);
- SET (file);
- SET (line);
- SET (column);
- SET (is_stmt);
- SET (basic_block);
- SET (end_sequence);
- SET (prologue_end);
- SET (epilogue_begin);
- SET (isa);
- SET (discriminator);
+ do { \
+ new_line->line.field = field; \
+ if (unlikely (new_line->line.field != field)) \
+ return true; \
+ } while (0)
+
+ SET (addr);
+ SET (op_index);
+ SET (file);
+ SET (line);
+ SET (column);
+ SET (is_stmt);
+ SET (basic_block);
+ SET (end_sequence);
+ SET (prologue_end);
+ SET (epilogue_begin);
+ SET (isa);
+ SET (discriminator);
#undef SET
- return false;
- }
+ return false;
+ }
+
+ while (linep < lineendp)
+ {
+ unsigned int opcode;
+ unsigned int u128;
+ int s128;
+
+ /* Read the opcode. */
+ opcode = *linep++;
- while (linep < lineendp)
+ /* Is this a special opcode? */
+ if (likely (opcode >= opcode_base))
+ {
+ /* Yes. Handling this is quite easy since the opcode value
+ is computed with
+
+ opcode = (desired line increment - line_base)
+ + (line_range * address advance) + opcode_base
+ */
+ int line_increment = (line_base
+ + (opcode - opcode_base) % line_range);
+
+ /* Perform the increments. */
+ line += line_increment;
+ advance_pc ((opcode - opcode_base) / line_range);
+
+ /* Add a new line with the current state machine values. */
+ NEW_LINE (0);
+
+ /* Reset the flags. */
+ basic_block = false;
+ prologue_end = false;
+ epilogue_begin = false;
+ discriminator = 0;
+ }
+ else if (opcode == 0)
{
- unsigned int opcode;
- unsigned int u128;
- int s128;
+ /* This an extended opcode. */
+ if (unlikely (lineendp - linep < 2))
+ goto invalid_data;
+
+ /* The length. */
+ uint_fast8_t len = *linep++;
+
+ if (unlikely ((size_t) (lineendp - linep) < len))
+ goto invalid_data;
- /* Read the opcode. */
+ /* The sub-opcode. */
opcode = *linep++;
- /* Is this a special opcode? */
- if (likely (opcode >= opcode_base))
+ switch (opcode)
{
- /* Yes. Handling this is quite easy since the opcode value
- is computed with
+ case DW_LNE_end_sequence:
+ /* Add a new line with the current state machine values.
+ The is the end of the sequence. */
+ NEW_LINE (1);
+
+ /* Reset the registers. */
+ addr = 0;
+ op_index = 0;
+ file = 1;
+ line = 1;
+ column = 0;
+ is_stmt = default_is_stmt;
+ basic_block = false;
+ prologue_end = false;
+ epilogue_begin = false;
+ isa = 0;
+ discriminator = 0;
+ break;
+
+ case DW_LNE_set_address:
+ /* The value is an address. The size is defined as
+ apporiate for the target machine. We use the
+ address size field from the CU header. */
+ op_index = 0;
+ if (unlikely (lineendp - linep < address_size))
+ goto invalid_data;
+ if (__libdw_read_address_inc (dbg, IDX_debug_line, &linep,
+ address_size, &addr))
+ goto out;
+ break;
+
+ case DW_LNE_define_file:
+ {
+ char *fname = (char *) linep;
+ uint8_t *endp = memchr (linep, '\0', lineendp - linep);
+ if (endp == NULL)
+ goto invalid_data;
+ size_t fnamelen = endp - linep;
+ linep = endp + 1;
+
+ unsigned int diridx;
+ get_uleb128 (diridx, linep);
+ Dwarf_Word mtime;
+ get_uleb128 (mtime, linep);
+ Dwarf_Word filelength;
+ get_uleb128 (filelength, linep);
+
+ struct filelist *new_file =
+ (struct filelist *) alloca (sizeof (*new_file));
+ if (fname[0] == '/')
+ new_file->info.name = fname;
+ else
+ {
+ new_file->info.name =
+ libdw_alloc (dbg, char, 1, (dirarray[diridx]->len + 1
+ + fnamelen + 1));
+ char *cp = new_file->info.name;
+
+ if (dirarray[diridx]->dir != NULL)
+ /* This value could be NULL in case the
+ DW_AT_comp_dir was not present. We
+ cannot do much in this case. The easiest
+ thing is to convert the path in an
+ absolute path. */
+ cp = stpcpy (cp, dirarray[diridx]->dir);
+ *cp++ = '/';
+ strcpy (cp, fname);
+ }
- opcode = (desired line increment - line_base)
- + (line_range * address advance) + opcode_base
- */
- int line_increment = (line_base
- + (opcode - opcode_base) % line_range);
+ new_file->info.mtime = mtime;
+ new_file->info.length = filelength;
+ new_file->next = filelist;
+ filelist = new_file;
+ ++nfilelist;
+ }
+ break;
+
+ case DW_LNE_set_discriminator:
+ /* Takes one ULEB128 parameter, the discriminator. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_data;
- /* Perform the increments. */
- line += line_increment;
- advance_pc ((opcode - opcode_base) / line_range);
+ get_uleb128 (discriminator, linep);
+ break;
+
+ default:
+ /* Unknown, ignore it. */
+ if (unlikely ((size_t) (lineendp - (linep - 1)) < len))
+ goto invalid_data;
+ linep += len - 1;
+ break;
+ }
+ }
+ else if (opcode <= DW_LNS_set_isa)
+ {
+ /* This is a known standard opcode. */
+ switch (opcode)
+ {
+ case DW_LNS_copy:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
/* Add a new line with the current state machine values. */
NEW_LINE (0);
@@ -416,330 +515,195 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
prologue_end = false;
epilogue_begin = false;
discriminator = 0;
- }
- else if (opcode == 0)
- {
- /* This an extended opcode. */
- if (unlikely (lineendp - linep < 2))
+ break;
+
+ case DW_LNS_advance_pc:
+ /* Takes one uleb128 parameter which is added to the
+ address. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
goto invalid_data;
- /* The length. */
- uint_fast8_t len = *linep++;
+ get_uleb128 (u128, linep);
+ advance_pc (u128);
+ break;
- if (unlikely ((size_t) (lineendp - linep) < len))
+ case DW_LNS_advance_line:
+ /* Takes one sleb128 parameter which is added to the
+ line. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
goto invalid_data;
- /* The sub-opcode. */
- opcode = *linep++;
-
- switch (opcode)
- {
- case DW_LNE_end_sequence:
- /* Add a new line with the current state machine values.
- The is the end of the sequence. */
- NEW_LINE (1);
-
- /* Reset the registers. */
- addr = 0;
- op_index = 0;
- file = 1;
- line = 1;
- column = 0;
- is_stmt = default_is_stmt;
- basic_block = false;
- prologue_end = false;
- epilogue_begin = false;
- isa = 0;
- discriminator = 0;
- break;
-
- case DW_LNE_set_address:
- /* The value is an address. The size is defined as
- apporiate for the target machine. We use the
- address size field from the CU header. */
- op_index = 0;
- if (unlikely (lineendp - linep < cu->address_size))
- goto invalid_data;
- if (__libdw_read_address_inc (dbg, IDX_debug_line, &linep,
- cu->address_size, &addr))
- goto out;
- break;
-
- case DW_LNE_define_file:
- {
- char *fname = (char *) linep;
- uint8_t *endp = memchr (linep, '\0', lineendp - linep);
- if (endp == NULL)
- goto invalid_data;
- size_t fnamelen = endp - linep;
- linep = endp + 1;
-
- unsigned int diridx;
- get_uleb128 (diridx, linep);
- Dwarf_Word mtime;
- get_uleb128 (mtime, linep);
- Dwarf_Word filelength;
- get_uleb128 (filelength, linep);
-
- struct filelist *new_file =
- (struct filelist *) alloca (sizeof (*new_file));
- if (fname[0] == '/')
- new_file->info.name = fname;
- else
- {
- new_file->info.name =
- libdw_alloc (dbg, char, 1, (dirarray[diridx]->len + 1
- + fnamelen + 1));
- char *cp = new_file->info.name;
-
- if (dirarray[diridx]->dir != NULL)
- /* This value could be NULL in case the
- DW_AT_comp_dir was not present. We
- cannot do much in this case. The easiest
- thing is to convert the path in an
- absolute path. */
- cp = stpcpy (cp, dirarray[diridx]->dir);
- *cp++ = '/';
- strcpy (cp, fname);
- }
-
- new_file->info.mtime = mtime;
- new_file->info.length = filelength;
- new_file->next = filelist;
- filelist = new_file;
- ++nfilelist;
- }
- break;
-
- case DW_LNE_set_discriminator:
- /* Takes one ULEB128 parameter, the discriminator. */
- if (unlikely (standard_opcode_lengths[opcode] != 1))
- goto invalid_data;
-
- get_uleb128 (discriminator, linep);
- break;
-
- default:
- /* Unknown, ignore it. */
- if (unlikely ((size_t) (lineendp - (linep - 1)) < len))
- goto invalid_data;
- linep += len - 1;
- break;
- }
- }
- else if (opcode <= DW_LNS_set_isa)
- {
- /* This is a known standard opcode. */
- switch (opcode)
- {
- case DW_LNS_copy:
- /* Takes no argument. */
- if (unlikely (standard_opcode_lengths[opcode] != 0))
- goto invalid_data;
-
- /* Add a new line with the current state machine values. */
- NEW_LINE (0);
-
- /* Reset the flags. */
- basic_block = false;
- prologue_end = false;
- epilogue_begin = false;
- discriminator = 0;
- break;
-
- case DW_LNS_advance_pc:
- /* Takes one uleb128 parameter which is added to the
- address. */
- if (unlikely (standard_opcode_lengths[opcode] != 1))
- goto invalid_data;
-
- get_uleb128 (u128, linep);
- advance_pc (u128);
- break;
-
- case DW_LNS_advance_line:
- /* Takes one sleb128 parameter which is added to the
- line. */
- if (unlikely (standard_opcode_lengths[opcode] != 1))
- goto invalid_data;
-
- get_sleb128 (s128, linep);
- line += s128;
- break;
-
- case DW_LNS_set_file:
- /* Takes one uleb128 parameter which is stored in file. */
- if (unlikely (standard_opcode_lengths[opcode] != 1))
- goto invalid_data;
-
- get_uleb128 (u128, linep);
- file = u128;
- break;
-
- case DW_LNS_set_column:
- /* Takes one uleb128 parameter which is stored in column. */
- if (unlikely (standard_opcode_lengths[opcode] != 1))
- goto invalid_data;
-
- get_uleb128 (u128, linep);
- column = u128;
- break;
-
- case DW_LNS_negate_stmt:
- /* Takes no argument. */
- if (unlikely (standard_opcode_lengths[opcode] != 0))
- goto invalid_data;
-
- is_stmt = 1 - is_stmt;
- break;
-
- case DW_LNS_set_basic_block:
- /* Takes no argument. */
- if (unlikely (standard_opcode_lengths[opcode] != 0))
- goto invalid_data;
-
- basic_block = true;
- break;
-
- case DW_LNS_const_add_pc:
- /* Takes no argument. */
- if (unlikely (standard_opcode_lengths[opcode] != 0))
- goto invalid_data;
-
- advance_pc ((255 - opcode_base) / line_range);
- break;
-
- case DW_LNS_fixed_advance_pc:
- /* Takes one 16 bit parameter which is added to the
- address. */
- if (unlikely (standard_opcode_lengths[opcode] != 1)
- || unlikely (lineendp - linep < 2))
- goto invalid_data;
-
- addr += read_2ubyte_unaligned_inc (dbg, linep);
- op_index = 0;
- break;
-
- case DW_LNS_set_prologue_end:
- /* Takes no argument. */
- if (unlikely (standard_opcode_lengths[opcode] != 0))
- goto invalid_data;
-
- prologue_end = true;
- break;
-
- case DW_LNS_set_epilogue_begin:
- /* Takes no argument. */
- if (unlikely (standard_opcode_lengths[opcode] != 0))
- goto invalid_data;
-
- epilogue_begin = true;
- break;
-
- case DW_LNS_set_isa:
- /* Takes one uleb128 parameter which is stored in isa. */
- if (unlikely (standard_opcode_lengths[opcode] != 1))
- goto invalid_data;
-
- get_uleb128 (isa, linep);
- break;
- }
- }
- else
- {
- /* This is a new opcode the generator but not we know about.
- Read the parameters associated with it but then discard
- everything. Read all the parameters for this opcode. */
- for (int n = standard_opcode_lengths[opcode]; n > 0; --n)
- get_uleb128 (u128, linep);
-
- /* Next round, ignore this opcode. */
- continue;
- }
- }
+ get_sleb128 (s128, linep);
+ line += s128;
+ break;
- /* Put all the files in an array. */
- Dwarf_Files *files = libdw_alloc (dbg, Dwarf_Files,
- sizeof (Dwarf_Files)
- + nfilelist * sizeof (Dwarf_Fileinfo)
- + (ndirlist + 1) * sizeof (char *),
- 1);
- const char **dirs = (void *) &files->info[nfilelist];
+ case DW_LNS_set_file:
+ /* Takes one uleb128 parameter which is stored in file. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_data;
- files->nfiles = nfilelist;
- while (nfilelist-- > 0)
- {
- files->info[nfilelist] = filelist->info;
- filelist = filelist->next;
- }
- assert (filelist == NULL);
-
- /* Put all the directory strings in an array. */
- files->ndirs = ndirlist;
- for (unsigned int i = 0; i < ndirlist; ++i)
- dirs[i] = dirarray[i]->dir;
- dirs[ndirlist] = NULL;
-
- /* Make the file data structure available through the CU. */
- cu->files = files;
-
- void *buf = libdw_alloc (dbg, Dwarf_Lines, (sizeof (Dwarf_Lines)
- + (sizeof (Dwarf_Line)
- * nlinelist)), 1);
-
- /* First use the buffer for the pointers, and sort the entries.
- We'll write the pointers in the end of the buffer, and then
- copy into the buffer from the beginning so the overlap works. */
- assert (sizeof (Dwarf_Line) >= sizeof (Dwarf_Line *));
- Dwarf_Line **sortlines = (buf + sizeof (Dwarf_Lines)
- + ((sizeof (Dwarf_Line)
- - sizeof (Dwarf_Line *)) * nlinelist));
-
- /* The list is in LIFO order and usually they come in clumps with
- ascending addresses. So fill from the back to probably start with
- runs already in order before we sort. */
- unsigned int i = nlinelist;
- while (i-- > 0)
- {
- sortlines[i] = &linelist->line;
- linelist = linelist->next;
- }
- assert (linelist == NULL);
+ get_uleb128 (u128, linep);
+ file = u128;
+ break;
+
+ case DW_LNS_set_column:
+ /* Takes one uleb128 parameter which is stored in column. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_data;
+
+ get_uleb128 (u128, linep);
+ column = u128;
+ break;
+
+ case DW_LNS_negate_stmt:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ is_stmt = 1 - is_stmt;
+ break;
+
+ case DW_LNS_set_basic_block:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ basic_block = true;
+ break;
+
+ case DW_LNS_const_add_pc:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ advance_pc ((255 - opcode_base) / line_range);
+ break;
+
+ case DW_LNS_fixed_advance_pc:
+ /* Takes one 16 bit parameter which is added to the
+ address. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1)
+ || unlikely (lineendp - linep < 2))
+ goto invalid_data;
- /* Sort by ascending address. */
- qsort (sortlines, nlinelist, sizeof sortlines[0], &compare_lines);
+ addr += read_2ubyte_unaligned_inc (dbg, linep);
+ op_index = 0;
+ break;
- /* Now that they are sorted, put them in the final array.
- The buffers overlap, so we've clobbered the early elements
- of SORTLINES by the time we're reading the later ones. */
- cu->lines = buf;
- cu->lines->nlines = nlinelist;
- for (i = 0; i < nlinelist; ++i)
+ case DW_LNS_set_prologue_end:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ prologue_end = true;
+ break;
+
+ case DW_LNS_set_epilogue_begin:
+ /* Takes no argument. */
+ if (unlikely (standard_opcode_lengths[opcode] != 0))
+ goto invalid_data;
+
+ epilogue_begin = true;
+ break;
+
+ case DW_LNS_set_isa:
+ /* Takes one uleb128 parameter which is stored in isa. */
+ if (unlikely (standard_opcode_lengths[opcode] != 1))
+ goto invalid_data;
+
+ get_uleb128 (isa, linep);
+ break;
+ }
+ }
+ else
{
- cu->lines->info[i] = *sortlines[i];
- cu->lines->info[i].files = files;
+ /* This is a new opcode the generator but not we know about.
+ Read the parameters associated with it but then discard
+ everything. Read all the parameters for this opcode. */
+ for (int n = standard_opcode_lengths[opcode]; n > 0; --n)
+ get_uleb128 (u128, linep);
+
+ /* Next round, ignore this opcode. */
+ continue;
}
+ }
- /* Make sure the highest address for the CU is marked as end_sequence.
- This is required by the DWARF spec, but some compilers forget and
- dwfl_module_getsrc depends on it. */
- if (nlinelist > 0)
- cu->lines->info[nlinelist - 1].end_sequence = 1;
+ /* Put all the files in an array. */
+ Dwarf_Files *files = libdw_alloc (dbg, Dwarf_Files,
+ sizeof (Dwarf_Files)
+ + nfilelist * sizeof (Dwarf_Fileinfo)
+ + (ndirlist + 1) * sizeof (char *),
+ 1);
+ const char **dirs = (void *) &files->info[nfilelist];
- /* Success. */
- res = 0;
+ files->nfiles = nfilelist;
+ while (nfilelist-- > 0)
+ {
+ files->info[nfilelist] = filelist->info;
+ filelist = filelist->next;
}
- else if (cu->lines != (void *) -1l)
- /* We already have the information. */
- res = 0;
+ assert (filelist == NULL);
+
+ /* Put all the directory strings in an array. */
+ files->ndirs = ndirlist;
+ for (unsigned int i = 0; i < ndirlist; ++i)
+ dirs[i] = dirarray[i]->dir;
+ dirs[ndirlist] = NULL;
+
+ /* Pass the file data structure to the caller. */
+ if (filesp != NULL)
+ *filesp = files;
+
+ void *buf = libdw_alloc (dbg, Dwarf_Lines, (sizeof (Dwarf_Lines)
+ + (sizeof (Dwarf_Line)
+ * nlinelist)), 1);
+
+ /* First use the buffer for the pointers, and sort the entries.
+ We'll write the pointers in the end of the buffer, and then
+ copy into the buffer from the beginning so the overlap works. */
+ assert (sizeof (Dwarf_Line) >= sizeof (Dwarf_Line *));
+ Dwarf_Line **sortlines = (buf + sizeof (Dwarf_Lines)
+ + ((sizeof (Dwarf_Line)
+ - sizeof (Dwarf_Line *)) * nlinelist));
+
+ /* The list is in LIFO order and usually they come in clumps with
+ ascending addresses. So fill from the back to probably start with
+ runs already in order before we sort. */
+ for (unsigned int i = nlinelist; i-- > 0; )
+ {
+ sortlines[i] = &linelist->line;
+ linelist = linelist->next;
+ }
+ assert (linelist == NULL);
- if (likely (res == 0))
+ /* Sort by ascending address. */
+ qsort (sortlines, nlinelist, sizeof sortlines[0], &compare_lines);
+
+ /* Now that they are sorted, put them in the final array.
+ The buffers overlap, so we've clobbered the early elements
+ of SORTLINES by the time we're reading the later ones. */
+ Dwarf_Lines *lines = buf;
+ lines->nlines = nlinelist;
+ for (unsigned int i = 0; i < nlinelist; ++i)
{
- *lines = cu->lines;
- *nlines = cu->lines->nlines;
+ lines->info[i] = *sortlines[i];
+ lines->info[i].files = files;
}
- out:
+ /* Make sure the highest address for the CU is marked as end_sequence.
+ This is required by the DWARF spec, but some compilers forget and
+ dwfl_module_getsrc depends on it. */
+ if (nlinelist > 0)
+ lines->info[nlinelist - 1].end_sequence = 1;
+
+ /* Pass the line structure back to the caller. */
+ if (linesp != NULL)
+ *linesp = lines;
+
+ /* Success. */
+ res = 0;
+
+ out:
/* Free malloced line records, if any. */
for (unsigned int i = MAX_STACK_ALLOC; i < nlinelist; i++)
{
@@ -748,8 +712,123 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
malloc_linelist = ll;
}
+ return res;
+}
+
+static int
+files_lines_compare (const void *p1, const void *p2)
+{
+ const struct files_lines_s *t1 = p1;
+ const struct files_lines_s *t2 = p2;
+
+ if (t1->debug_line_offset < t2->debug_line_offset)
+ return -1;
+ if (t1->debug_line_offset > t2->debug_line_offset)
+ return 1;
+
+ return 0;
+}
+
+int
+__libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset,
+ const char *comp_dir, unsigned address_size,
+ Dwarf_Lines **linesp, Dwarf_Files **filesp)
+{
+ struct files_lines_s fake = { .debug_line_offset = debug_line_offset };
+ struct files_lines_s **found = tfind (&fake, &dbg->files_lines,
+ files_lines_compare);
+ if (found == NULL)
+ {
+ Elf_Data *data = __libdw_checked_get_data (dbg, IDX_debug_line);
+ if (data == NULL
+ || __libdw_offset_in_section (dbg, IDX_debug_line,
+ debug_line_offset, 1) != 0)
+ return -1;
+
+ const unsigned char *linep = data->d_buf + debug_line_offset;
+ const unsigned char *lineendp = data->d_buf + data->d_size;
+
+ struct files_lines_s *node = libdw_alloc (dbg, struct files_lines_s,
+ sizeof *node, 1);
+
+ if (read_srclines (dbg, linep, lineendp, comp_dir, address_size,
+ &node->lines, &node->files) != 0)
+ return -1;
+
+ node->debug_line_offset = debug_line_offset;
+
+ found = tsearch (node, &dbg->files_lines, files_lines_compare);
+ if (found == NULL)
+ {
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return -1;
+ }
+ }
+
+ if (linesp != NULL)
+ *linesp = (*found)->lines;
+
+ if (filesp != NULL)
+ *filesp = (*found)->files;
+
+ return 0;
+}
+
+/* Get the compilation directory, if any is set. */
+const char *
+__libdw_getcompdir (Dwarf_Die *cudie)
+{
+ if (cudie == NULL)
+ return NULL;
+
+ Dwarf_Attribute compdir_attr_mem;
+ Dwarf_Attribute *compdir_attr = INTUSE(dwarf_attr) (cudie,
+ DW_AT_comp_dir,
+ &compdir_attr_mem);
+ return INTUSE(dwarf_formstring) (compdir_attr);
+}
+
+int
+dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
+{
+ if (unlikely (cudie == NULL
+ || (INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit
+ && INTUSE(dwarf_tag) (cudie) != DW_TAG_partial_unit)))
+ return -1;
+
+ /* Get the information if it is not already known. */
+ struct Dwarf_CU *const cu = cudie->cu;
+ if (cu->lines == NULL)
+ {
+ /* Failsafe mode: no data found. */
+ cu->lines = (void *) -1l;
+ cu->files = (void *) -1l;
+
+ /* The die must have a statement list associated. */
+ Dwarf_Attribute stmt_list_mem;
+ Dwarf_Attribute *stmt_list = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list,
+ &stmt_list_mem);
+
+ /* Get the offset into the .debug_line section. NB: this call
+ also checks whether the previous dwarf_attr call failed. */
+ Dwarf_Off debug_line_offset;
+ if (__libdw_formptr (stmt_list, IDX_debug_line, DWARF_E_NO_DEBUG_LINE,
+ NULL, &debug_line_offset) == NULL)
+ return -1;
+
+ if (__libdw_getsrclines (cu->dbg, debug_line_offset,
+ __libdw_getcompdir (cudie),
+ cu->address_size, &cu->lines, &cu->files) < 0)
+ return -1;
+ }
+ else if (cu->lines == (void *) -1l)
+ return -1;
+
+ *lines = cu->lines;
+ *nlines = cu->lines->nlines;
+
// XXX Eventually: unlocking here.
- return res;
+ return 0;
}
INTDEF(dwarf_getsrclines)
diff --git a/libdw/dwarf_macro_param1.c b/libdw/dwarf_macro_param1.c
index 35d4a718..337ad6f0 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@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, &param) != 0)
+ return -1;
- return 0;
+ return dwarf_formudata (&param, paramp);
}
diff --git a/libdw/dwarf_macro_param2.c b/libdw/dwarf_macro_param2.c
index b49e60ea..cc902c99 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@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, &param) != 0)
+ return -1;
- return 0;
+ if (param.form == DW_FORM_string
+ || param.form == DW_FORM_strp)
+ {
+ *strp = dwarf_formstring (&param);
+ return 0;
+ }
+ else
+ return dwarf_formudata (&param, paramp);
}
diff --git a/libdw/libdw.h b/libdw/libdw.h
index 196d54ae..cd7f5d15 100644
--- a/libdw/libdw.h
+++ b/libdw/libdw.h
@@ -826,26 +826,86 @@ extern int dwarf_func_inline_instances (Dwarf_Die *func,
extern int dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts);
-/* Call callback function for each of the macro information entry for
- the CU. */
+/* Iterate through the macro unit 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.
+
+ Note that the Dwarf_Macro pointer passed to the callback is only
+ valid for the duration of the callback invocation.
+
+ Note that this interface will refuse to serve opcode 0xff from
+ .debug_macro sections. Such opcode is considered invalid and will
+ cause dwarf_getmacros to return with error. Note that this should
+ be no limitation as of now, as DW_MACRO_GNU_* domain doesn't
+ allocate 0xff. It is however a theoretical possibility with future
+ Dwarf standards. */
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);
+
+/* This is similar in operation to dwarf_getmacros, but selects the
+ unit to iterate through by offset instead of by CU. This can be
+ used for handling DW_MACRO_GNU_transparent_include's or similar
+ opcodes. Note that with TOKEN of 0, this will always choose to
+ iterate through .debug_macro, never .debug_macinfo.
+
+ It is not appropriate to obtain macro unit offset by hand from a CU
+ DIE and then request iteration through this interface. The reason
+ for this is that if a dwarf_macro_getsrcfiles is later called,
+ there would be no way to figure out what DW_AT_comp_dir was present
+ on the CU DIE, and file names referenced in either the macro unit
+ itself, or the .debug_line unit that it references, might be wrong.
+ Use dwarf_getmacro. */
+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 macro opcode. */
+/* Get the source files used by the macro entry. You shouldn't assume
+ that Dwarf_Files references will remain valid after MACRO becomes
+ invalid. (Which is to say it's only valid within the
+ dwarf_getmacros* callback.) Returns 0 for success or a negative
+ value in case of an error. */
+extern int dwarf_macro_getsrcfiles (Dwarf *dbg, Dwarf_Macro *macro,
+ Dwarf_Files **files, size_t *nfiles)
+ __nonnull_attribute__ (2, 3, 4);
+
+/* 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 55bc5371..754fb5fb 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -306,4 +306,12 @@ 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_off;
+ dwarf_macro_getsrcfiles;
+ dwarf_macro_getparamcnt;
+ dwarf_macro_param;
+} ELFUTILS_0.160;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index c0f3741c..1963ef58 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -59,6 +59,14 @@ struct loc_block_s
size_t length;
};
+/* Already decoded .debug_line units. */
+struct files_lines_s
+{
+ Dwarf_Off debug_line_offset;
+ Dwarf_Files *files;
+ Dwarf_Lines *lines;
+};
+
/* Valid indeces for the section data. */
enum
{
@@ -118,7 +126,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 +176,12 @@ struct Dwarf
Dwarf_Off next_tu_offset;
Dwarf_Sig8_Hash sig8_hash;
+ /* Search tree for .debug_macro operator tables. */
+ void *macro_ops;
+
+ /* Search tree for decoded .debug_line units. */
+ void *files_lines;
+
/* Address ranges. */
Dwarf_Aranges *aranges;
@@ -325,18 +340,58 @@ 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;
+
+ /* The source file information. */
+ Dwarf_Files *files;
+
+ /* If this macro unit was opened through dwarf_getmacros or
+ dwarf_getmacros_die, this caches value of DW_AT_comp_dir, if
+ present. */
+ const char *comp_dir;
+
+ /* Header length. */
+ Dwarf_Half header_len;
+
+ uint16_t version;
+ bool is_64bit;
+ uint8_t sec_index; /* IDX_debug_macro or IDX_debug_macinfo. */
+
+ /* 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_Macro_Op_Table *table;
+ Dwarf_Attribute *attributes;
+ uint8_t opcode;
};
+static inline Dwarf_Word
+libdw_macro_nforms (Dwarf_Macro *macro)
+{
+ return macro->table->table[macro->table->opcodes[macro->opcode - 1]].nforms;
+}
/* We have to include the file at this point because the inline
functions access internals of the Dwarf structure. */
@@ -655,6 +710,20 @@ unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
void __libdw_empty_loc_attr (Dwarf_Attribute *attr, struct Dwarf_CU *cu)
internal_function;
+/* Load .debug_line unit at DEBUG_LINE_OFFSET. COMP_DIR is a value of
+ DW_AT_comp_dir or NULL if that attribute is not available. Caches
+ the loaded unit and optionally set *LINESP and/or *FILESP (if not
+ NULL) with loaded information. Returns 0 for success or a negative
+ value for failure. */
+int __libdw_getsrclines (Dwarf *dbg, Dwarf_Off debug_line_offset,
+ const char *comp_dir, unsigned address_size,
+ Dwarf_Lines **linesp, Dwarf_Files **filesp)
+ internal_function
+ __nonnull_attribute__ (1);
+
+/* Load and return value of DW_AT_comp_dir from CUDIE. */
+const char *__libdw_getcompdir (Dwarf_Die *cudie);
+
/* Aliases to avoid PLTs. */
INTDECL (dwarf_aggregate_size)