1/* Helper functions to descend DWARF scope trees.
2   Copyright (C) 2005,2006,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 "libdwP.h"
34#include <dwarf.h>
35
36
37static bool
38may_have_scopes (Dwarf_Die *die)
39{
40  switch (INTUSE(dwarf_tag) (die))
41    {
42      /* DIEs with addresses we can try to match.  */
43    case DW_TAG_compile_unit:
44    case DW_TAG_module:
45    case DW_TAG_lexical_block:
46    case DW_TAG_with_stmt:
47    case DW_TAG_catch_block:
48    case DW_TAG_try_block:
49    case DW_TAG_entry_point:
50    case DW_TAG_inlined_subroutine:
51    case DW_TAG_subprogram:
52      return true;
53
54      /* DIEs without addresses that can own DIEs with addresses.  */
55    case DW_TAG_namespace:
56    case DW_TAG_class_type:
57    case DW_TAG_structure_type:
58      return true;
59
60      /* Other DIEs we have no reason to descend.  */
61    default:
62      break;
63    }
64  return false;
65}
66
67struct walk_children_state
68{
69  /* Parameters of __libdw_visit_scopes. */
70  unsigned int depth;
71  struct Dwarf_Die_Chain *imports;
72  int (*previsit) (unsigned int depth, struct Dwarf_Die_Chain *, void *);
73  int (*postvisit) (unsigned int depth, struct Dwarf_Die_Chain *, void *);
74  void *arg;
75  /* Extra local variables for the walker. */
76  struct Dwarf_Die_Chain child;
77};
78
79static inline int
80walk_children (struct walk_children_state *state);
81
82int
83internal_function
84__libdw_visit_scopes (unsigned int depth, struct Dwarf_Die_Chain *root,
85		      struct Dwarf_Die_Chain *imports,
86		      int (*previsit) (unsigned int,
87				       struct Dwarf_Die_Chain *,
88				       void *),
89		      int (*postvisit) (unsigned int,
90					struct Dwarf_Die_Chain *,
91					void *),
92		      void *arg)
93{
94  struct walk_children_state state =
95    {
96      .depth = depth,
97      .imports = imports,
98      .previsit = previsit,
99      .postvisit = postvisit,
100      .arg = arg
101    };
102
103  state.child.parent = root;
104  int ret;
105  if ((ret = INTUSE(dwarf_child) (&root->die, &state.child.die)) != 0)
106    return ret < 0 ? -1 : 0; // Having zero children is legal.
107
108  return walk_children (&state);
109}
110
111static inline int
112walk_children (struct walk_children_state *state)
113{
114  int ret;
115  do
116    {
117      /* For an imported unit, it is logically as if the children of
118	 that unit are siblings of the other children.  So don't do
119	 a full recursion into the imported unit, but just walk the
120	 children in place before moving to the next real child.  */
121      while (INTUSE(dwarf_tag) (&state->child.die) == DW_TAG_imported_unit)
122	{
123	  Dwarf_Die orig_child_die = state->child.die;
124	  Dwarf_Attribute attr_mem;
125	  Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&state->child.die,
126						      DW_AT_import,
127						      &attr_mem);
128	  /* Some gcc -flto versions imported other top-level compile units,
129	     skip those.  */
130	  if (INTUSE(dwarf_formref_die) (attr, &state->child.die) != NULL
131	      && INTUSE(dwarf_tag) (&state->child.die) != DW_TAG_compile_unit
132	      && (INTUSE(dwarf_child) (&state->child.die, &state->child.die)
133		  == 0))
134	    {
135	      /* Checks the given DIE hasn't been imported yet
136	         to prevent cycles.  */
137	      bool imported = false;
138	      for (struct Dwarf_Die_Chain *import = state->imports; import != NULL;
139	        import = import->parent)
140	        if (import->die.addr == orig_child_die.addr)
141	          {
142	            imported = true;
143	            break;
144	          }
145	      if (imported)
146		{
147		  __libdw_seterrno (DWARF_E_INVALID_DWARF);
148		  return -1;
149		}
150	      struct Dwarf_Die_Chain *orig_imports = state->imports;
151	      struct Dwarf_Die_Chain import = { .die = orig_child_die,
152					        .parent = orig_imports };
153	      state->imports = &import;
154	      int result = walk_children (state);
155	      state->imports = orig_imports;
156	      if (result != DWARF_CB_OK)
157		return result;
158	    }
159
160	  /* Any "real" children left?  */
161	  if ((ret = INTUSE(dwarf_siblingof) (&orig_child_die,
162					      &state->child.die)) != 0)
163	    return ret < 0 ? -1 : 0;
164	};
165
166	state->child.prune = false;
167
168	/* previsit is declared NN */
169	int result = (*state->previsit) (state->depth + 1, &state->child, state->arg);
170	if (result != DWARF_CB_OK)
171	  return result;
172
173	if (!state->child.prune && may_have_scopes (&state->child.die)
174	    && INTUSE(dwarf_haschildren) (&state->child.die))
175	  {
176	    result = __libdw_visit_scopes (state->depth + 1, &state->child, state->imports,
177				           state->previsit, state->postvisit, state->arg);
178	    if (result != DWARF_CB_OK)
179	      return result;
180	  }
181
182	if (state->postvisit != NULL)
183	  {
184	    result = (*state->postvisit) (state->depth + 1, &state->child, state->arg);
185	    if (result != DWARF_CB_OK)
186	      return result;
187	  }
188    }
189  while ((ret = INTUSE(dwarf_siblingof) (&state->child.die, &state->child.die)) == 0);
190
191  return ret < 0 ? -1 : 0;
192}
193