packaging: update homepage url
[platform/upstream/elfutils.git] / libdw / dwarf_getscopes.c
1 /* Return scope DIEs containing PC address.
2    Copyright (C) 2005, 2007, 2015 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
39 struct 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.  */
48 static int
49 pc_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.  */
85 static int
86 origin_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.  */
118 static int
119 pc_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, NULL, &origin_match, NULL, a);
180 }
181
182
183 int
184 dwarf_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, NULL, &pc_match, &pc_record, &a);
193
194   if (result == 0 && a.scopes != NULL)
195     result = __libdw_visit_scopes (0, &cu, NULL, &origin_match, NULL, &a);
196
197   if (result > 0)
198     *scopes = a.scopes;
199
200   return result;
201 }