summaryrefslogtreecommitdiff
path: root/libdw/dwarf_siblingof.c
blob: bd965ea8dbc045e20f7181de35f991d178d302f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* Return sibling of given DIE.
   Copyright (C) 2003, 2004, 2005 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;
  /* End of the buffer.  */
  unsigned char *endp
    = ((unsigned char *) sibattr.cu->dbg->sectiondata[IDX_debug_info]->d_buf
       + sibattr.cu->end);

  /* 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 (INTUSE(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;


      while (1)
	{
	  /* Make sure we are still in range.  Some producers might skip
	     the trailing NUL bytes.  */
	  if (addr >= endp)
	    return 1;

	  if (*addr != '\0')
	    break;

	  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 >= endp)
    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;
}
INTDEF(dwarf_siblingof)