xref: /aosp_15_r20/external/elfutils/libdw/dwarf_getscopes.c (revision 7304104da70ce23c86437a01be71edd1a2d7f37e)
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