dwarf_getscopes.c revision 03333823c75a1c1887e923828113a1b0fd12020c
1/* Return scope DIEs containing PC address.
2   Copyright (C) 2005, 2007 Red Hat, Inc.
3   This file is part of elfutils.
4
5   This file is free software; you can redistribute it and/or modify
6   it under the terms of either
7
8     * the GNU Lesser General Public License as published by the Free
9       Software Foundation; either version 3 of the License, or (at
10       your option) any later version
11
12   or
13
14     * the GNU General Public License as published by the Free
15       Software Foundation; either version 2 of the License, or (at
16       your option) any later version
17
18   or both in parallel, as here.
19
20   elfutils is distributed in the hope that it will be useful, but
21   WITHOUT ANY WARRANTY; without even the implied warranty of
22   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23   General Public License for more details.
24
25   You should have received copies of the GNU General Public License and
26   the GNU Lesser General Public License along with this program.  If
27   not, see <http://www.gnu.org/licenses/>.  */
28
29#ifdef HAVE_CONFIG_H
30# include <config.h>
31#endif
32
33#include <assert.h>
34#include <stdlib.h>
35#include "libdwP.h"
36#include <dwarf.h>
37
38
39struct args
40{
41  Dwarf_Addr pc;
42  Dwarf_Die *scopes;
43  unsigned int inlined, nscopes;
44  Dwarf_Die inlined_origin;
45};
46
47/* Preorder visitor: prune the traversal if this DIE does not contain PC.  */
48static int
49pc_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
50{
51  struct args *a = arg;
52
53  if (a->scopes != NULL)
54    die->prune = true;
55  else
56    {
57      /* dwarf_haspc returns an error if there are no appropriate attributes.
58	 But we use it indiscriminantly instead of presuming which tags can
59	 have PC attributes.  So when it fails for that reason, treat it just
60	 as a nonmatching return.  */
61      int result = INTUSE(dwarf_haspc) (&die->die, a->pc);
62      if (result < 0)
63	{
64	  int error = INTUSE(dwarf_errno) ();
65	  if (error != DWARF_E_NOERROR && error != DWARF_E_NO_DEBUG_RANGES)
66	    {
67	      __libdw_seterrno (error);
68	      return -1;
69	    }
70	  result = 0;
71	}
72      if (result == 0)
73    	die->prune = true;
74
75      if (!die->prune
76	  && INTUSE (dwarf_tag) (&die->die) == DW_TAG_inlined_subroutine)
77	a->inlined = depth;
78    }
79
80  return 0;
81}
82
83/* Preorder visitor for second partial traversal after finding a
84   concrete inlined instance.  */
85static int
86origin_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
87{
88  struct args *a = arg;
89
90  if (die->die.addr != a->inlined_origin.addr)
91    return 0;
92
93  /* We have a winner!  This is the abstract definition of the inline
94     function of which A->scopes[A->nscopes - 1] is a concrete instance.
95  */
96
97  unsigned int nscopes = a->nscopes + depth;
98  Dwarf_Die *scopes = realloc (a->scopes, nscopes * sizeof scopes[0]);
99  if (scopes == NULL)
100    {
101      free (a->scopes);
102      __libdw_seterrno (DWARF_E_NOMEM);
103      return -1;
104    }
105
106  a->scopes = scopes;
107  do
108    {
109      die = die->parent;
110      scopes[a->nscopes++] = die->die;
111    }
112  while (a->nscopes < nscopes);
113  assert (die->parent == NULL);
114  return a->nscopes;
115}
116
117/* Postorder visitor: first (innermost) call wins.  */
118static int
119pc_record (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
120{
121  struct args *a = arg;
122
123  if (die->prune)
124    return 0;
125
126  if (a->scopes == NULL)
127    {
128      /* We have hit the innermost DIE that contains the target PC.  */
129
130      a->nscopes = depth + 1 - a->inlined;
131      a->scopes = malloc (a->nscopes * sizeof a->scopes[0]);
132      if (a->scopes == NULL)
133	{
134	  __libdw_seterrno (DWARF_E_NOMEM);
135	  return -1;
136	}
137
138      for (unsigned int i = 0; i < a->nscopes; ++i)
139	{
140	  a->scopes[i] = die->die;
141	  die = die->parent;
142	}
143
144      if (a->inlined == 0)
145	{
146	  assert (die == NULL);
147	  return a->nscopes;
148	}
149
150      /* This is the concrete inlined instance itself.
151	 Record its abstract_origin pointer.  */
152      Dwarf_Die *const inlinedie = &a->scopes[depth - a->inlined];
153
154      assert (INTUSE (dwarf_tag) (inlinedie) == DW_TAG_inlined_subroutine);
155      Dwarf_Attribute attr_mem;
156      Dwarf_Attribute *attr = INTUSE (dwarf_attr) (inlinedie,
157						   DW_AT_abstract_origin,
158						   &attr_mem);
159      if (INTUSE (dwarf_formref_die) (attr, &a->inlined_origin) == NULL)
160	return -1;
161      return 0;
162    }
163
164
165  /* We've recorded the scopes back to one that is a concrete inlined
166     instance.  Now return out of the traversal back to the scope
167     containing that instance.  */
168
169  assert (a->inlined);
170  if (depth >= a->inlined)
171    /* Not there yet.  */
172    return 0;
173
174  /* Now we are in a scope that contains the concrete inlined instance.
175     Search it for the inline function's abstract definition.
176     If we don't find it, return to search the containing scope.
177     If we do find it, the nonzero return value will bail us out
178     of the postorder traversal.  */
179  return __libdw_visit_scopes (depth, die, &origin_match, NULL, a);
180}
181
182
183int
184dwarf_getscopes (Dwarf_Die *cudie, Dwarf_Addr pc, Dwarf_Die **scopes)
185{
186  if (cudie == NULL)
187    return -1;
188
189  struct Dwarf_Die_Chain cu = { .parent = NULL, .die = *cudie };
190  struct args a = { .pc = pc };
191
192  int result = __libdw_visit_scopes (0, &cu, &pc_match, &pc_record, &a);
193
194  if (result == 0 && a.scopes != NULL)
195    result = __libdw_visit_scopes (0, &cu, &origin_match, NULL, &a);
196
197  if (result > 0)
198    *scopes = a.scopes;
199
200  return result;
201}
202