diff options
Diffstat (limited to 'src/libdw/dwarf_getmacros.c')
-rw-r--r-- | src/libdw/dwarf_getmacros.c | 548 |
1 files changed, 0 insertions, 548 deletions
diff --git a/src/libdw/dwarf_getmacros.c b/src/libdw/dwarf_getmacros.c deleted file mode 100644 index f9f29961..00000000 --- a/src/libdw/dwarf_getmacros.c +++ /dev/null @@ -1,548 +0,0 @@ -/* Get macro information. - Copyright (C) 2002-2009, 2014 Red Hat, Inc. - This file is part of elfutils. - Written by Ulrich Drepper <drepper@redhat.com>, 2002. - - This file is free software; you can redistribute it and/or modify - it under the terms of either - - * the GNU Lesser General Public License as published by the Free - Software Foundation; either version 3 of the License, or (at - your option) any later version - - or - - * the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at - your option) any later version - - or both in parallel, as here. - - elfutils is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received copies of the GNU General Public License and - the GNU Lesser General Public License along with this program. If - not, see <http://www.gnu.org/licenses/>. */ - -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <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; - - /* Offset into the corresponding section. */ - return INTUSE(dwarf_formudata) (&attr, retp); -} - -static int -macro_op_compare (const void *p1, const void *p2) -{ - const Dwarf_Macro_Op_Table *t1 = (const Dwarf_Macro_Op_Table *) p1; - const Dwarf_Macro_Op_Table *t2 = (const Dwarf_Macro_Op_Table *) p2; - - if (t1->offset < t2->offset) - return -1; - if (t1->offset > t2->offset) - 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, 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; - if (readp >= endp) - goto invalid; - get_uleb128 (e.nforms, readp, endp); - e.forms = readp; - op_protos[opcode - 1] = e; - - readp += e.nforms; - if (readp > endp) - { - invalid: - __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, 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; - } - - 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 (unlikely (macoff >= d->d_size)) - { - __libdw_seterrno (DWARF_E_INVALID_DWARF); - return -1; - } - - const unsigned char *const startp = d->d_buf + macoff; - const unsigned char *const endp = d->d_buf + d->d_size; - - Dwarf_Macro_Op_Table *table = cache_op_table (dbg, sec_index, macoff, - startp, endp, cudie); - if (table == NULL) - return -1; - - if (offset == 0) - offset = table->header_len; - - assert (offset >= 0); - assert (offset < endp - startp); - const unsigned char *readp = startp + offset; - - while (readp < endp) - { - unsigned int opcode = *readp++; - 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]; - - /* 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, - .startp = (void *) startp + offset, - .endp = (void *) endp, - }; - - Dwarf_Attribute attributes[proto->nforms]; - for (Dwarf_Word i = 0; i < proto->nforms; ++i) - { - /* 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; - - size_t len = __libdw_form_val_len (&fake_cu, proto->forms[i], readp); - if (len == (size_t) -1) - return -1; - - readp += len; - } - - Dwarf_Macro macro = { - .table = table, - .opcode = opcode, - .attributes = attributes, - }; - - if (callback (¯o, arg) != DWARF_CB_OK) - return readp - startp; - } - - return 0; -} - -/* Token layout: - - - The highest bit is used for distinguishing between callers that - know that opcode 0xff may have one of two incompatible meanings. - The mask that we use for selecting this bit is - DWARF_GETMACROS_START. - - - The rest of the token (31 or 63 bits) encodes address inside the - macro unit. - - Besides, token value of 0 signals end of iteration and -1 is - reserved for signaling errors. That means it's impossible to - represent maximum offset of a .debug_macro unit to new-style - callers (which in practice decreases the permissible macro unit - size by another 1 byte). */ - -static ptrdiff_t -token_from_offset (ptrdiff_t offset, bool accept_0xff) -{ - if (offset == -1 || offset == 0) - return offset; - - /* Make sure the offset didn't overflow into the flag bit. */ - if ((offset & DWARF_GETMACROS_START) != 0) - { - __libdw_seterrno (DWARF_E_TOO_BIG); - return -1; - } - - if (accept_0xff) - offset |= DWARF_GETMACROS_START; - - return offset; -} - -static ptrdiff_t -offset_from_token (ptrdiff_t token, bool *accept_0xffp) -{ - *accept_0xffp = (token & DWARF_GETMACROS_START) != 0; - token &= ~DWARF_GETMACROS_START; - - return token; -} - -static ptrdiff_t -gnu_macros_getmacros_off (Dwarf *dbg, Dwarf_Off macoff, - int (*callback) (Dwarf_Macro *, void *), - void *arg, ptrdiff_t offset, bool accept_0xff, - Dwarf_Die *cudie) -{ - assert (offset >= 0); - - if (macoff >= dbg->sectiondata[IDX_debug_macro]->d_size) - { - __libdw_seterrno (DWARF_E_INVALID_OFFSET); - return -1; - } - - return read_macros (dbg, IDX_debug_macro, macoff, - callback, arg, offset, accept_0xff, cudie); -} - -static ptrdiff_t -macro_info_getmacros_off (Dwarf *dbg, Dwarf_Off macoff, - int (*callback) (Dwarf_Macro *, void *), - void *arg, ptrdiff_t offset, Dwarf_Die *cudie) -{ - assert (offset >= 0); - - return read_macros (dbg, IDX_debug_macinfo, macoff, - callback, arg, offset, 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; - } - - bool accept_0xff; - ptrdiff_t offset = offset_from_token (token, &accept_0xff); - assert (accept_0xff); - - offset = gnu_macros_getmacros_off (dbg, macoff, callback, arg, offset, - accept_0xff, NULL); - - return token_from_offset (offset, accept_0xff); -} - -ptrdiff_t -dwarf_getmacros (cudie, callback, arg, token) - Dwarf_Die *cudie; - int (*callback) (Dwarf_Macro *, void *); - void *arg; - ptrdiff_t token; -{ - if (cudie == NULL) - { - __libdw_seterrno (DWARF_E_NO_DWARF); - return -1; - } - - /* 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 - unless the TOKEN that we obtained indicates the call originates - from a new-style caller. See above for details on what - information is encoded into tokens. */ - - bool accept_0xff; - ptrdiff_t offset = offset_from_token (token, &accept_0xff); - - /* DW_AT_macro_info */ - if (dwarf_hasattr (cudie, DW_AT_macro_info)) - { - Dwarf_Word macoff; - if (get_offset_from (cudie, DW_AT_macro_info, &macoff) != 0) - return -1; - offset = macro_info_getmacros_off (cudie->cu->dbg, macoff, - callback, arg, offset, cudie); - } - else - { - /* DW_AT_GNU_macros, DW_AT_macros */ - Dwarf_Word macoff; - if (get_offset_from (cudie, DW_AT_GNU_macros, &macoff) != 0) - return -1; - offset = gnu_macros_getmacros_off (cudie->cu->dbg, macoff, - callback, arg, offset, accept_0xff, - cudie); - } - - return token_from_offset (offset, accept_0xff); -} |