Brian Silverman | 8649792 | 2018-02-10 19:28:39 -0500 | [diff] [blame^] | 1 | /* Find entry breakpoint locations for a function. |
| 2 | Copyright (C) 2005-2009 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 | #include "libdwP.h" |
| 33 | #include <dwarf.h> |
| 34 | #include <stdlib.h> |
| 35 | |
| 36 | |
| 37 | /* Add one breakpoint location to the result vector. */ |
| 38 | static inline int |
| 39 | add_bkpt (Dwarf_Addr pc, Dwarf_Addr **bkpts, int *pnbkpts) |
| 40 | { |
| 41 | Dwarf_Addr *newlist = realloc (*bkpts, ++(*pnbkpts) * sizeof newlist[0]); |
| 42 | if (newlist == NULL) |
| 43 | { |
| 44 | free (*bkpts); |
| 45 | *bkpts = NULL; |
| 46 | __libdw_seterrno (DWARF_E_NOMEM); |
| 47 | return -1; |
| 48 | } |
| 49 | newlist[*pnbkpts - 1] = pc; |
| 50 | *bkpts = newlist; |
| 51 | return *pnbkpts; |
| 52 | } |
| 53 | |
| 54 | /* Fallback result, break at the entrypc/lowpc value. */ |
| 55 | static inline int |
| 56 | entrypc_bkpt (Dwarf_Die *die, Dwarf_Addr **bkpts, int *pnbkpts) |
| 57 | { |
| 58 | Dwarf_Addr pc; |
| 59 | return INTUSE(dwarf_entrypc) (die, &pc) < 0 ? -1 : add_bkpt (pc, bkpts, pnbkpts); |
| 60 | } |
| 61 | |
| 62 | /* Search a contiguous PC range for prologue-end markers. |
| 63 | If DWARF, look for proper markers. |
| 64 | Failing that, if ADHOC, look for the ad hoc convention. */ |
| 65 | static inline int |
| 66 | search_range (Dwarf_Addr low, Dwarf_Addr high, |
| 67 | bool dwarf, bool adhoc, |
| 68 | Dwarf_Lines *lines, size_t nlines, |
| 69 | Dwarf_Addr **bkpts, int *pnbkpts) |
| 70 | { |
| 71 | size_t l = 0, u = nlines; |
| 72 | while (l < u) |
| 73 | { |
| 74 | size_t idx = (l + u) / 2; |
| 75 | if (lines->info[idx].addr < low) |
| 76 | l = idx + 1; |
| 77 | else if (lines->info[idx].addr > low) |
| 78 | u = idx; |
| 79 | else if (lines->info[idx].end_sequence) |
| 80 | l = idx + 1; |
| 81 | else |
| 82 | { |
| 83 | l = idx; |
| 84 | break; |
| 85 | } |
| 86 | } |
| 87 | if (l < u) |
| 88 | { |
| 89 | if (dwarf) |
| 90 | for (size_t i = l; i < u && lines->info[i].addr < high; ++i) |
| 91 | if (lines->info[i].prologue_end |
| 92 | && add_bkpt (lines->info[i].addr, bkpts, pnbkpts) < 0) |
| 93 | return -1; |
| 94 | if (adhoc && *pnbkpts == 0) |
| 95 | while (++l < nlines && lines->info[l].addr < high) |
| 96 | if (!lines->info[l].end_sequence) |
| 97 | return add_bkpt (lines->info[l].addr, bkpts, pnbkpts); |
| 98 | return *pnbkpts; |
| 99 | } |
| 100 | __libdw_seterrno (DWARF_E_INVALID_DWARF); |
| 101 | return -1; |
| 102 | } |
| 103 | |
| 104 | int |
| 105 | dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts) |
| 106 | { |
| 107 | int nbkpts = 0; |
| 108 | *bkpts = NULL; |
| 109 | |
| 110 | /* Fetch the CU's line records to look for this DIE's addresses. */ |
| 111 | Dwarf_Die cudie = CUDIE (die->cu); |
| 112 | Dwarf_Lines *lines; |
| 113 | size_t nlines; |
| 114 | if (INTUSE(dwarf_getsrclines) (&cudie, &lines, &nlines) < 0) |
| 115 | { |
| 116 | int error = INTUSE (dwarf_errno) (); |
| 117 | if (error == 0) /* CU has no DW_AT_stmt_list. */ |
| 118 | return entrypc_bkpt (die, bkpts, &nbkpts); |
| 119 | __libdw_seterrno (error); |
| 120 | return -1; |
| 121 | } |
| 122 | |
| 123 | /* Search each contiguous address range for DWARF prologue_end markers. */ |
| 124 | |
| 125 | Dwarf_Addr base; |
| 126 | Dwarf_Addr begin; |
| 127 | Dwarf_Addr end; |
| 128 | ptrdiff_t offset = INTUSE(dwarf_ranges) (die, 0, &base, &begin, &end); |
| 129 | if (offset < 0) |
| 130 | return -1; |
| 131 | |
| 132 | /* Most often there is a single contiguous PC range for the DIE. */ |
| 133 | if (offset == 1) |
| 134 | return search_range (begin, end, true, true, lines, nlines, bkpts, &nbkpts) |
| 135 | ?: entrypc_bkpt (die, bkpts, &nbkpts); |
| 136 | |
| 137 | Dwarf_Addr lowpc = (Dwarf_Addr) -1l; |
| 138 | Dwarf_Addr highpc = (Dwarf_Addr) -1l; |
| 139 | while (offset > 0) |
| 140 | { |
| 141 | /* We have an address range entry. */ |
| 142 | if (search_range (begin, end, true, false, |
| 143 | lines, nlines, bkpts, &nbkpts) < 0) |
| 144 | return -1; |
| 145 | |
| 146 | if (begin < lowpc) |
| 147 | { |
| 148 | lowpc = begin; |
| 149 | highpc = end; |
| 150 | } |
| 151 | |
| 152 | offset = INTUSE(dwarf_ranges) (die, offset, &base, &begin, &end); |
| 153 | } |
| 154 | |
| 155 | /* If we didn't find any proper DWARF markers, then look in the |
| 156 | lowest-addressed range for an ad hoc marker. Failing that, |
| 157 | fall back to just using the entrypc value. */ |
| 158 | return (nbkpts |
| 159 | ?: (lowpc == (Dwarf_Addr) -1l ? 0 |
| 160 | : search_range (lowpc, highpc, false, true, |
| 161 | lines, nlines, bkpts, &nbkpts)) |
| 162 | ?: entrypc_bkpt (die, bkpts, &nbkpts)); |
| 163 | } |