summaryrefslogtreecommitdiff
path: root/libdwarf/dwarf_die_deliv.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdwarf/dwarf_die_deliv.c')
-rw-r--r--libdwarf/dwarf_die_deliv.c1214
1 files changed, 1214 insertions, 0 deletions
diff --git a/libdwarf/dwarf_die_deliv.c b/libdwarf/dwarf_die_deliv.c
new file mode 100644
index 0000000..9ef5c45
--- /dev/null
+++ b/libdwarf/dwarf_die_deliv.c
@@ -0,0 +1,1214 @@
+/*
+
+ Copyright (C) 2000-2006 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"
+#ifdef HAVE_ELF_H
+#include <elf.h>
+#endif
+#include <stdio.h>
+#include "dwarf_die_deliv.h"
+
+
+static int
+dwarf_next_cu_header_internal(Dwarf_Debug dbg,
+ Dwarf_Bool is_info,
+ Dwarf_Unsigned * cu_header_length,
+ Dwarf_Half * version_stamp,
+ Dwarf_Unsigned * abbrev_offset,
+ Dwarf_Half * address_size,
+ Dwarf_Half * offset_size,
+ Dwarf_Half * extension_size,
+ Dwarf_Sig8 * signature,
+ Dwarf_Unsigned *typeoffset,
+ Dwarf_Unsigned * next_cu_offset,
+ Dwarf_Error * error);
+
+/* New October 2011. Enables client code to know if
+ it is a debug_info or debug_types context. */
+Dwarf_Bool
+dwarf_get_die_infotypes_flag(Dwarf_Die die)
+{
+ return die->di_is_info;
+}
+
+/*
+ For a given Dwarf_Debug dbg, this function checks
+ if a CU that includes the given offset has been read
+ or not. If yes, it returns the Dwarf_CU_Context
+ for the CU. Otherwise it returns NULL. Being an
+ internal routine, it is assumed that a valid dbg
+ is passed.
+
+ **This is a sequential search. May be too slow.
+
+ If debug_info and debug_abbrev not loaded, this will
+ wind up returning NULL. So no need to load before calling
+ this.
+*/
+static Dwarf_CU_Context
+_dwarf_find_CU_Context(Dwarf_Debug dbg, Dwarf_Off offset,Dwarf_Bool is_info)
+{
+ Dwarf_CU_Context cu_context = 0;
+ Dwarf_Debug_InfoTypes dis = is_info? &dbg->de_info_reading:
+ &dbg->de_types_reading;
+
+ if (offset >= dis->de_last_offset)
+ return (NULL);
+
+ if (dis->de_cu_context != NULL &&
+ dis->de_cu_context->cc_next != NULL &&
+ dis->de_cu_context->cc_next->cc_debug_offset == offset) {
+
+ return (dis->de_cu_context->cc_next);
+ }
+
+ if (dis->de_cu_context != NULL &&
+ dis->de_cu_context->cc_debug_offset <= offset) {
+
+ for (cu_context = dis->de_cu_context;
+ cu_context != NULL; cu_context = cu_context->cc_next) {
+
+ if (offset >= cu_context->cc_debug_offset &&
+ offset < cu_context->cc_debug_offset +
+ cu_context->cc_length + cu_context->cc_length_size
+ + cu_context->cc_extension_size) {
+
+ return (cu_context);
+ }
+ }
+ }
+
+ for (cu_context = dis->de_cu_context_list;
+ cu_context != NULL; cu_context = cu_context->cc_next) {
+
+ if (offset >= cu_context->cc_debug_offset &&
+ offset < cu_context->cc_debug_offset +
+ cu_context->cc_length + cu_context->cc_length_size
+ + cu_context->cc_extension_size) {
+
+ return (cu_context);
+ }
+ }
+
+ return (NULL);
+}
+
+
+/* This routine checks the dwarf_offdie() list of
+ CU contexts for the right CU context. */
+static Dwarf_CU_Context
+_dwarf_find_offdie_CU_Context(Dwarf_Debug dbg, Dwarf_Off offset,
+ Dwarf_Bool is_info)
+{
+ Dwarf_CU_Context cu_context = 0;
+ Dwarf_Debug_InfoTypes dis = is_info? &dbg->de_info_reading:
+ &dbg->de_types_reading;
+
+ for (cu_context = dis->de_offdie_cu_context;
+ cu_context != NULL; cu_context = cu_context->cc_next)
+
+ if (offset >= cu_context->cc_debug_offset &&
+ offset < cu_context->cc_debug_offset +
+ cu_context->cc_length + cu_context->cc_length_size
+ + cu_context->cc_extension_size)
+
+ return (cu_context);
+
+ return (NULL);
+}
+
+
+/* This function is used to create a CU Context for
+ a compilation-unit that begins at offset in
+ .debug_info. The CU Context is attached to the
+ list of CU Contexts for this dbg. It is assumed
+ that the CU at offset has not been read before,
+ and so do not call this routine before making
+ sure of this with _dwarf_find_CU_Context().
+ Returns NULL on error. As always, being an
+ internal routine, assumes a good dbg.
+
+ This function must always set a dwarf error code
+ before returning NULL. Always. */
+static Dwarf_CU_Context
+_dwarf_make_CU_Context(Dwarf_Debug dbg,
+ Dwarf_Off offset,Dwarf_Bool is_info,Dwarf_Error * error)
+{
+ Dwarf_CU_Context cu_context = 0;
+ Dwarf_Unsigned length = 0;
+ Dwarf_Signed abbrev_offset = 0;
+ Dwarf_Unsigned typeoffset = 0;
+ Dwarf_Sig8 signaturedata;
+ Dwarf_Unsigned types_extra_len = 0;
+ Dwarf_Byte_Ptr cu_ptr = 0;
+ int local_extension_size = 0;
+ int local_length_size = 0;
+ Dwarf_Debug_InfoTypes dis = is_info? &dbg->de_info_reading:
+ &dbg->de_types_reading;
+ Dwarf_Unsigned section_size = is_info? dbg->de_debug_info.dss_size:
+ dbg->de_debug_types.dss_size;
+
+ cu_context =
+ (Dwarf_CU_Context) _dwarf_get_alloc(dbg, DW_DLA_CU_CONTEXT, 1);
+ if (cu_context == NULL) {
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (NULL);
+ }
+ cu_context->cc_dbg = dbg;
+ cu_context->cc_is_info = is_info;
+
+ {
+ Dwarf_Small *dataptr = is_info? dbg->de_debug_info.dss_data:
+ dbg->de_debug_types.dss_data;
+ cu_ptr = (Dwarf_Byte_Ptr) (dataptr+offset);
+ }
+
+ /* READ_AREA_LENGTH updates cu_ptr for consumed bytes */
+ READ_AREA_LENGTH(dbg, length, Dwarf_Unsigned,
+ cu_ptr, local_length_size, local_extension_size);
+ cu_context->cc_length_size = local_length_size;
+ cu_context->cc_extension_size = local_extension_size;
+
+
+ cu_context->cc_length = (Dwarf_Word) length;
+
+ READ_UNALIGNED(dbg, cu_context->cc_version_stamp, Dwarf_Half,
+ cu_ptr, sizeof(Dwarf_Half));
+ cu_ptr += sizeof(Dwarf_Half);
+
+ READ_UNALIGNED(dbg, abbrev_offset, Dwarf_Signed,
+ cu_ptr, local_length_size);
+ cu_ptr += local_length_size;
+ cu_context->cc_abbrev_offset = (Dwarf_Sword) abbrev_offset;
+
+ cu_context->cc_address_size = *(Dwarf_Small *) cu_ptr;
+ ++cu_ptr;
+
+
+
+ if(cu_context->cc_address_size > sizeof(Dwarf_Addr)) {
+ _dwarf_error(dbg, error, DW_DLE_CU_ADDRESS_SIZE_BAD);
+ return (NULL);
+ }
+ if(!is_info) {
+ /* debug_types CU headers have extra header bytes. */
+ types_extra_len = sizeof(signaturedata) + local_length_size;
+ }
+ if ((length < (CU_VERSION_STAMP_SIZE + local_length_size +
+ CU_ADDRESS_SIZE_SIZE + types_extra_len)) ||
+ ((offset + length + local_length_size + local_extension_size) >
+ section_size)) {
+
+ dwarf_dealloc(dbg, cu_context, DW_DLA_CU_CONTEXT);
+ _dwarf_error(dbg, error, DW_DLE_CU_LENGTH_ERROR);
+ return (NULL);
+ }
+
+ if (cu_context->cc_version_stamp != CURRENT_VERSION_STAMP
+ && cu_context->cc_version_stamp != CURRENT_VERSION_STAMP3
+ && cu_context->cc_version_stamp != CURRENT_VERSION_STAMP4) {
+ dwarf_dealloc(dbg, cu_context, DW_DLA_CU_CONTEXT);
+ _dwarf_error(dbg, error, DW_DLE_VERSION_STAMP_ERROR);
+ return (NULL);
+ }
+ if (!is_info) {
+ if (cu_context->cc_version_stamp != CURRENT_VERSION_STAMP4) {
+ dwarf_dealloc(dbg, cu_context, DW_DLA_CU_CONTEXT);
+ _dwarf_error(dbg, error, DW_DLE_DEBUG_TYPES_ONLY_DWARF4);
+ return (NULL);
+ }
+ /* Now read the debug_types extra header fields of
+ the signature (8 bytes) and the typeoffset. */
+ memcpy(&signaturedata,cu_ptr,sizeof(signaturedata));
+ cu_ptr += sizeof(signaturedata);
+ READ_UNALIGNED(dbg, typeoffset, Dwarf_Unsigned,
+ cu_ptr, local_length_size);
+ cu_context->cc_typeoffset = typeoffset;
+ cu_context->cc_signature = signaturedata;
+ {
+ Dwarf_Unsigned cu_len = length - (local_length_size +
+ local_extension_size);
+ if(typeoffset >= cu_len) {
+ dwarf_dealloc(dbg, cu_context, DW_DLA_CU_CONTEXT);
+ _dwarf_error(dbg, error, DW_DLE_DEBUG_TYPEOFFSET_BAD);
+ return (NULL);
+ }
+ }
+ }
+
+ if (abbrev_offset >= dbg->de_debug_abbrev.dss_size) {
+ dwarf_dealloc(dbg, cu_context, DW_DLA_CU_CONTEXT);
+ _dwarf_error(dbg, error, DW_DLE_ABBREV_OFFSET_ERROR);
+ return (NULL);
+ }
+
+ cu_context->cc_abbrev_hash_table =
+ (Dwarf_Hash_Table) _dwarf_get_alloc(dbg, DW_DLA_HASH_TABLE, 1);
+ if (cu_context->cc_abbrev_hash_table == NULL) {
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (NULL);
+ }
+
+ cu_context->cc_debug_offset = (Dwarf_Word) offset;
+
+ dis->de_last_offset = (Dwarf_Word) (offset + length +
+ local_extension_size + local_length_size);
+
+ if (dis->de_cu_context_list == NULL) {
+ dis->de_cu_context_list = cu_context;
+ dis->de_cu_context_list_end = cu_context;
+ } else {
+ dis->de_cu_context_list_end->cc_next = cu_context;
+ dis->de_cu_context_list_end = cu_context;
+ }
+
+ return (cu_context);
+}
+
+static int
+reloc_incomplete(Dwarf_Error err)
+{
+ int e = dwarf_errno(err);
+ if( e == DW_DLE_RELOC_MISMATCH_INDEX ||
+ e == DW_DLE_RELOC_MISMATCH_RELOC_INDEX ||
+ e == DW_DLE_RELOC_MISMATCH_STRTAB_INDEX ||
+ e == DW_DLE_RELOC_SECTION_MISMATCH ||
+ e == DW_DLE_RELOC_SECTION_MISSING_INDEX ||
+ e == DW_DLE_RELOC_SECTION_LENGTH_ODD ||
+ e == DW_DLE_RELOC_SECTION_PTR_NULL ||
+ e == DW_DLE_RELOC_SECTION_MALLOC_FAIL ||
+ e == DW_DLE_RELOC_SECTION_SYMBOL_INDEX_BAD ) {
+ return 1;
+ }
+ return 0;
+}
+
+
+
+/* Returns offset of next compilation-unit thru next_cu_offset
+ pointer.
+ It sequentially moves from one
+ cu to the next. The current cu is recorded
+ internally by libdwarf.
+
+ The _b form is new for DWARF4 adding new returned fields. */
+int
+dwarf_next_cu_header(Dwarf_Debug dbg,
+ Dwarf_Unsigned * cu_header_length,
+ Dwarf_Half * version_stamp,
+ Dwarf_Unsigned * abbrev_offset,
+ Dwarf_Half * address_size,
+ Dwarf_Unsigned * next_cu_offset,
+ Dwarf_Error * error)
+{
+ Dwarf_Bool is_info = true;
+ return dwarf_next_cu_header_internal(dbg,
+ is_info,
+ cu_header_length,
+ version_stamp,
+ abbrev_offset,
+ address_size,
+ 0,0,0,0,
+ next_cu_offset,
+ error);
+}
+int
+dwarf_next_cu_header_b(Dwarf_Debug dbg,
+ Dwarf_Unsigned * cu_header_length,
+ Dwarf_Half * version_stamp,
+ Dwarf_Unsigned * abbrev_offset,
+ Dwarf_Half * address_size,
+ Dwarf_Half * offset_size,
+ Dwarf_Half * extension_size,
+ Dwarf_Unsigned * next_cu_offset,
+ Dwarf_Error * error)
+{
+ Dwarf_Bool is_info = true;
+ return dwarf_next_cu_header_internal(dbg,
+ is_info,
+ cu_header_length,
+ version_stamp,
+ abbrev_offset,
+ address_size,
+ offset_size,extension_size,
+ 0,0,
+ next_cu_offset,
+ error);
+}
+
+int
+dwarf_next_cu_header_c(Dwarf_Debug dbg,
+ Dwarf_Bool is_info,
+ Dwarf_Unsigned * cu_header_length,
+ Dwarf_Half * version_stamp,
+ Dwarf_Unsigned * abbrev_offset,
+ Dwarf_Half * address_size,
+ Dwarf_Half * offset_size,
+ Dwarf_Half * extension_size,
+ Dwarf_Sig8 * signature,
+ Dwarf_Unsigned * typeoffset,
+ Dwarf_Unsigned * next_cu_offset,
+ Dwarf_Error * error)
+{
+ return dwarf_next_cu_header_internal(dbg,
+ is_info,
+ cu_header_length,
+ version_stamp,
+ abbrev_offset,
+ address_size,
+ offset_size,
+ extension_size,
+ signature,
+ typeoffset,
+ next_cu_offset,
+ error);
+}
+static int
+dwarf_next_cu_header_internal(Dwarf_Debug dbg,
+ Dwarf_Bool is_info,
+ Dwarf_Unsigned * cu_header_length,
+ Dwarf_Half * version_stamp,
+ Dwarf_Unsigned * abbrev_offset,
+ Dwarf_Half * address_size,
+ Dwarf_Half * offset_size,
+ Dwarf_Half * extension_size,
+ Dwarf_Sig8 * signature,
+ Dwarf_Unsigned *typeoffset,
+ Dwarf_Unsigned * next_cu_offset,
+ Dwarf_Error * error)
+{
+ /* Offset for current and new CU. */
+ Dwarf_Unsigned new_offset = 0;
+
+ /* CU Context for current CU. */
+ Dwarf_CU_Context cu_context = 0;
+ Dwarf_Debug_InfoTypes dis = 0;
+ Dwarf_Unsigned section_size = 0;
+
+
+
+ /* ***** BEGIN CODE ***** */
+
+ if (dbg == NULL) {
+ _dwarf_error(NULL, error, DW_DLE_DBG_NULL);
+ return (DW_DLV_ERROR);
+ }
+ dis = is_info? &dbg->de_info_reading: &dbg->de_types_reading;
+ /* Get offset into .debug_info of next CU. If dbg has no context,
+ this has to be the first one. */
+ if (dis->de_cu_context == NULL) {
+ Dwarf_Small *dataptr = is_info? dbg->de_debug_info.dss_data:
+ dbg->de_debug_types.dss_data;
+ new_offset = 0;
+ if (!dataptr) {
+ Dwarf_Error err2= 0;
+ int res = is_info?_dwarf_load_debug_info(dbg, &err2):
+ _dwarf_load_debug_types(dbg,&err2);
+
+ if (res != DW_DLV_OK) {
+ if(reloc_incomplete(err2)) {
+ /* We will assume all is ok, though it is not.
+ Relocation errors need not be fatal. */
+ char msg_buf[200];
+ snprintf(msg_buf,sizeof(msg_buf),
+ "Relocations did not complete successfully, but we are "
+ " ignoring error: %s",dwarf_errmsg(err2));
+ dwarf_insert_harmless_error(dbg,msg_buf);
+ res = DW_DLV_OK;
+ } else {
+ if( error) {
+ *error = err2;
+ }
+ return res;
+ }
+
+ }
+ }
+
+ } else {
+ new_offset = dis->de_cu_context->cc_debug_offset +
+ dis->de_cu_context->cc_length +
+ dis->de_cu_context->cc_length_size +
+ dis->de_cu_context->cc_extension_size;
+ }
+
+ /* Check that there is room in .debug_info beyond the new offset
+ for at least a new cu header. If not, return 0 to indicate end
+ of debug_info section, and reset de_cu_debug_info_offset to
+ enable looping back through the cu's. */
+ section_size = is_info? dbg->de_debug_info.dss_size:
+ dbg->de_debug_types.dss_size;
+ if ((new_offset + _dwarf_length_of_cu_header_simple(dbg,is_info)) >=
+ section_size) {
+ dis->de_cu_context = NULL;
+ return (DW_DLV_NO_ENTRY);
+ }
+
+ /* Check if this CU has been read before. */
+ cu_context = _dwarf_find_CU_Context(dbg, new_offset,is_info);
+
+ /* If not, make CU Context for it. */
+ if (cu_context == NULL) {
+ cu_context = _dwarf_make_CU_Context(dbg, new_offset,is_info, error);
+ if (cu_context == NULL) {
+ /* Error if CU Context could not be made. Since
+ _dwarf_make_CU_Context has already registered an error
+ we do not do that here: we let the lower error pass
+ thru. */
+ return (DW_DLV_ERROR);
+ }
+ }
+
+ dis->de_cu_context = cu_context;
+
+ if (cu_header_length != NULL) {
+ *cu_header_length = cu_context->cc_length;
+ }
+
+ if (version_stamp != NULL) {
+ *version_stamp = cu_context->cc_version_stamp;
+ }
+ if (abbrev_offset != NULL) {
+ *abbrev_offset = cu_context->cc_abbrev_offset;
+ }
+
+ if (address_size != NULL) {
+ *address_size = cu_context->cc_address_size;
+ }
+ if (offset_size != NULL) {
+ *offset_size = cu_context->cc_length_size;
+ }
+ if (extension_size != NULL) {
+ *extension_size = cu_context->cc_extension_size;
+ }
+ if(!is_info) {
+ if(signature) {
+ *signature = cu_context->cc_signature;
+ }
+ if(typeoffset) {
+ *typeoffset = cu_context->cc_typeoffset;
+ }
+ }
+
+ new_offset = new_offset + cu_context->cc_length +
+ cu_context->cc_length_size + cu_context->cc_extension_size;
+ *next_cu_offset = new_offset;
+ return (DW_DLV_OK);
+}
+
+static int
+dwarf_ptr_CU_offset(Dwarf_CU_Context cu_context,
+ Dwarf_Byte_Ptr di_ptr,
+ Dwarf_Bool is_info,
+ Dwarf_Off * cu_off)
+{
+ Dwarf_Debug dbg = cu_context->cc_dbg;
+ Dwarf_Small *dataptr = is_info? dbg->de_debug_info.dss_data:
+ dbg->de_debug_types.dss_data;
+ *cu_off = (di_ptr - dataptr);
+ return DW_DLV_OK;
+}
+#if 0
+/* Just for debug purposes */
+void print_sib_offset(Dwarf_Die sibling)
+{
+ Dwarf_Off sib_off;
+ Dwarf_Error error;
+ dwarf_dieoffset(sibling,&sib_off,&error);
+ fprintf(stderr," SIB OFF = 0x%" DW_PR_XZEROS DW_PR_DUx,sib_off);
+}
+void print_ptr_offset(Dwarf_CU_Context cu_context,Dwarf_Byte_Ptr di_ptr)
+{
+ Dwarf_Off ptr_off;
+ dwarf_ptr_CU_offset(cu_context,di_ptr,&ptr_off);
+ fprintf(stderr," PTR OFF = 0x%" DW_PR_XZEROS DW_PR_DUx,ptr_off);
+}
+#endif
+
+
+/* Validate the sibling DIE. This only makes sense to call
+ if the sibling's DIEs have been travsersed and
+ dwarf_child() called on each,
+ so that the last DIE dwarf_child saw was the last.
+ Essentially ensuring that (after such traversal) that we
+ are in the same place a sibling attribute would identify.
+ In case we return DW_DLV_ERROR, the global offset of the last
+ DIE traversed by dwarf_child is returned through *offset
+
+ It is essentially guaranteed that dbg->de_last_die
+ is a stale DIE pointer of a deallocated DIE when we get here.
+ It must not be used as a DIE pointer here,
+ just as a sort of anonymous pointer that we just check against
+ NULL.
+
+ There is a (subtle?) dependence on the fact that when we call this
+ the last dwarf_child() call would have been for this sibling.
+ Meaning that this works in a depth-first traversal even though there
+ is no stack of 'de_last_die' values.
+
+ The check for dbg->de_last_die just ensures sanity.
+
+ If one is switching between normal debug_frame and eh_frame
+ (traversing them in tandem, let us say) in a single
+ Dwarf_Debug this validator makes no sense.
+ It works if one processes a .debug_frame (entirely) and
+ then an eh_frame (or vice versa) though.
+ Use caution.
+*/
+int
+dwarf_validate_die_sibling(Dwarf_Die sibling,Dwarf_Off *offset)
+{
+ Dwarf_Debug dbg = 0;
+ Dwarf_Error *error = 0;
+ Dwarf_Debug_InfoTypes dis = 0;
+ CHECK_DIE(sibling, DW_DLV_ERROR);
+ dbg = sibling->di_cu_context->cc_dbg;
+
+ dis = sibling->di_is_info? &dbg->de_info_reading: &dbg->de_types_reading;
+
+ *offset = 0;
+ if (dis->de_last_die && dis->de_last_di_ptr) {
+ if (sibling->di_debug_ptr == dis->de_last_di_ptr) {
+ return (DW_DLV_OK);
+ }
+ }
+ /* Calculate global offset used for error reporting */
+ dwarf_ptr_CU_offset(sibling->di_cu_context,
+ dis->de_last_di_ptr,sibling->di_is_info,offset);
+ return (DW_DLV_ERROR);
+}
+
+/* This function does two slightly different things
+ depending on the input flag want_AT_sibling. If
+ this flag is true, it checks if the input die has
+ a DW_AT_sibling attribute. If it does it returns
+ a pointer to the start of the sibling die in the
+ .debug_info section. Otherwise it behaves the
+ same as the want_AT_sibling false case.
+
+ If the want_AT_sibling flag is false, it returns
+ a pointer to the immediately adjacent die in the
+ .debug_info section.
+
+ Die_info_end points to the end of the .debug_info
+ portion for the cu the die belongs to. It is used
+ to check that the search for the next die does not
+ cross the end of the current cu. Cu_info_start points
+ to the start of the .debug_info portion for the
+ current cu, and is used to add to the offset for
+ DW_AT_sibling attributes. Finally, has_die_child
+ is a pointer to a Dwarf_Bool that is set true if
+ the present die has children, false otherwise.
+ However, in case want_AT_child is true and the die
+ has a DW_AT_sibling attribute *has_die_child is set
+ false to indicate that the children are being skipped.
+
+ die_info_end points to the last byte+1 of the cu. */
+static Dwarf_Byte_Ptr
+_dwarf_next_die_info_ptr(Dwarf_Byte_Ptr die_info_ptr,
+ Dwarf_CU_Context cu_context,
+ Dwarf_Byte_Ptr die_info_end,
+ Dwarf_Byte_Ptr cu_info_start,
+ Dwarf_Bool want_AT_sibling,
+ Dwarf_Bool * has_die_child)
+{
+ Dwarf_Byte_Ptr info_ptr = 0;
+ Dwarf_Byte_Ptr abbrev_ptr = 0;
+ Dwarf_Word abbrev_code = 0;
+ Dwarf_Abbrev_List abbrev_list;
+ Dwarf_Half attr = 0;
+ Dwarf_Half attr_form = 0;
+ Dwarf_Unsigned offset = 0;
+ Dwarf_Word leb128_length = 0;
+ Dwarf_Unsigned utmp = 0;
+ Dwarf_Debug dbg = 0;
+
+ info_ptr = die_info_ptr;
+ DECODE_LEB128_UWORD(info_ptr, utmp);
+ abbrev_code = (Dwarf_Word) utmp;
+ if (abbrev_code == 0) {
+ return NULL;
+ }
+
+
+ abbrev_list = _dwarf_get_abbrev_for_code(cu_context, abbrev_code);
+ if (abbrev_list == NULL) {
+ return (NULL);
+ }
+ dbg = cu_context->cc_dbg;
+
+ *has_die_child = abbrev_list->ab_has_child;
+
+ abbrev_ptr = abbrev_list->ab_abbrev_ptr;
+ do {
+ Dwarf_Unsigned utmp2;
+
+ DECODE_LEB128_UWORD(abbrev_ptr, utmp2);
+ attr = (Dwarf_Half) utmp2;
+ DECODE_LEB128_UWORD(abbrev_ptr, utmp2);
+ attr_form = (Dwarf_Half) utmp2;
+ if (attr_form == DW_FORM_indirect) {
+ Dwarf_Unsigned utmp6;
+
+ /* DECODE_LEB128_UWORD updates info_ptr */
+ DECODE_LEB128_UWORD(info_ptr, utmp6);
+ attr_form = (Dwarf_Half) utmp6;
+
+ }
+
+ if (want_AT_sibling && attr == DW_AT_sibling) {
+ switch (attr_form) {
+ case DW_FORM_ref1:
+ offset = *(Dwarf_Small *) info_ptr;
+ break;
+ case DW_FORM_ref2:
+ /* READ_UNALIGNED does not update info_ptr */
+ READ_UNALIGNED(dbg, offset, Dwarf_Unsigned,
+ info_ptr, sizeof(Dwarf_Half));
+ break;
+ case DW_FORM_ref4:
+ READ_UNALIGNED(dbg, offset, Dwarf_Unsigned,
+ info_ptr, sizeof(Dwarf_ufixed));
+ break;
+ case DW_FORM_ref8:
+ READ_UNALIGNED(dbg, offset, Dwarf_Unsigned,
+ info_ptr, sizeof(Dwarf_Unsigned));
+ break;
+ case DW_FORM_ref_udata:
+ offset =
+ _dwarf_decode_u_leb128(info_ptr, &leb128_length);
+ break;
+ case DW_FORM_ref_addr:
+ /* Very unusual. The FORM is intended to refer to
+ a different CU, but a different CU cannot
+ be a sibling, can it?
+ We could ignore this and treat as if no DW_AT_sibling
+ present. Or derive the offset from it and if
+ it is in the same CU use it directly.
+ The offset here is *supposed* to be a global offset,
+ so adding cu_info_start is wrong to any offset
+ we find here unless cu_info_start
+ is zero! Lets pretend there is no DW_AT_sibling
+ attribute. */
+ goto no_sibling_attr;
+ default:
+ return (NULL);
+ }
+
+ /* Reset *has_die_child to indicate children skipped. */
+ *has_die_child = false;
+
+ /* A value beyond die_info_end indicates an error. Exactly
+ at die_info_end means 1-past-cu-end and simply means we
+ are at the end, do not return NULL. Higher level code
+ will detect that we are at the end. */
+ if (cu_info_start + offset > die_info_end) {
+ /* Error case, bad DWARF. */
+ return (NULL);
+ }
+ /* At or before end-of-cu */
+ return (cu_info_start + offset);
+ }
+
+ no_sibling_attr:
+ if (attr_form != 0) {
+ info_ptr += _dwarf_get_size_of_val(cu_context->cc_dbg,
+ attr_form,
+ cu_context->cc_address_size,
+ info_ptr,
+ cu_context->cc_length_size);
+ /* It is ok for info_ptr == die_info_end, as we will test
+ later before using a too-large info_ptr */
+ if (info_ptr > die_info_end) {
+ /* More than one-past-end indicates a bug somewhere,
+ likely bad dwarf generation. */
+ return (NULL);
+ }
+ }
+ } while (attr != 0 || attr_form != 0);
+ return (info_ptr);
+}
+
+/* Multiple TAGs are in fact compile units.
+ Allow them all.
+ Return non-zero if a CU tag.
+ Else return 0.
+*/
+static int
+is_cu_tag(int t)
+{
+ if(t == DW_TAG_compile_unit ||
+ t == DW_TAG_partial_unit ||
+ t == DW_TAG_imported_unit ||
+ t == DW_TAG_type_unit) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Given a Dwarf_Debug dbg, and a Dwarf_Die die, it returns
+ a Dwarf_Die for the sibling of die. In case die is NULL,
+ it returns (thru ptr) a Dwarf_Die for the first die in the current
+ cu in dbg. Returns DW_DLV_ERROR on error.
+
+ It is assumed that every sibling chain including those with
+ only one element is terminated with a NULL die, except a
+ chain with only a NULL die.
+
+ The algorithm moves from one die to the adjacent one. It
+ returns when the depth of children it sees equals the number
+ of sibling chain terminations. A single count, child_depth
+ is used to track the depth of children and sibling terminations
+ encountered. Child_depth is incremented when a die has the
+ Has-Child flag set unless the child happens to be a NULL die.
+ Child_depth is decremented when a die has Has-Child false,
+ and the adjacent die is NULL. Algorithm returns when
+ child_depth is 0.
+
+ **NOTE: Do not modify input die, since it is used at the end. */
+int
+dwarf_siblingof(Dwarf_Debug dbg,
+ Dwarf_Die die,
+ Dwarf_Die * caller_ret_die, Dwarf_Error * error)
+{
+ Dwarf_Bool is_info = true;
+ return dwarf_siblingof_b(dbg,die,is_info,caller_ret_die,error);
+}
+/* This is the new form, October 2011. On calling with 'die' NULL,
+ we cannot tell if this is debug_info or debug_types, so
+ we must be informed!. */
+int
+dwarf_siblingof_b(Dwarf_Debug dbg,
+ Dwarf_Die die,
+ Dwarf_Bool is_info,
+ Dwarf_Die * caller_ret_die, Dwarf_Error * error)
+{
+ Dwarf_Die ret_die = 0;
+ Dwarf_Byte_Ptr die_info_ptr = 0;
+ Dwarf_Byte_Ptr cu_info_start = 0;
+
+ /* die_info_end points 1-past end of die (once set) */
+ Dwarf_Byte_Ptr die_info_end = 0;
+ Dwarf_Word abbrev_code = 0;
+ Dwarf_Unsigned utmp = 0;
+ /* Since die may be NULL, we rely on the input argument. */
+ Dwarf_Debug_InfoTypes dis = is_info? &dbg->de_info_reading:
+ &dbg->de_types_reading;
+ Dwarf_Small *dataptr = is_info? dbg->de_debug_info.dss_data:
+ dbg->de_debug_types.dss_data;
+
+
+
+ if (dbg == NULL) {
+ _dwarf_error(NULL, error, DW_DLE_DBG_NULL);
+ return (DW_DLV_ERROR);
+ }
+
+ if (die == NULL) {
+ /* Find root die of cu */
+ /* die_info_end is untouched here, need not be set in this
+ branch. */
+ Dwarf_Off off2;
+ Dwarf_CU_Context context=0;
+
+ /* If we've not loaded debug_info, de_cu_context will be NULL,
+ so no need to laod */
+
+ context = dis->de_cu_context;
+ if (context == NULL) {
+ _dwarf_error(dbg, error, DW_DLE_DBG_NO_CU_CONTEXT);
+ return (DW_DLV_ERROR);
+ }
+
+ off2 = context->cc_debug_offset;
+ cu_info_start = dataptr + off2;
+ die_info_ptr = cu_info_start +
+ _dwarf_length_of_cu_header(dbg, off2,is_info);
+ die_info_end = cu_info_start + context->cc_length +
+ context->cc_length_size +
+ context->cc_extension_size;
+ } else {
+ /* Find sibling die. */
+ Dwarf_Bool has_child = false;
+ Dwarf_Sword child_depth = 0;
+ Dwarf_CU_Context context=0;
+
+ /* We cannot have a legal die unless debug_info was loaded, so
+ no need to load debug_info here. */
+ CHECK_DIE(die, DW_DLV_ERROR);
+
+ die_info_ptr = die->di_debug_ptr;
+ if (*die_info_ptr == 0) {
+ return (DW_DLV_NO_ENTRY);
+ }
+ context = die->di_cu_context;
+ cu_info_start = dataptr+ context->cc_debug_offset;
+ die_info_end = cu_info_start + context->cc_length +
+ context->cc_length_size +
+ context->cc_extension_size;
+
+ if ((*die_info_ptr) == 0) {
+ return (DW_DLV_NO_ENTRY);
+ }
+ child_depth = 0;
+ do {
+ die_info_ptr = _dwarf_next_die_info_ptr(die_info_ptr,
+ die->di_cu_context, die_info_end,
+ cu_info_start, true, &has_child);
+ if (die_info_ptr == NULL) {
+ _dwarf_error(dbg, error, DW_DLE_NEXT_DIE_PTR_NULL);
+ return (DW_DLV_ERROR);
+ }
+
+ /* die_info_end is one past end. Do not read it!
+ A test for ``!= die_info_end'' would work as well,
+ but perhaps < reads more like the meaning. */
+ if(die_info_ptr < die_info_end) {
+ if ((*die_info_ptr) == 0 && has_child) {
+ die_info_ptr++;
+ has_child = false;
+ }
+ }
+
+ /* die_info_ptr can be one-past-end. */
+ if ((die_info_ptr == die_info_end) ||
+ ((*die_info_ptr) == 0)) {
+ for (; child_depth > 0 && *die_info_ptr == 0;
+ child_depth--, die_info_ptr++);
+ } else {
+ child_depth = has_child ? child_depth + 1 : child_depth;
+ }
+
+ } while (child_depth != 0);
+ }
+
+ /* die_info_ptr > die_info_end is really a bug (possibly in dwarf
+ generation)(but we are past end, no more DIEs here), whereas
+ die_info_ptr == die_info_end means 'one past end, no more DIEs
+ here'. */
+ if (die_info_ptr >= die_info_end) {
+ return (DW_DLV_NO_ENTRY);
+ }
+
+ if ((*die_info_ptr) == 0) {
+ return (DW_DLV_NO_ENTRY);
+ }
+
+ ret_die = (Dwarf_Die) _dwarf_get_alloc(dbg, DW_DLA_DIE, 1);
+ if (ret_die == NULL) {
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (DW_DLV_ERROR);
+ }
+
+ ret_die->di_is_info = is_info;
+ ret_die->di_debug_ptr = die_info_ptr;
+ ret_die->di_cu_context =
+ die == NULL ? dis->de_cu_context : die->di_cu_context;
+
+ DECODE_LEB128_UWORD(die_info_ptr, utmp);
+ if (die_info_ptr > die_info_end) {
+ /* We managed to go past the end of the CU!.
+ Something is badly wrong. */
+ dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
+ _dwarf_error(dbg, error, DW_DLE_ABBREV_DECODE_ERROR);
+ return (DW_DLV_ERROR);
+ }
+ abbrev_code = (Dwarf_Word) utmp;
+ if (abbrev_code == 0) {
+ /* Zero means a null DIE */
+ dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
+ return (DW_DLV_NO_ENTRY);
+ }
+ ret_die->di_abbrev_code = abbrev_code;
+ ret_die->di_abbrev_list =
+ _dwarf_get_abbrev_for_code(ret_die->di_cu_context, abbrev_code);
+ if (ret_die->di_abbrev_list == NULL ) {
+ dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
+ _dwarf_error(dbg, error, DW_DLE_DIE_ABBREV_LIST_NULL);
+ return (DW_DLV_ERROR);
+ }
+ if (die == NULL && !is_cu_tag(ret_die->di_abbrev_list->ab_tag)) {
+ dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
+ _dwarf_error(dbg, error, DW_DLE_FIRST_DIE_NOT_CU);
+ return (DW_DLV_ERROR);
+ }
+
+ *caller_ret_die = ret_die;
+ return (DW_DLV_OK);
+}
+
+
+int
+dwarf_child(Dwarf_Die die,
+ Dwarf_Die * caller_ret_die, Dwarf_Error * error)
+{
+ Dwarf_Byte_Ptr die_info_ptr = 0;
+
+ /* die_info_end points one-past-end of die area. */
+ Dwarf_Byte_Ptr die_info_end = 0;
+ Dwarf_Die ret_die = 0;
+ Dwarf_Bool has_die_child = 0;
+ Dwarf_Debug dbg;
+ Dwarf_Word abbrev_code = 0;
+ Dwarf_Unsigned utmp = 0;
+ Dwarf_Small *dataptr = 0;
+ Dwarf_Debug_InfoTypes dis = 0;
+
+ CHECK_DIE(die, DW_DLV_ERROR);
+ dbg = die->di_cu_context->cc_dbg;
+ dis = die->di_is_info? &dbg->de_info_reading:
+ &dbg->de_types_reading;
+ die_info_ptr = die->di_debug_ptr;
+
+ dataptr = die->di_is_info? dbg->de_debug_info.dss_data:
+ dbg->de_debug_types.dss_data;
+
+
+ /* We are saving a DIE pointer here, but the pointer
+ will not be presumed live later, when it is tested. */
+ dis->de_last_die = die;
+ dis->de_last_di_ptr = die_info_ptr;
+
+ /* NULL die has no child. */
+ if ((*die_info_ptr) == 0)
+ return (DW_DLV_NO_ENTRY);
+
+ die_info_end = dataptr +
+ die->di_cu_context->cc_debug_offset +
+ die->di_cu_context->cc_length +
+ die->di_cu_context->cc_length_size +
+ die->di_cu_context->cc_extension_size;
+
+ die_info_ptr =
+ _dwarf_next_die_info_ptr(die_info_ptr, die->di_cu_context,
+ die_info_end, NULL, false,
+ &has_die_child);
+ if (die_info_ptr == NULL) {
+ _dwarf_error(dbg, error, DW_DLE_NEXT_DIE_PTR_NULL);
+ return (DW_DLV_ERROR);
+ }
+
+ dis->de_last_di_ptr = die_info_ptr;
+
+ if (!has_die_child) {
+ /* Look for end of sibling chain. */
+ while ( dis->de_last_di_ptr < die_info_end) {
+ if (*dis->de_last_di_ptr) {
+ break;
+ }
+ ++dis->de_last_di_ptr;
+ }
+ return (DW_DLV_NO_ENTRY);
+ }
+
+ ret_die = (Dwarf_Die) _dwarf_get_alloc(dbg, DW_DLA_DIE, 1);
+ if (ret_die == NULL) {
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (DW_DLV_ERROR);
+ }
+ ret_die->di_debug_ptr = die_info_ptr;
+ ret_die->di_cu_context = die->di_cu_context;
+ ret_die->di_is_info = die->di_is_info;
+
+ DECODE_LEB128_UWORD(die_info_ptr, utmp);
+ abbrev_code = (Dwarf_Word) utmp;
+
+ dis->de_last_di_ptr = die_info_ptr;
+
+ if (abbrev_code == 0) {
+ /* Look for end of sibling chain */
+ while ( dis->de_last_di_ptr < die_info_end) {
+ if (*dis->de_last_di_ptr) {
+ break;
+ }
+ ++dis->de_last_di_ptr;
+ }
+
+ /* We have arrived at a null DIE, at the end of a CU or the end
+ of a list of siblings. */
+ *caller_ret_die = 0;
+ dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
+ return DW_DLV_NO_ENTRY;
+ }
+ ret_die->di_abbrev_code = abbrev_code;
+ ret_die->di_abbrev_list =
+ _dwarf_get_abbrev_for_code(die->di_cu_context, abbrev_code);
+ if (ret_die->di_abbrev_list == NULL) {
+ dwarf_dealloc(dbg, ret_die, DW_DLA_DIE);
+ _dwarf_error(dbg, error, DW_DLE_DIE_BAD);
+ return (DW_DLV_ERROR);
+ }
+
+ *caller_ret_die = ret_die;
+ return (DW_DLV_OK);
+}
+
+/* Given a (global, not cu_relative) die offset, this returns
+ a pointer to a DIE thru *new_die.
+ It is up to the caller to do a
+ dwarf_dealloc(dbg,*new_die,DW_DLE_DIE);
+ The old form only works with debug_info.
+ The new _b form works with debug_info or debug_types.
+ */
+int
+dwarf_offdie(Dwarf_Debug dbg,
+ Dwarf_Off offset, Dwarf_Die * new_die, Dwarf_Error * error)
+{
+ Dwarf_Bool is_info = true;
+ return dwarf_offdie_b(dbg,offset,is_info,new_die,error);
+}
+
+int
+dwarf_offdie_b(Dwarf_Debug dbg,
+ Dwarf_Off offset, Dwarf_Bool is_info,
+ Dwarf_Die * new_die, Dwarf_Error * error)
+{
+ Dwarf_CU_Context cu_context = 0;
+ Dwarf_Off new_cu_offset = 0;
+ Dwarf_Die die = 0;
+ Dwarf_Byte_Ptr info_ptr = 0;
+ Dwarf_Unsigned abbrev_code = 0;
+ Dwarf_Unsigned utmp = 0;
+ Dwarf_Debug_InfoTypes dis = 0;
+
+
+ if (dbg == NULL) {
+ _dwarf_error(NULL, error, DW_DLE_DBG_NULL);
+ return (DW_DLV_ERROR);
+ }
+ dis = is_info? &dbg->de_info_reading:
+ &dbg->de_types_reading;
+
+ cu_context = _dwarf_find_CU_Context(dbg, offset,is_info);
+ if (cu_context == NULL)
+ cu_context = _dwarf_find_offdie_CU_Context(dbg, offset,is_info);
+
+ if (cu_context == NULL) {
+ Dwarf_Unsigned section_size = is_info? dbg->de_debug_info.dss_size:
+ dbg->de_debug_types.dss_size;
+ int res = is_info?_dwarf_load_debug_info(dbg, error):
+ _dwarf_load_debug_types(dbg,error);
+
+ if (res != DW_DLV_OK) {
+ return res;
+ }
+
+ if (dis->de_offdie_cu_context_end != NULL) {
+ Dwarf_CU_Context lcu_context =
+ dis->de_offdie_cu_context_end;
+ new_cu_offset =
+ lcu_context->cc_debug_offset +
+ lcu_context->cc_length +
+ lcu_context->cc_length_size +
+ lcu_context->cc_extension_size;
+ }
+
+
+ do {
+ if ((new_cu_offset +
+ _dwarf_length_of_cu_header_simple(dbg,is_info)) >= section_size) {
+ _dwarf_error(dbg, error, DW_DLE_OFFSET_BAD);
+ return (DW_DLV_ERROR);
+ }
+
+ cu_context =
+ _dwarf_make_CU_Context(dbg, new_cu_offset,is_info, error);
+ if (cu_context == NULL) {
+ /* Error if CU Context could not be made. Since
+ _dwarf_make_CU_Context has already registered an
+ error we do not do that here: we let the lower error
+ pass thru. */
+
+ return (DW_DLV_ERROR);
+ }
+
+ if (dis->de_offdie_cu_context == NULL) {
+ dis->de_offdie_cu_context = cu_context;
+ dis->de_offdie_cu_context_end = cu_context;
+ } else {
+ dis->de_offdie_cu_context_end->cc_next = cu_context;
+ dis->de_offdie_cu_context_end = cu_context;
+ }
+
+ new_cu_offset = new_cu_offset + cu_context->cc_length +
+ cu_context->cc_length_size;
+
+ } while (offset >= new_cu_offset);
+ }
+
+ die = (Dwarf_Die) _dwarf_get_alloc(dbg, DW_DLA_DIE, 1);
+ if (die == NULL) {
+ _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL);
+ return (DW_DLV_ERROR);
+ }
+ die->di_cu_context = cu_context;
+ die->di_is_info = is_info;
+
+ {
+ Dwarf_Small *dataptr = is_info? dbg->de_debug_info.dss_data:
+ dbg->de_debug_types.dss_data;
+ info_ptr = dataptr + offset;
+ }
+ die->di_debug_ptr = info_ptr;
+ DECODE_LEB128_UWORD(info_ptr, utmp);
+ abbrev_code = utmp;
+ if (abbrev_code == 0) {
+ /* we are at a null DIE (or there is a bug). */
+ *new_die = 0;
+ dwarf_dealloc(dbg, die, DW_DLA_DIE);
+ return DW_DLV_NO_ENTRY;
+ }
+ die->di_abbrev_code = abbrev_code;
+ die->di_abbrev_list =
+ _dwarf_get_abbrev_for_code(cu_context, abbrev_code);
+ if (die->di_abbrev_list == NULL) {
+ dwarf_dealloc(dbg, die, DW_DLA_DIE);
+ _dwarf_error(dbg, error, DW_DLE_DIE_ABBREV_LIST_NULL);
+ return (DW_DLV_ERROR);
+ }
+
+ *new_die = die;
+ return (DW_DLV_OK);
+}