Branch data Line data Source code
1 : : /* Return scope DIEs containing PC address.
2 : : Copyright (C) 2005, 2007, 2015 Red Hat, Inc.
3 : : Copyright (C) 2023 Mark J. Wielaard <mark@klomp.org>
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
50 : 4096 : pc_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
51 : : {
52 : 4096 : struct args *a = arg;
53 : :
54 [ + + ]: 4096 : if (a->scopes != NULL)
55 : 34 : 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 : 4062 : int result = INTUSE(dwarf_haspc) (&die->die, a->pc);
63 [ - + ]: 4062 : if (result < 0)
64 : : {
65 : 0 : int error = INTUSE(dwarf_errno) ();
66 : 0 : if (error != DWARF_E_NOERROR
67 [ # # ]: 0 : && error != DWARF_E_NO_DEBUG_RANGES
68 [ # # ]: 0 : && error != DWARF_E_NO_DEBUG_RNGLISTS)
69 : : {
70 : 0 : __libdw_seterrno (error);
71 : 0 : return -1;
72 : : }
73 : : result = 0;
74 : : }
75 [ + + ]: 4062 : if (result == 0)
76 : 3156 : die->prune = true;
77 : :
78 [ + + ]: 4062 : if (!die->prune
79 [ + + ]: 906 : && INTUSE (dwarf_tag) (&die->die) == DW_TAG_inlined_subroutine)
80 : 358 : 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
89 : 406 : origin_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
90 : : {
91 : 406 : struct args *a = arg;
92 : :
93 [ + + ]: 406 : 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 : 240 : unsigned int nscopes = a->nscopes + depth;
101 : 240 : Dwarf_Die *scopes = realloc (a->scopes, nscopes * sizeof scopes[0]);
102 [ - + ]: 240 : if (scopes == NULL)
103 : : {
104 : : /* a->scopes will be freed by dwarf_getscopes on error. */
105 : 0 : __libdw_seterrno (DWARF_E_NOMEM);
106 : 0 : return -1;
107 : : }
108 : :
109 : 240 : a->scopes = scopes;
110 : 240 : do
111 : : {
112 : 240 : die = die->parent;
113 : 240 : scopes[a->nscopes++] = die->die;
114 : : }
115 [ - + ]: 240 : while (a->nscopes < nscopes);
116 [ - + ]: 240 : assert (die->parent == NULL);
117 : 240 : return a->nscopes;
118 : : }
119 : :
120 : : /* Postorder visitor: first (innermost) call wins. */
121 : : static int
122 : 3938 : pc_record (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
123 : : {
124 : 3938 : struct args *a = arg;
125 : :
126 [ + + ]: 3938 : if (die->prune)
127 : : return 0;
128 : :
129 [ + + ]: 748 : if (a->scopes == NULL)
130 : : {
131 : : /* We have hit the innermost DIE that contains the target PC. */
132 : :
133 : 508 : a->nscopes = depth + 1 - a->inlined;
134 : 508 : a->scopes = malloc (a->nscopes * sizeof a->scopes[0]);
135 [ - + ]: 508 : if (a->scopes == NULL)
136 : : {
137 : 0 : __libdw_seterrno (DWARF_E_NOMEM);
138 : 0 : return -1;
139 : : }
140 : :
141 [ + + ]: 1284 : for (unsigned int i = 0; i < a->nscopes; ++i)
142 : : {
143 : 776 : a->scopes[i] = die->die;
144 : 776 : die = die->parent;
145 : : }
146 : :
147 [ + + ]: 508 : if (a->inlined == 0)
148 : : {
149 [ - + ]: 268 : assert (die == NULL);
150 : 268 : return a->nscopes;
151 : : }
152 : :
153 : : /* This is the concrete inlined instance itself.
154 : : Record its abstract_origin pointer. */
155 : 240 : Dwarf_Die *const inlinedie = &a->scopes[depth - a->inlined];
156 : :
157 [ - + ]: 240 : assert (INTUSE (dwarf_tag) (inlinedie) == DW_TAG_inlined_subroutine);
158 : 240 : Dwarf_Attribute attr_mem;
159 : 240 : Dwarf_Attribute *attr = INTUSE (dwarf_attr) (inlinedie,
160 : : DW_AT_abstract_origin,
161 : : &attr_mem);
162 [ - + ]: 240 : 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 [ - + ]: 240 : assert (a->inlined);
173 [ + - ]: 240 : if (depth >= a->inlined)
174 : : /* Not there yet. */
175 : : return 0;
176 : :
177 : : /* This is the innermost inline scope, we are done here. */
178 : 240 : return a->nscopes;
179 : : }
180 : :
181 : :
182 : : int
183 : 520 : dwarf_getscopes (Dwarf_Die *cudie, Dwarf_Addr pc, Dwarf_Die **scopes)
184 : : {
185 [ + + ]: 520 : if (cudie == NULL)
186 : : return -1;
187 : :
188 : 508 : struct Dwarf_Die_Chain cu = { .parent = NULL, .die = *cudie };
189 : 508 : struct args a = { .pc = pc };
190 : :
191 : 508 : int result = __libdw_visit_scopes (0, &cu, NULL, &pc_match, &pc_record, &a);
192 : :
193 [ + - + - : 508 : 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 : 240 : cu.die = CUDIE (a.inlined_origin.cu);
198 : 240 : result = __libdw_visit_scopes (0, &cu, NULL, &origin_match, NULL, &a);
199 : : }
200 : :
201 [ + - ]: 508 : if (result > 0)
202 : 508 : *scopes = a.scopes;
203 [ # # ]: 0 : else if (result < 0)
204 : 0 : free (a.scopes);
205 : :
206 : : return result;
207 : : }
|