1/* Return sibling of given DIE.
2   Copyright (C) 2003, 2004 Red Hat, Inc.
3   Written by Ulrich Drepper <drepper@redhat.com>, 2003.
4
5   This program is Open Source software; you can redistribute it and/or
6   modify it under the terms of the Open Software License version 1.0 as
7   published by the Open Source Initiative.
8
9   You should have received a copy of the Open Software License along
10   with this program; if not, you may obtain a copy of the Open Software
11   License version 1.0 from http://www.opensource.org/licenses/osl.php or
12   by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
13   3001 King Ranch Road, Ukiah, CA 95482.   */
14
15#ifdef HAVE_CONFIG_H
16# include <config.h>
17#endif
18
19#include "libdwP.h"
20#include <dwarf.h>
21#include <string.h>
22
23
24int
25dwarf_siblingof (die, result)
26     Dwarf_Die *die;
27     Dwarf_Die *result;
28{
29  /* Ignore previous errors.  */
30  if (die == NULL)
31    return -1;
32
33  unsigned int level = 0;
34
35  /* Copy of the current DIE.  */
36  Dwarf_Die this_die = *die;
37  /* Temporary attributes we create.  */
38  Dwarf_Attribute sibattr;
39  /* Copy of the CU in the request.  */
40  sibattr.cu = this_die.cu;
41  /* That's the address we start looking.  */
42  unsigned char *addr = this_die.addr;
43
44  /* Search for the beginning of the next die on this level.  We
45     must not return the dies for children of the given die.  */
46  do
47    {
48      /* Find the end of the DIE or the sibling attribute.  */
49      addr = __libdw_find_attr (&this_die, DW_AT_sibling, &sibattr.code,
50				&sibattr.form);
51      if (sibattr.code == DW_AT_sibling)
52	{
53	  Dwarf_Off offset;
54	  sibattr.valp = addr;
55	  if (dwarf_formref (&sibattr, &offset) != 0)
56	    /* Something went wrong.  */
57	    return -1;
58
59	  /* Compute the next address.  */
60	  addr = ((unsigned char *)
61		  sibattr.cu->dbg->sectiondata[IDX_debug_info]->d_buf
62		  + sibattr.cu->start + offset);
63	}
64      else if (unlikely (addr == NULL)
65	       || unlikely (this_die.abbrev == (Dwarf_Abbrev *) -1l))
66	return -1;
67      else if (this_die.abbrev->has_children)
68	/* This abbreviation has children.  */
69	++level;
70
71      /* Check that we are not yet at the end.  */
72      while (*addr == '\0')
73	{
74	  if (level-- == 0)
75	    /* No more sibling at all.  */
76	    return 1;
77
78	  ++addr;
79	}
80
81      /* Initialize the 'current DIE'.  */
82      this_die.addr = addr;
83      this_die.abbrev = NULL;
84    }
85  while (level > 0);
86
87  /* Maybe we reached the end of the CU.  */
88  if (addr
89      >= ((unsigned char *) sibattr.cu->dbg->sectiondata[IDX_debug_info]->d_buf
90	  + sibattr.cu->end))
91    return 1;
92
93  /* Clear the entire DIE structure.  This signals we have not yet
94     determined any of the information.  */
95  memset (result, '\0', sizeof (Dwarf_Die));
96
97  /* We have the address.  */
98  result->addr = addr;
99
100  /* Same CU as the parent.  */
101  result->cu = sibattr.cu;
102
103  return 0;
104}
105