diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-10-20 14:42:03 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-10-20 14:42:03 +0400 |
commit | bb1c3da3c12651f1c408d96dd6d33ae157bdadd6 (patch) | |
tree | 4a535b35500684ac6a928bf0fd661325b5a04697 /libdwarf/dwarf_arange.c | |
download | dwarfutils-upstream.tar.gz |
Imported Upstream version 20120410upstream/20120410upstream
Diffstat (limited to 'libdwarf/dwarf_arange.c')
-rw-r--r-- | libdwarf/dwarf_arange.c | 616 |
1 files changed, 616 insertions, 0 deletions
diff --git a/libdwarf/dwarf_arange.c b/libdwarf/dwarf_arange.c new file mode 100644 index 0000000..fe74385 --- /dev/null +++ b/libdwarf/dwarf_arange.c @@ -0,0 +1,616 @@ +/* + + Copyright (C) 2000-2004 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_arange.h" +#include "dwarf_global.h" /* for _dwarf_fixup_* */ + + +/* Common code for two user-visible routines to share. + Errors here result in memory leaks, but errors here + are serious (making aranges unusable) so we assume + callers will not repeat the error often or mind the leaks. +*/ +static int +dwarf_get_aranges_list(Dwarf_Debug dbg, + Dwarf_Chain * chain_out, + Dwarf_Signed * chain_count_out, + Dwarf_Error * error) +{ + /* Sweeps through the arange. */ + Dwarf_Small *arange_ptr = 0; + Dwarf_Small *arange_ptr_start = 0; + + /* Start of arange header. Used for rounding offset of arange_ptr + to twice the tuple size. Libdwarf requirement. */ + Dwarf_Small *header_ptr = 0; + + /* Version of .debug_aranges header. */ + Dwarf_Half version = 0; + + /* Offset of current set of aranges into .debug_info. */ + Dwarf_Off info_offset = 0; + + /* Size in bytes of addresses in target. */ + Dwarf_Small address_size = 0; + + /* Size in bytes of segment offsets in target. */ + Dwarf_Small segment_size = 0; + + /* Count of total number of aranges. */ + Dwarf_Unsigned arange_count = 0; + + Dwarf_Arange arange = 0; + + /* Used to chain Dwarf_Aranges structs. */ + Dwarf_Chain curr_chain = NULL; + Dwarf_Chain prev_chain = NULL; + Dwarf_Chain head_chain = NULL; + if (!dbg->de_debug_aranges.dss_size) { + return (DW_DLV_NO_ENTRY); + } + + + arange_ptr = dbg->de_debug_aranges.dss_data; + arange_ptr_start = arange_ptr; + do { + /* Length of current set of aranges. */ + Dwarf_Unsigned length = 0; + Dwarf_Small remainder = 0; + Dwarf_Small *arange_ptr_past_end = 0; + Dwarf_Unsigned range_entry_size = 0; + + int local_length_size; + + /*REFERENCED*/ /* Not used in this instance of the macro */ + int local_extension_size = 0; + + header_ptr = arange_ptr; + + /* READ_AREA_LENGTH updates arange_ptr for consumed bytes */ + READ_AREA_LENGTH(dbg, length, Dwarf_Unsigned, + arange_ptr, local_length_size, + local_extension_size); + arange_ptr_past_end = arange_ptr + length; + + + READ_UNALIGNED(dbg, version, Dwarf_Half, + arange_ptr, sizeof(Dwarf_Half)); + arange_ptr += sizeof(Dwarf_Half); + length = length - sizeof(Dwarf_Half); + if (version != CURRENT_VERSION_STAMP) { + _dwarf_error(dbg, error, DW_DLE_VERSION_STAMP_ERROR); + return (DW_DLV_ERROR); + } + + READ_UNALIGNED(dbg, info_offset, Dwarf_Off, + arange_ptr, local_length_size); + arange_ptr += local_length_size; + length = length - local_length_size; + /* This applies to debug_info only, not to debug_types. */ + if (info_offset >= dbg->de_debug_info.dss_size) { + FIX_UP_OFFSET_IRIX_BUG(dbg, info_offset, + "arange info offset.a"); + if (info_offset >= dbg->de_debug_info.dss_size) { + _dwarf_error(dbg, error, DW_DLE_ARANGE_OFFSET_BAD); + return (DW_DLV_ERROR); + } + } + + address_size = *(Dwarf_Small *) arange_ptr; + if(address_size > sizeof(Dwarf_Addr)) { + _dwarf_error(dbg, error, DW_DLE_ADDRESS_SIZE_ERROR); + return DW_DLV_ERROR; + } + if(address_size == 0) { + _dwarf_error(dbg, error, DW_DLE_ADDRESS_SIZE_ERROR); + return DW_DLV_ERROR; + } + /* It is not an error if the sizes differ. + Unusual, but not an error. */ + arange_ptr = arange_ptr + sizeof(Dwarf_Small); + length = length - sizeof(Dwarf_Small); + + /* Even DWARF2 had a segment_size field here, meaning + size in bytes of a segment descriptor on the target + system. */ + segment_size = *(Dwarf_Small *) arange_ptr; + if(segment_size > sizeof(Dwarf_Addr)) { + _dwarf_error(dbg, error, DW_DLE_SEGMENT_SIZE_BAD); + return (DW_DLV_ERROR); + } + arange_ptr = arange_ptr + sizeof(Dwarf_Small); + length = length - sizeof(Dwarf_Small); + + range_entry_size = 2*address_size + segment_size; + /* Round arange_ptr offset to next multiple of address_size. */ + remainder = (Dwarf_Unsigned) (arange_ptr - header_ptr) % + (range_entry_size); + if (remainder != 0) { + arange_ptr = arange_ptr + (2 * address_size) - remainder; + length = length - ((2 * address_size) - remainder); + } + do { + Dwarf_Addr range_address = 0; + Dwarf_Unsigned segment_selector = 0; + Dwarf_Unsigned range_length = 0; + /* For segmented address spaces, the first field to + read is a segment selector (new in DWARF4). + Surprising since the segment_size was always there + in the table header! */ + if(version == 4 && segment_size != 0) { + READ_UNALIGNED(dbg, segment_selector, Dwarf_Unsigned, + arange_ptr, segment_size); + arange_ptr += address_size; + length = length - address_size; + } + + READ_UNALIGNED(dbg, range_address, Dwarf_Addr, + arange_ptr, address_size); + arange_ptr += address_size; + length = length - address_size; + + READ_UNALIGNED(dbg, range_length, Dwarf_Unsigned, + arange_ptr, address_size); + arange_ptr += address_size; + length = length - address_size; + + { + /* We used to suppress all-zero entries, but + now we return all aranges entries so we show + the entire content. March 31, 2010. */ + + arange = (Dwarf_Arange) + _dwarf_get_alloc(dbg, DW_DLA_ARANGE, 1); + if (arange == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + arange->ar_segment_selector = segment_selector; + arange->ar_segment_selector_size = segment_size; + arange->ar_address = range_address; + arange->ar_length = range_length; + arange->ar_info_offset = info_offset; + arange->ar_dbg = dbg; + arange_count++; + + curr_chain = (Dwarf_Chain) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (curr_chain == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + curr_chain->ch_item = arange; + if (head_chain == NULL) + head_chain = prev_chain = curr_chain; + else { + prev_chain->ch_next = curr_chain; + prev_chain = curr_chain; + } + } + /* The current set of ranges is terminated by + range_address 0 and range_length 0, but that + does not necessarily terminate the ranges for this CU! + There can be multiple sets in that DWARF + does not explicitly forbid multiple sets. + DWARF2,3,4 section 7.20 + We stop short to avoid overrun of the end of the CU. */ + + } while (arange_ptr_past_end >= (arange_ptr + range_entry_size)); + + /* A compiler could emit some padding bytes here. dwarf2/3 + (dwarf4 sec 7.20) does not clearly make extra padding + bytes illegal. */ + if (arange_ptr_past_end < arange_ptr) { + char buf[200]; + Dwarf_Unsigned pad_count = arange_ptr - arange_ptr_past_end; + Dwarf_Unsigned offset = arange_ptr - arange_ptr_start; + snprintf(buf,sizeof(buf),"DW_DLE_ARANGE_LENGTH_BAD." + " 0x%" DW_PR_XZEROS DW_PR_DUx + " pad bytes at offset 0x%" DW_PR_XZEROS DW_PR_DUx + " in .debug_aranges", + pad_count, offset); + dwarf_insert_harmless_error(dbg,buf); + } + /* For most compilers, arange_ptr == arange_ptr_past_end at + this point. But not if there were padding bytes */ + arange_ptr = arange_ptr_past_end; + } while (arange_ptr < + dbg->de_debug_aranges.dss_data + dbg->de_debug_aranges.dss_size); + + if (arange_ptr != + dbg->de_debug_aranges.dss_data + dbg->de_debug_aranges.dss_size) { + _dwarf_error(dbg, error, DW_DLE_ARANGE_DECODE_ERROR); + return (DW_DLV_ERROR); + } + *chain_out = head_chain; + *chain_count_out = arange_count; + return DW_DLV_OK; +} + +/* + This function returns the count of the number of + aranges in the .debug_aranges section. It sets + aranges to point to a block of Dwarf_Arange's + describing the arange's. It returns DW_DLV_ERROR + on error. + + Must be identical in most aspects to + dwarf_get_aranges_addr_offsets! + +*/ +int +dwarf_get_aranges(Dwarf_Debug dbg, + Dwarf_Arange ** aranges, + Dwarf_Signed * returned_count, Dwarf_Error * error) +{ + /* Count of total number of aranges. */ + Dwarf_Signed arange_count = 0; + + Dwarf_Arange *arange_block = 0; + + /* Used to chain Dwarf_Aranges structs. */ + Dwarf_Chain curr_chain = NULL; + Dwarf_Chain prev_chain = NULL; + Dwarf_Chain head_chain = NULL; + Dwarf_Unsigned i = 0; + int res = DW_DLV_ERROR; + + /* ***** BEGIN CODE ***** */ + + if (dbg == NULL) { + _dwarf_error(NULL, error, DW_DLE_DBG_NULL); + return (DW_DLV_ERROR); + } + + res = _dwarf_load_section(dbg, &dbg->de_debug_aranges, error); + if (res != DW_DLV_OK) { + return res; + } + + res = dwarf_get_aranges_list(dbg,&head_chain,&arange_count,error); + if(res != DW_DLV_OK) { + return res; + } + + arange_block = (Dwarf_Arange *) + _dwarf_get_alloc(dbg, DW_DLA_LIST, arange_count); + if (arange_block == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + curr_chain = head_chain; + for (i = 0; i < arange_count; i++) { + *(arange_block + i) = curr_chain->ch_item; + prev_chain = curr_chain; + curr_chain = curr_chain->ch_next; + dwarf_dealloc(dbg, prev_chain, DW_DLA_CHAIN); + } + + *aranges = arange_block; + *returned_count = (arange_count); + return DW_DLV_OK; +} + +/* + This function returns DW_DLV_OK if it succeeds + and DW_DLV_ERR or DW_DLV_OK otherwise. + count is set to the number of addresses in the + .debug_aranges section. + For each address, the corresponding element in + an array is set to the address itself(aranges) and + the section offset (offsets). + Must be identical in most aspects to + dwarf_get_aranges! +*/ +int +_dwarf_get_aranges_addr_offsets(Dwarf_Debug dbg, + Dwarf_Addr ** addrs, + Dwarf_Off ** offsets, + Dwarf_Signed * count, + Dwarf_Error * error) +{ + Dwarf_Unsigned i = 0; + + /* Used to chain Dwarf_Aranges structs. */ + Dwarf_Chain curr_chain = NULL; + Dwarf_Chain prev_chain = NULL; + Dwarf_Chain head_chain = NULL; + + Dwarf_Signed arange_count = 0; + Dwarf_Addr *arange_addrs = 0; + Dwarf_Off *arange_offsets = 0; + + int res = DW_DLV_ERROR; + + /* ***** BEGIN CODE ***** */ + + if (error != NULL) + *error = NULL; + + if (dbg == NULL) { + _dwarf_error(NULL, error, DW_DLE_DBG_NULL); + return (DW_DLV_ERROR); + } + + res = _dwarf_load_section(dbg, &dbg->de_debug_aranges,error); + if (res != DW_DLV_OK) { + return res; + } + + res = dwarf_get_aranges_list(dbg,&head_chain,&arange_count,error); + if(res != DW_DLV_OK) { + return res; + } + + arange_addrs = (Dwarf_Addr *) + _dwarf_get_alloc(dbg, DW_DLA_ADDR, arange_count); + if (arange_addrs == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + arange_offsets = (Dwarf_Off *) + _dwarf_get_alloc(dbg, DW_DLA_ADDR, arange_count); + if (arange_offsets == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + curr_chain = head_chain; + for (i = 0; i < arange_count; i++) { + Dwarf_Arange ar = curr_chain->ch_item; + + arange_addrs[i] = ar->ar_address; + arange_offsets[i] = ar->ar_info_offset; + prev_chain = curr_chain; + curr_chain = curr_chain->ch_next; + dwarf_dealloc(dbg, ar, DW_DLA_ARANGE); + dwarf_dealloc(dbg, prev_chain, DW_DLA_CHAIN); + } + *count = arange_count; + *offsets = arange_offsets; + *addrs = arange_addrs; + return (DW_DLV_OK); +} + + +/* + This function takes a pointer to a block + of Dwarf_Arange's, and a count of the + length of the block. It checks if the + given address is within the range of an + address range in the block. If yes, it + returns the appropriate Dwarf_Arange. + Otherwise, it returns DW_DLV_ERROR. +*/ +int +dwarf_get_arange(Dwarf_Arange * aranges, + Dwarf_Unsigned arange_count, + Dwarf_Addr address, + Dwarf_Arange * returned_arange, Dwarf_Error * error) +{ + Dwarf_Arange curr_arange = 0; + Dwarf_Unsigned i = 0; + + if (aranges == NULL) { + _dwarf_error(NULL, error, DW_DLE_ARANGES_NULL); + return (DW_DLV_ERROR); + } + for (i = 0; i < arange_count; i++) { + curr_arange = *(aranges + i); + if (address >= curr_arange->ar_address && + address < + curr_arange->ar_address + curr_arange->ar_length) { + *returned_arange = curr_arange; + return (DW_DLV_OK); + } + } + + return (DW_DLV_NO_ENTRY); +} + + +/* + This function takes an Dwarf_Arange, + and returns the offset of the first + die in the compilation-unit that the + arange belongs to. Returns DW_DLV_ERROR + on error. + + For an arange, the cu_die can only be from debug_info, + not debug_types, it seems. +*/ +int +dwarf_get_cu_die_offset(Dwarf_Arange arange, + Dwarf_Off * returned_offset, + Dwarf_Error * error) +{ + Dwarf_Debug dbg = 0; + Dwarf_Off offset = 0; + + if (arange == NULL) { + _dwarf_error(NULL, error, DW_DLE_ARANGE_NULL); + return (DW_DLV_ERROR); + } + dbg = arange->ar_dbg; + offset = arange->ar_info_offset; + /* This applies to debug_info only, not to debug_types. */ + if (!dbg->de_debug_info.dss_data) { + int res = _dwarf_load_debug_info(dbg, error); + + if (res != DW_DLV_OK) { + return res; + } + } + *returned_offset = offset + _dwarf_length_of_cu_header(dbg, offset, true); + return DW_DLV_OK; +} + +/* This function takes an Dwarf_Arange, + and returns the offset of the CU header + in the compilation-unit that the + arange belongs to. Returns DW_DLV_ERROR + on error. + Ensures .debug_info loaded so + the cu_offset is meaningful. */ +int +dwarf_get_arange_cu_header_offset(Dwarf_Arange arange, + Dwarf_Off * cu_header_offset_returned, + Dwarf_Error * error) +{ + Dwarf_Debug dbg = 0; + if (arange == NULL) { + _dwarf_error(NULL, error, DW_DLE_ARANGE_NULL); + return (DW_DLV_ERROR); + } + dbg = arange->ar_dbg; + /* This applies to debug_info only, not to debug_types. */ + /* Like dwarf_get_arange_info this ensures debug_info loaded: + the cu_header is in debug_info and will be used else + we would not call dwarf_get_arange_cu_header_offset. */ + if (!dbg->de_debug_info.dss_data) { + int res = _dwarf_load_debug_info(dbg, error); + if (res != DW_DLV_OK) { + return res; + } + } + *cu_header_offset_returned = arange->ar_info_offset; + return DW_DLV_OK; +} + + + + +/* + This function takes a Dwarf_Arange, and returns + true if it is not NULL. It also stores the start + address of the range in *start, the length of the + range in *length, and the offset of the first die + in the compilation-unit in *cu_die_offset. It + returns false on error. + If cu_die_offset returned ensures .debug_info loaded so + the cu_die_offset is meaningful. +*/ +int +dwarf_get_arange_info(Dwarf_Arange arange, + Dwarf_Addr * start, + Dwarf_Unsigned * length, + Dwarf_Off * cu_die_offset, Dwarf_Error * error) +{ + if (arange == NULL) { + _dwarf_error(NULL, error, DW_DLE_ARANGE_NULL); + return (DW_DLV_ERROR); + } + + if (start != NULL) + *start = arange->ar_address; + if (length != NULL) + *length = arange->ar_length; + if (cu_die_offset != NULL) { + Dwarf_Debug dbg = arange->ar_dbg; + Dwarf_Off offset = arange->ar_info_offset; + + /* This applies to debug_info only, not to debug_types. */ + if (!dbg->de_debug_info.dss_data) { + int res = _dwarf_load_debug_info(dbg, error); + if (res != DW_DLV_OK) { + return res; + } + } + *cu_die_offset = + offset + _dwarf_length_of_cu_header(dbg, offset,true); + } + return (DW_DLV_OK); +} + + +/* New for DWARF4, entries may have segment information. + *segment is only meaningful if *segment_entry_size is non-zero. */ +int +dwarf_get_arange_info_b(Dwarf_Arange arange, + Dwarf_Unsigned* segment, + Dwarf_Unsigned* segment_entry_size, + Dwarf_Addr * start, + Dwarf_Unsigned* length, + Dwarf_Off * cu_die_offset, + Dwarf_Error * error) +{ + if (arange == NULL) { + _dwarf_error(NULL, error, DW_DLE_ARANGE_NULL); + return (DW_DLV_ERROR); + } + + if(segment != NULL) { + *segment = arange->ar_segment_selector; + } + if(segment_entry_size != NULL) { + *segment_entry_size = arange->ar_segment_selector_size; + } + if (start != NULL) + *start = arange->ar_address; + if (length != NULL) + *length = arange->ar_length; + if (cu_die_offset != NULL) { + Dwarf_Debug dbg = arange->ar_dbg; + Dwarf_Off offset = arange->ar_info_offset; + + /* This applies to debug_info only, not to debug_types. */ + if (!dbg->de_debug_info.dss_data) { + int res = _dwarf_load_debug_info(dbg, error); + if (res != DW_DLV_OK) { + return res; + } + } + *cu_die_offset = + offset + _dwarf_length_of_cu_header(dbg, offset,true); + } + return (DW_DLV_OK); +} |