Brian Silverman | 8649792 | 2018-02-10 19:28:39 -0500 | [diff] [blame] | 1 | /* Get function information. |
| 2 | Copyright (C) 2005, 2013, 2015 Red Hat, Inc. |
| 3 | This file is part of elfutils. |
| 4 | Written by Ulrich Drepper <drepper@redhat.com>, 2005. |
| 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 <dwarf.h> |
| 35 | #include "libdwP.h" |
| 36 | |
| 37 | |
| 38 | struct visitor_info |
| 39 | { |
| 40 | /* The user callback of dwarf_getfuncs. */ |
| 41 | int (*callback) (Dwarf_Die *, void *); |
| 42 | |
| 43 | /* The user arg value to dwarf_getfuncs. */ |
| 44 | void *arg; |
| 45 | |
| 46 | /* Addr of the DIE offset where to (re)start the search. Zero for all. */ |
| 47 | void *start_addr; |
| 48 | |
| 49 | /* Last subprogram DIE addr seen. */ |
| 50 | void *last_addr; |
| 51 | |
| 52 | /* The CU only contains C functions. Allows pruning of most subtrees. */ |
| 53 | bool c_cu; |
| 54 | }; |
| 55 | |
| 56 | static int |
| 57 | tree_visitor (unsigned int depth __attribute__ ((unused)), |
| 58 | struct Dwarf_Die_Chain *chain, void *arg) |
| 59 | { |
| 60 | struct visitor_info *const v = arg; |
| 61 | Dwarf_Die *die = &chain->die; |
| 62 | void *start_addr = v->start_addr; |
| 63 | void *die_addr = die->addr; |
| 64 | |
| 65 | /* Pure C CUs can only contain defining subprogram DIEs as direct |
| 66 | children of the CU DIE or as nested function inside a normal C |
| 67 | code constructs. */ |
| 68 | int tag = INTUSE(dwarf_tag) (die); |
| 69 | if (v->c_cu |
| 70 | && tag != DW_TAG_subprogram |
| 71 | && tag != DW_TAG_lexical_block |
| 72 | && tag != DW_TAG_inlined_subroutine) |
| 73 | { |
| 74 | chain->prune = true; |
| 75 | return DWARF_CB_OK; |
| 76 | } |
| 77 | |
| 78 | /* Skip all DIEs till we found the (re)start addr. */ |
| 79 | if (start_addr != NULL) |
| 80 | { |
| 81 | if (die_addr == start_addr) |
| 82 | v->start_addr = NULL; |
| 83 | return DWARF_CB_OK; |
| 84 | } |
| 85 | |
| 86 | /* If this isn't a (defining) subprogram entity, skip DIE. */ |
| 87 | if (tag != DW_TAG_subprogram |
| 88 | || INTUSE(dwarf_hasattr) (die, DW_AT_declaration)) |
| 89 | return DWARF_CB_OK; |
| 90 | |
| 91 | v->last_addr = die_addr; |
| 92 | return (*v->callback) (die, v->arg); |
| 93 | } |
| 94 | |
| 95 | ptrdiff_t |
| 96 | dwarf_getfuncs (Dwarf_Die *cudie, int (*callback) (Dwarf_Die *, void *), |
| 97 | void *arg, ptrdiff_t offset) |
| 98 | { |
| 99 | if (unlikely (cudie == NULL |
| 100 | || INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit)) |
| 101 | return -1; |
| 102 | |
| 103 | int lang = INTUSE(dwarf_srclang) (cudie); |
| 104 | bool c_cu = (lang == DW_LANG_C89 |
| 105 | || lang == DW_LANG_C |
| 106 | || lang == DW_LANG_C99 |
| 107 | || lang == DW_LANG_C11); |
| 108 | |
| 109 | struct visitor_info v = { callback, arg, (void *) offset, NULL, c_cu }; |
| 110 | struct Dwarf_Die_Chain chain = { .die = CUDIE (cudie->cu), |
| 111 | .parent = NULL }; |
| 112 | int res = __libdw_visit_scopes (0, &chain, NULL, &tree_visitor, NULL, &v); |
| 113 | |
| 114 | if (res == DWARF_CB_ABORT) |
| 115 | return (ptrdiff_t) v.last_addr; |
| 116 | else |
| 117 | return res; |
| 118 | } |