summaryrefslogtreecommitdiffstats
path: root/libdw/dwarf_siblingof.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdw/dwarf_siblingof.c')
-rw-r--r--libdw/dwarf_siblingof.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/libdw/dwarf_siblingof.c b/libdw/dwarf_siblingof.c
new file mode 100644
index 00000000..90081c64
--- /dev/null
+++ b/libdw/dwarf_siblingof.c
@@ -0,0 +1,104 @@
+/* Return sibling of given DIE.
+ Copyright (C) 2003, 2004 Red Hat, Inc.
+ Written by Ulrich Drepper <drepper@redhat.com>, 2003.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwP.h"
+#include <dwarf.h>
+#include <string.h>
+
+
+int
+dwarf_siblingof (die, result)
+ Dwarf_Die *die;
+ Dwarf_Die *result;
+{
+ /* Ignore previous errors. */
+ if (die == NULL)
+ return -1;
+
+ unsigned int level = 0;
+
+ /* Copy of the current DIE. */
+ Dwarf_Die this_die = *die;
+ /* Temporary attributes we create. */
+ Dwarf_Attribute sibattr;
+ /* Copy of the CU in the request. */
+ sibattr.cu = this_die.cu;
+ /* That's the address we start looking. */
+ unsigned char *addr = this_die.addr;
+
+ /* Search for the beginning of the next die on this level. We
+ must not return the dies for children of the given die. */
+ do
+ {
+ /* Find the end of the DIE or the sibling attribute. */
+ addr = __libdw_find_attr (&this_die, DW_AT_sibling, &sibattr.code,
+ &sibattr.form);
+ if (sibattr.code == DW_AT_sibling)
+ {
+ Dwarf_Off offset;
+ sibattr.valp = addr;
+ if (dwarf_formref (&sibattr, &offset) != 0)
+ /* Something went wrong. */
+ return -1;
+
+ /* Compute the next address. */
+ addr = ((unsigned char *)
+ sibattr.cu->dbg->sectiondata[IDX_debug_info]->d_buf
+ + sibattr.cu->start + offset);
+ }
+ else if (unlikely (addr == NULL)
+ || unlikely (this_die.abbrev == (Dwarf_Abbrev *) -1l))
+ return -1;
+ else if (this_die.abbrev->has_children)
+ /* This abbreviation has children. */
+ ++level;
+
+ /* Check that we are not yet at the end. */
+ while (*addr == '\0')
+ {
+ if (level-- == 0)
+ /* No more sibling at all. */
+ return 1;
+
+ ++addr;
+ }
+
+ /* Initialize the 'current DIE'. */
+ this_die.addr = addr;
+ this_die.abbrev = NULL;
+ }
+ while (level > 0);
+
+ /* Maybe we reached the end of the CU. */
+ if (addr
+ >= ((unsigned char *) sibattr.cu->dbg->sectiondata[IDX_debug_info]->d_buf
+ + sibattr.cu->end))
+ return 1;
+
+ /* Clear the entire DIE structure. This signals we have not yet
+ determined any of the information. */
+ memset (result, '\0', sizeof (Dwarf_Die));
+
+ /* We have the address. */
+ result->addr = addr;
+
+ /* Same CU as the parent. */
+ result->cu = sibattr.cu;
+
+ return 0;
+}