• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
pc_match(unsigned int depth,struct Dwarf_Die_Chain * die,void * arg)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
origin_match(unsigned int depth,struct Dwarf_Die_Chain * die,void * arg)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
pc_record(unsigned int depth,struct Dwarf_Die_Chain * die,void * arg)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
dwarf_getscopes(Dwarf_Die * cudie,Dwarf_Addr pc,Dwarf_Die ** scopes)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 }
202