diff options
-rw-r--r-- | libdw/ChangeLog | 39 | ||||
-rw-r--r-- | libdw/Makefile.am | 8 | ||||
-rw-r--r-- | libdw/dwarf_end.c | 6 | ||||
-rw-r--r-- | libdw/dwarf_error.c | 3 | ||||
-rw-r--r-- | libdw/dwarf_getmacros.c | 545 | ||||
-rw-r--r-- | libdw/dwarf_getsrclines.c | 1275 | ||||
-rw-r--r-- | libdw/dwarf_macro_param1.c | 8 | ||||
-rw-r--r-- | libdw/dwarf_macro_param2.c | 18 | ||||
-rw-r--r-- | libdw/libdw.h | 76 | ||||
-rw-r--r-- | libdw/libdw.map | 10 | ||||
-rw-r--r-- | libdw/libdwP.h | 87 |
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 (¯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, 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, ¶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 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, ¶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/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) |