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