diff options
Diffstat (limited to 'libdwarf/dwarf_util.c')
-rw-r--r-- | libdwarf/dwarf_util.c | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/libdwarf/dwarf_util.c b/libdwarf/dwarf_util.c new file mode 100644 index 0000000..5af6232 --- /dev/null +++ b/libdwarf/dwarf_util.c @@ -0,0 +1,584 @@ +/* + Copyright (C) 2000-2005 Silicon Graphics, Inc. All Rights Reserved. + Portions Copyright (C) 2007-2011 David Anderson. All Rights Reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2.1 of the GNU Lesser General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it would be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + Further, this software is distributed without any warranty that it is + free of the rightful claim of any third person regarding infringement + or the like. Any license provided herein, whether implied or + otherwise, applies only to this software file. Patent licenses, if + any, provided herein do not apply to combinations of this program with + other software, or any other product whatsoever. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301, + USA. + + Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, + Mountain View, CA 94043, or: + + http://www.sgi.com + + For further information regarding this notice, see: + + http://oss.sgi.com/projects/GenInfo/NoticeExplan + +*/ +/* The address of the Free Software Foundation is + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301, USA. + SGI has moved from the Crittenden Lane address. +*/ + + + + + +#include "config.h" +#include "dwarf_incl.h" +#include <stdio.h> +#include "dwarf_die_deliv.h" + + + +/* Given a form, and a pointer to the bytes encoding + a value of that form, val_ptr, this function returns + the length, in bytes, of a value of that form. + When using this function, check for a return of 0 + a recursive DW_FORM_INDIRECT value. */ +Dwarf_Unsigned +_dwarf_get_size_of_val(Dwarf_Debug dbg, + Dwarf_Unsigned form, + Dwarf_Half address_size, + Dwarf_Small * val_ptr, int v_length_size) +{ + Dwarf_Unsigned length = 0; + Dwarf_Word leb128_length = 0; + Dwarf_Unsigned form_indirect = 0; + Dwarf_Unsigned ret_value = 0; + + switch (form) { + + default: /* Handles form = 0. */ + return (form); + + case DW_FORM_addr: + if(address_size) { + return address_size; + } + /* This should never happen, address_size should be set. */ + return (dbg->de_pointer_size); + case DW_FORM_ref_sig8: + return 8; /* sizeof Dwarf_Sig8 */ + + /* DWARF2 was wrong on the size of the attribute for + DW_FORM_ref_addr. We assume compilers are using the + corrected DWARF3 text (for 32bit pointer target objects pointer and + offsets are the same size anyway). */ + case DW_FORM_ref_addr: + return (v_length_size); + + case DW_FORM_block1: + return (*(Dwarf_Small *) val_ptr + 1); + + case DW_FORM_block2: + READ_UNALIGNED(dbg, ret_value, Dwarf_Unsigned, + val_ptr, sizeof(Dwarf_Half)); + return (ret_value + sizeof(Dwarf_Half)); + + case DW_FORM_block4: + READ_UNALIGNED(dbg, ret_value, Dwarf_Unsigned, + val_ptr, sizeof(Dwarf_ufixed)); + return (ret_value + sizeof(Dwarf_ufixed)); + + + case DW_FORM_data1: + return (1); + + case DW_FORM_data2: + return (2); + + case DW_FORM_data4: + return (4); + + case DW_FORM_data8: + return (8); + + case DW_FORM_string: + return (strlen((char *) val_ptr) + 1); + + case DW_FORM_block: + case DW_FORM_exprloc: + length = _dwarf_decode_u_leb128(val_ptr, &leb128_length); + return (length + leb128_length); + + case DW_FORM_flag_present: + return (0); + case DW_FORM_flag: + return (1); + + case DW_FORM_sec_offset: + /* If 32bit dwarf, is 4. Else is 64bit dwarf and is 8. */ + return (v_length_size); + + case DW_FORM_ref_udata: + /* Discard the decoded value, we just want the length + of the value. */ + _dwarf_decode_u_leb128(val_ptr, &leb128_length); + return (leb128_length); + + case DW_FORM_indirect: + { + Dwarf_Word indir_len = 0; + + form_indirect = _dwarf_decode_u_leb128(val_ptr, &indir_len); + if (form_indirect == DW_FORM_indirect) { + return (0); /* We are in big trouble: The true form + of DW_FORM_indirect is + DW_FORM_indirect? Nonsense. Should + never happen. */ + } + return (indir_len + _dwarf_get_size_of_val(dbg, + form_indirect, + address_size, + val_ptr + indir_len, + v_length_size)); + } + + case DW_FORM_ref1: + return (1); + + case DW_FORM_ref2: + return (2); + + case DW_FORM_ref4: + return (4); + + case DW_FORM_ref8: + return (8); + + case DW_FORM_sdata: + /* Discard the decoded value, we just want the length + of the value. */ + _dwarf_decode_s_leb128(val_ptr, &leb128_length); + return (leb128_length); + + case DW_FORM_strp: + return (v_length_size); + + case DW_FORM_udata: + /* Discard the decoded value, we just want the length + of the value. */ + _dwarf_decode_u_leb128(val_ptr, &leb128_length); + return (leb128_length); + } +} + +/* We allow an arbitrary number of HT_MULTIPLE entries + before resizing. It seems up to 20 or 30 + would work nearly as well. + We could have a different resize multiple than 'resize now' + test multiple, but for now we don't do that. */ +#define HT_MULTIPLE 8 + +/* Copy the old entries, updating each to be in + a new list. Don't delete anything. Leave the + htin with stale data. */ +static void +copy_abbrev_table_to_new_table(Dwarf_Hash_Table htin, + Dwarf_Hash_Table htout) +{ + Dwarf_Hash_Table_Entry entry_in = htin->tb_entries; + unsigned entry_in_count = htin->tb_table_entry_count; + Dwarf_Hash_Table_Entry entry_out = htout->tb_entries; + unsigned entry_out_count = htout->tb_table_entry_count; + unsigned k = 0; + for ( ; k < entry_in_count; ++k,++entry_in) { + Dwarf_Abbrev_List listent = entry_in->at_head; + Dwarf_Abbrev_List nextlistent = 0; + + for ( ; listent ; listent = nextlistent) { + unsigned newtmp = listent->ab_code; + unsigned newhash = newtmp%entry_out_count; + Dwarf_Hash_Table_Entry e; + nextlistent = listent->ab_next; + e = entry_out+newhash; + /* Move_entry_to_new_hash. This reverses the + order of the entries, effectively, but + that does not seem significant. */ + listent->ab_next = e->at_head; + e->at_head = listent; + + htout->tb_total_abbrev_count++; + } + } +} + +/* This function returns a pointer to a Dwarf_Abbrev_List_s + struct for the abbrev with the given code. It puts the + struct on the appropriate hash table. It also adds all + the abbrev between the last abbrev added and this one to + the hash table. In other words, the .debug_abbrev section + is scanned sequentially from the top for an abbrev with + the given code. All intervening abbrevs are also put + into the hash table. + + This function hashes the given code, and checks the chain + at that hash table entry to see if a Dwarf_Abbrev_List_s + with the given code exists. If yes, it returns a pointer + to that struct. Otherwise, it scans the .debug_abbrev + section from the last byte scanned for that CU till either + an abbrev with the given code is found, or an abbrev code + of 0 is read. It puts Dwarf_Abbrev_List_s entries for all + abbrev's read till that point into the hash table. The + hash table contains both a head pointer and a tail pointer + for each entry. + + While the lists can move and entries can be moved between + lists on reallocation, any given Dwarf_Abbrev_list entry + never moves once allocated, so the pointer is safe to return. + + Returns NULL on error. */ +Dwarf_Abbrev_List +_dwarf_get_abbrev_for_code(Dwarf_CU_Context cu_context, Dwarf_Unsigned code) +{ + Dwarf_Debug dbg = cu_context->cc_dbg; + Dwarf_Hash_Table hash_table_base = cu_context->cc_abbrev_hash_table; + Dwarf_Hash_Table_Entry entry_base = 0; + Dwarf_Hash_Table_Entry entry_cur = 0; + Dwarf_Word hash_num = 0; + Dwarf_Unsigned abbrev_code = 0; + Dwarf_Unsigned abbrev_tag = 0; + Dwarf_Unsigned attr_name = 0; + Dwarf_Unsigned attr_form = 0; + + Dwarf_Abbrev_List hash_abbrev_entry = 0; + + Dwarf_Abbrev_List inner_list_entry = 0; + Dwarf_Hash_Table_Entry inner_hash_entry = 0; + + Dwarf_Byte_Ptr abbrev_ptr = 0; + Dwarf_Byte_Ptr end_abbrev_ptr = 0; + unsigned hashable_val = 0; + + if ( !hash_table_base->tb_entries ) { + hash_table_base->tb_table_entry_count = HT_MULTIPLE; + hash_table_base->tb_total_abbrev_count= 0; + hash_table_base->tb_entries = _dwarf_get_alloc(dbg, + DW_DLA_HASH_TABLE_ENTRY, + hash_table_base->tb_table_entry_count); + if(! hash_table_base->tb_entries) { + return NULL; + } + + } else if (hash_table_base->tb_total_abbrev_count > + ( hash_table_base->tb_table_entry_count * HT_MULTIPLE) ) { + struct Dwarf_Hash_Table_s newht; + /* Effectively multiplies by >= HT_MULTIPLE */ + newht.tb_table_entry_count = hash_table_base->tb_total_abbrev_count; + newht.tb_total_abbrev_count = 0; + newht.tb_entries = _dwarf_get_alloc(dbg, + DW_DLA_HASH_TABLE_ENTRY, + newht.tb_table_entry_count); + + if(! newht.tb_entries) { + return NULL; + } + /* Copy the existing entries to the new table, + rehashing each. */ + copy_abbrev_table_to_new_table(hash_table_base, &newht); + /* Dealloc only the entries hash table array, not the lists + of things pointed to by a hash table entry array. */ + dwarf_dealloc(dbg, hash_table_base->tb_entries,DW_DLA_HASH_TABLE_ENTRY); + hash_table_base->tb_entries = 0; + /* Now overwrite the existing table descriptor with + the new, newly valid, contents. */ + *hash_table_base = newht; + } /* Else is ok as is, add entry */ + + + hashable_val = code; + hash_num = hashable_val % + hash_table_base->tb_table_entry_count; + entry_base = hash_table_base->tb_entries; + entry_cur = entry_base + hash_num; + + /* Determine if the 'code' is the list of synonyms already. */ + for (hash_abbrev_entry = entry_cur->at_head; + hash_abbrev_entry != NULL && hash_abbrev_entry->ab_code != code; + hash_abbrev_entry = hash_abbrev_entry->ab_next); + if (hash_abbrev_entry != NULL) { + /* This returns a pointer to an abbrev list entry, not + the list itself. */ + return (hash_abbrev_entry); + } + + abbrev_ptr = cu_context->cc_last_abbrev_ptr != NULL ? + cu_context->cc_last_abbrev_ptr : + dbg->de_debug_abbrev.dss_data + cu_context->cc_abbrev_offset; + end_abbrev_ptr = dbg->de_debug_abbrev.dss_data + + dbg->de_debug_abbrev.dss_size; + + /* End of abbrev's as we are past the end entirely. + THis can happen */ + if (abbrev_ptr > end_abbrev_ptr) { + return (NULL); + } + /* End of abbrev's for this cu, since abbrev code is 0. */ + if (*abbrev_ptr == 0) { + return (NULL); + } + + do { + unsigned new_hashable_val = 0; + DECODE_LEB128_UWORD(abbrev_ptr, abbrev_code); + DECODE_LEB128_UWORD(abbrev_ptr, abbrev_tag); + + inner_list_entry = (Dwarf_Abbrev_List) + _dwarf_get_alloc(cu_context->cc_dbg, DW_DLA_ABBREV_LIST, 1); + if (inner_list_entry == NULL) { + return (NULL); + } + + new_hashable_val = abbrev_code; + hash_num = new_hashable_val % + hash_table_base->tb_table_entry_count; + inner_hash_entry = entry_base + hash_num; + /* Move_entry_to_new_hash */ + inner_list_entry->ab_next = inner_hash_entry->at_head; + inner_hash_entry->at_head = inner_list_entry; + + hash_table_base->tb_total_abbrev_count++; + + inner_list_entry->ab_code = abbrev_code; + inner_list_entry->ab_tag = abbrev_tag; + inner_list_entry->ab_has_child = *(abbrev_ptr++); + inner_list_entry->ab_abbrev_ptr = abbrev_ptr; + + /* Cycle thru the abbrev content, ignoring the content except + to find the end of the content. */ + do { + DECODE_LEB128_UWORD(abbrev_ptr, attr_name); + DECODE_LEB128_UWORD(abbrev_ptr, attr_form); + } while (attr_name != 0 && attr_form != 0); + + /* We may have fallen off the end of content, that is not + a botch in the section, as there is no rule that the last + abbrev need have abbrev_code of 0. */ + } while ( (abbrev_ptr < end_abbrev_ptr ) && + *abbrev_ptr != 0 && abbrev_code != code); + + cu_context->cc_last_abbrev_ptr = abbrev_ptr; + return (abbrev_code == code ? inner_list_entry : NULL); +} + + +/* return 1 if string ends before 'endptr' else +** return 0 meaning string is not properly terminated. +** Presumption is the 'endptr' pts to end of some dwarf section data. +*/ +int +_dwarf_string_valid(void *startptr, void *endptr) +{ + + char *start = startptr; + char *end = endptr; + + while (start < end) { + if (*start == 0) { + return 1; /* OK! */ + } + ++start; + ++end; + } + return 0; /* FAIL! bad string! */ +} + +/* + A byte-swapping version of memcpy + for cross-endian use. + Only 2,4,8 should be lengths passed in. +*/ +void * +_dwarf_memcpy_swap_bytes(void *s1, const void *s2, size_t len) +{ + void *orig_s1 = s1; + unsigned char *targ = (unsigned char *) s1; + unsigned char *src = (unsigned char *) s2; + + if (len == 4) { + targ[3] = src[0]; + targ[2] = src[1]; + targ[1] = src[2]; + targ[0] = src[3]; + } else if (len == 8) { + targ[7] = src[0]; + targ[6] = src[1]; + targ[5] = src[2]; + targ[4] = src[3]; + targ[3] = src[4]; + targ[2] = src[5]; + targ[1] = src[6]; + targ[0] = src[7]; + } else if (len == 2) { + targ[1] = src[0]; + targ[0] = src[1]; + } +/* should NOT get below here: is not the intended use */ + else if (len == 1) { + targ[0] = src[0]; + } else { + memcpy(s1, s2, len); + } + + return orig_s1; +} + + +/* This calculation used to be sprinkled all over. + Now brought to one place. + + We try to accurately compute the size of a cu header + given a known cu header location ( an offset in .debug_info + or debug_types). */ +/* ARGSUSED */ +Dwarf_Unsigned +_dwarf_length_of_cu_header(Dwarf_Debug dbg, Dwarf_Unsigned offset, + Dwarf_Bool is_info) +{ + int local_length_size = 0; + int local_extension_size = 0; + Dwarf_Unsigned length = 0; + Dwarf_Unsigned final_size = 0; + Dwarf_Small *cuptr = + is_info? dbg->de_debug_info.dss_data + offset: + dbg->de_debug_types.dss_data+ offset; + + READ_AREA_LENGTH(dbg, length, Dwarf_Unsigned, + cuptr, local_length_size, local_extension_size); + + final_size = local_extension_size + /* initial extension, if present */ + local_length_size + /* Size of cu length field. */ + sizeof(Dwarf_Half) + /* Size of version stamp field. */ + local_length_size + /* Size of abbrev offset field. */ + sizeof(Dwarf_Small); /* Size of address size field. */ + + if(!is_info) { + final_size += + /* type signature size */ + sizeof (Dwarf_Sig8) + + /* type offset size */ + local_length_size; + } + return final_size; +} + +/* Pretend we know nothing about the CU + and just roughly compute the result. */ +Dwarf_Unsigned +_dwarf_length_of_cu_header_simple(Dwarf_Debug dbg, + Dwarf_Bool dinfo) +{ + Dwarf_Unsigned finalsize = 0; + finalsize = dbg->de_length_size + /* Size of cu length field. */ + sizeof(Dwarf_Half) + /* Size of version stamp field. */ + dbg->de_length_size + /* Size of abbrev offset field. */ + sizeof(Dwarf_Small); /* Size of address size field. */ + if(!dinfo) { + finalsize += + /* type signature size */ + sizeof (Dwarf_Sig8) + + /* type offset size */ + dbg->de_length_size; + } + return finalsize; +} + +/* Now that we delay loading .debug_info, we need to do the + load in more places. So putting the load + code in one place now instead of replicating it in multiple + places. */ +int +_dwarf_load_debug_info(Dwarf_Debug dbg, Dwarf_Error * error) +{ + int res = DW_DLV_ERROR; + if (dbg->de_debug_info.dss_data) { + return DW_DLV_OK; + } + res = _dwarf_load_section(dbg, &dbg->de_debug_abbrev,error); + if (res != DW_DLV_OK) { + return res; + } + res = _dwarf_load_section(dbg, &dbg->de_debug_info, error); + return res; +} +int +_dwarf_load_debug_types(Dwarf_Debug dbg, Dwarf_Error * error) +{ + int res = DW_DLV_ERROR; + if (dbg->de_debug_types.dss_data) { + return DW_DLV_OK; + } + res = _dwarf_load_section(dbg, &dbg->de_debug_abbrev,error); + if (res != DW_DLV_OK) { + return res; + } + res = _dwarf_load_section(dbg, &dbg->de_debug_types, error); + return res; +} +void +_dwarf_free_abbrev_hash_table_contents(Dwarf_Debug dbg,Dwarf_Hash_Table hash_table) +{ + /* A Hash Table is an array with tb_table_entry_count struct + Dwarf_Hash_Table_s entries in the array. */ + int hashnum = 0; + for (; hashnum < hash_table->tb_table_entry_count; ++hashnum) { + struct Dwarf_Abbrev_List_s *abbrev = 0; + struct Dwarf_Abbrev_List_s *nextabbrev = 0; + struct Dwarf_Hash_Table_Entry_s *tb = &hash_table->tb_entries[hashnum]; + + abbrev = tb->at_head; + for (; abbrev; abbrev = nextabbrev) { + nextabbrev = abbrev->ab_next; + dwarf_dealloc(dbg, abbrev, DW_DLA_ABBREV_LIST); + } + } + /* Frees all the entries at once: an array. */ + dwarf_dealloc(dbg,hash_table->tb_entries,DW_DLA_HASH_TABLE_ENTRY); +} + +/* + If no die provided the size value returned might be wrong. + If different compilation units have different address sizes + this may not give the correct value in all contexts if the die + pointer is NULL. + If the Elf offset size != address_size + (for example if address_size = 4 but recorded in elf64 object) + this may not give the correct value in all contexts if the die + pointer is NULL. + If the die pointer is non-NULL (in which case it must point to + a valid DIE) this will return the correct size. +*/ +int +_dwarf_get_address_size(Dwarf_Debug dbg, Dwarf_Die die) +{ + Dwarf_CU_Context context = 0; + Dwarf_Half addrsize = 0; + if(!die) { + return dbg->de_pointer_size; + } + context = die->di_cu_context; + addrsize = context->cc_address_size; + return addrsize; +} + + + |