1da0c48c4Sopenharmony_ci/* Get function information.
2da0c48c4Sopenharmony_ci   Copyright (C) 2005, 2013, 2015 Red Hat, Inc.
3da0c48c4Sopenharmony_ci   This file is part of elfutils.
4da0c48c4Sopenharmony_ci   Written by Ulrich Drepper <drepper@redhat.com>, 2005.
5da0c48c4Sopenharmony_ci
6da0c48c4Sopenharmony_ci   This file is free software; you can redistribute it and/or modify
7da0c48c4Sopenharmony_ci   it under the terms of either
8da0c48c4Sopenharmony_ci
9da0c48c4Sopenharmony_ci     * the GNU Lesser General Public License as published by the Free
10da0c48c4Sopenharmony_ci       Software Foundation; either version 3 of the License, or (at
11da0c48c4Sopenharmony_ci       your option) any later version
12da0c48c4Sopenharmony_ci
13da0c48c4Sopenharmony_ci   or
14da0c48c4Sopenharmony_ci
15da0c48c4Sopenharmony_ci     * the GNU General Public License as published by the Free
16da0c48c4Sopenharmony_ci       Software Foundation; either version 2 of the License, or (at
17da0c48c4Sopenharmony_ci       your option) any later version
18da0c48c4Sopenharmony_ci
19da0c48c4Sopenharmony_ci   or both in parallel, as here.
20da0c48c4Sopenharmony_ci
21da0c48c4Sopenharmony_ci   elfutils is distributed in the hope that it will be useful, but
22da0c48c4Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
23da0c48c4Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24da0c48c4Sopenharmony_ci   General Public License for more details.
25da0c48c4Sopenharmony_ci
26da0c48c4Sopenharmony_ci   You should have received copies of the GNU General Public License and
27da0c48c4Sopenharmony_ci   the GNU Lesser General Public License along with this program.  If
28da0c48c4Sopenharmony_ci   not, see <http://www.gnu.org/licenses/>.  */
29da0c48c4Sopenharmony_ci
30da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H
31da0c48c4Sopenharmony_ci# include <config.h>
32da0c48c4Sopenharmony_ci#endif
33da0c48c4Sopenharmony_ci
34da0c48c4Sopenharmony_ci#include <dwarf.h>
35da0c48c4Sopenharmony_ci#include "libdwP.h"
36da0c48c4Sopenharmony_ci
37da0c48c4Sopenharmony_ci
38da0c48c4Sopenharmony_cistruct visitor_info
39da0c48c4Sopenharmony_ci{
40da0c48c4Sopenharmony_ci  /* The user callback of dwarf_getfuncs.  */
41da0c48c4Sopenharmony_ci  int (*callback) (Dwarf_Die *, void *);
42da0c48c4Sopenharmony_ci
43da0c48c4Sopenharmony_ci  /* The user arg value to dwarf_getfuncs.  */
44da0c48c4Sopenharmony_ci  void *arg;
45da0c48c4Sopenharmony_ci
46da0c48c4Sopenharmony_ci  /* Addr of the DIE offset where to (re)start the search.  Zero for all.  */
47da0c48c4Sopenharmony_ci  void *start_addr;
48da0c48c4Sopenharmony_ci
49da0c48c4Sopenharmony_ci  /* Last subprogram DIE addr seen.  */
50da0c48c4Sopenharmony_ci  void *last_addr;
51da0c48c4Sopenharmony_ci
52da0c48c4Sopenharmony_ci  /* The CU only contains C functions.  Allows pruning of most subtrees.  */
53da0c48c4Sopenharmony_ci  bool c_cu;
54da0c48c4Sopenharmony_ci};
55da0c48c4Sopenharmony_ci
56da0c48c4Sopenharmony_cistatic int
57da0c48c4Sopenharmony_citree_visitor (unsigned int depth __attribute__ ((unused)),
58da0c48c4Sopenharmony_ci	      struct Dwarf_Die_Chain *chain, void *arg)
59da0c48c4Sopenharmony_ci{
60da0c48c4Sopenharmony_ci  struct visitor_info *const v = arg;
61da0c48c4Sopenharmony_ci  Dwarf_Die *die = &chain->die;
62da0c48c4Sopenharmony_ci  void *start_addr = v->start_addr;
63da0c48c4Sopenharmony_ci  void *die_addr = die->addr;
64da0c48c4Sopenharmony_ci
65da0c48c4Sopenharmony_ci  /* Pure C CUs can only contain defining subprogram DIEs as direct
66da0c48c4Sopenharmony_ci     children of the CU DIE or as nested function inside a normal C
67da0c48c4Sopenharmony_ci     code constructs.  */
68da0c48c4Sopenharmony_ci  int tag = INTUSE(dwarf_tag) (die);
69da0c48c4Sopenharmony_ci  if (v->c_cu
70da0c48c4Sopenharmony_ci      && tag != DW_TAG_subprogram
71da0c48c4Sopenharmony_ci      && tag != DW_TAG_lexical_block
72da0c48c4Sopenharmony_ci      && tag != DW_TAG_inlined_subroutine)
73da0c48c4Sopenharmony_ci    {
74da0c48c4Sopenharmony_ci      chain->prune = true;
75da0c48c4Sopenharmony_ci      return DWARF_CB_OK;
76da0c48c4Sopenharmony_ci    }
77da0c48c4Sopenharmony_ci
78da0c48c4Sopenharmony_ci  /* Skip all DIEs till we found the (re)start addr.  */
79da0c48c4Sopenharmony_ci  if (start_addr != NULL)
80da0c48c4Sopenharmony_ci    {
81da0c48c4Sopenharmony_ci      if (die_addr == start_addr)
82da0c48c4Sopenharmony_ci	v->start_addr = NULL;
83da0c48c4Sopenharmony_ci      return DWARF_CB_OK;
84da0c48c4Sopenharmony_ci    }
85da0c48c4Sopenharmony_ci
86da0c48c4Sopenharmony_ci  /* If this isn't a (defining) subprogram entity, skip DIE.  */
87da0c48c4Sopenharmony_ci  if (tag != DW_TAG_subprogram
88da0c48c4Sopenharmony_ci      || INTUSE(dwarf_hasattr) (die, DW_AT_declaration))
89da0c48c4Sopenharmony_ci    return DWARF_CB_OK;
90da0c48c4Sopenharmony_ci
91da0c48c4Sopenharmony_ci  v->last_addr = die_addr;
92da0c48c4Sopenharmony_ci  return (*v->callback) (die, v->arg);
93da0c48c4Sopenharmony_ci}
94da0c48c4Sopenharmony_ci
95da0c48c4Sopenharmony_ciptrdiff_t
96da0c48c4Sopenharmony_cidwarf_getfuncs (Dwarf_Die *cudie, int (*callback) (Dwarf_Die *, void *),
97da0c48c4Sopenharmony_ci		void *arg, ptrdiff_t offset)
98da0c48c4Sopenharmony_ci{
99da0c48c4Sopenharmony_ci  if (unlikely (cudie == NULL
100da0c48c4Sopenharmony_ci		|| INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit))
101da0c48c4Sopenharmony_ci    return -1;
102da0c48c4Sopenharmony_ci
103da0c48c4Sopenharmony_ci  int lang = INTUSE(dwarf_srclang) (cudie);
104da0c48c4Sopenharmony_ci  bool c_cu = (lang == DW_LANG_C89
105da0c48c4Sopenharmony_ci	       || lang == DW_LANG_C
106da0c48c4Sopenharmony_ci	       || lang == DW_LANG_C99
107da0c48c4Sopenharmony_ci	       || lang == DW_LANG_C11);
108da0c48c4Sopenharmony_ci
109da0c48c4Sopenharmony_ci  struct visitor_info v = { callback, arg, (void *) offset, NULL, c_cu };
110da0c48c4Sopenharmony_ci  struct Dwarf_Die_Chain chain = { .die = CUDIE (cudie->cu),
111da0c48c4Sopenharmony_ci				   .parent = NULL };
112da0c48c4Sopenharmony_ci  int res = __libdw_visit_scopes (0, &chain, NULL, &tree_visitor, NULL, &v);
113da0c48c4Sopenharmony_ci
114da0c48c4Sopenharmony_ci  if (res == DWARF_CB_ABORT)
115da0c48c4Sopenharmony_ci    return (ptrdiff_t) v.last_addr;
116da0c48c4Sopenharmony_ci  else
117da0c48c4Sopenharmony_ci    return res;
118da0c48c4Sopenharmony_ci}
119