diff options
Diffstat (limited to 'libdw/dwarf_siblingof.c')
-rw-r--r-- | libdw/dwarf_siblingof.c | 104 |
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; +} |