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