1 /* Enumerate the PC ranges covered by a DIE.
2 Copyright (C) 2005, 2007, 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
33 #include "libdwP.h"
34 #include <dwarf.h>
35 #include <assert.h>
36
37 /* Read up begin/end pair and increment read pointer.
38 - If it's normal range record, set up `*beginp' and `*endp' and return 0.
39 - If it's base address selection record, set up `*basep' and return 1.
40 - If it's end of rangelist, don't set anything and return 2
41 - If an error occurs, don't set anything and return -1. */
42 internal_function int
__libdw_read_begin_end_pair_inc(Dwarf * dbg,int sec_index,unsigned char ** addrp,int width,Dwarf_Addr * beginp,Dwarf_Addr * endp,Dwarf_Addr * basep)43 __libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index,
44 unsigned char **addrp, int width,
45 Dwarf_Addr *beginp, Dwarf_Addr *endp,
46 Dwarf_Addr *basep)
47 {
48 Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1
49 : (Elf64_Addr) (Elf32_Addr) -1);
50 Dwarf_Addr begin;
51 Dwarf_Addr end;
52
53 unsigned char *addr = *addrp;
54 bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address, begin);
55 bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address, end);
56 *addrp = addr;
57
58 /* Unrelocated escape for begin means base address selection. */
59 if (begin == escape && !begin_relocated)
60 {
61 if (unlikely (end == escape))
62 {
63 __libdw_seterrno (DWARF_E_INVALID_DWARF);
64 return -1;
65 }
66
67 if (basep != NULL)
68 *basep = end;
69 return 1;
70 }
71
72 /* Unrelocated pair of zeroes means end of range list. */
73 if (begin == 0 && end == 0 && !begin_relocated && !end_relocated)
74 return 2;
75
76 /* Don't check for begin_relocated == end_relocated. Serve the data
77 to the client even though it may be buggy. */
78 *beginp = begin;
79 *endp = end;
80
81 return 0;
82 }
83
84 ptrdiff_t
dwarf_ranges(Dwarf_Die * die,ptrdiff_t offset,Dwarf_Addr * basep,Dwarf_Addr * startp,Dwarf_Addr * endp)85 dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep,
86 Dwarf_Addr *startp, Dwarf_Addr *endp)
87 {
88 if (die == NULL)
89 return -1;
90
91 if (offset == 0
92 /* Usually there is a single contiguous range. */
93 && INTUSE(dwarf_highpc) (die, endp) == 0
94 && INTUSE(dwarf_lowpc) (die, startp) == 0)
95 /* A offset into .debug_ranges will never be 1, it must be at least a
96 multiple of 4. So we can return 1 as a special case value to mark
97 there are no ranges to look for on the next call. */
98 return 1;
99
100 if (offset == 1)
101 return 0;
102
103 /* We have to look for a noncontiguous range. */
104
105 const Elf_Data *d = die->cu->dbg->sectiondata[IDX_debug_ranges];
106 if (d == NULL && offset != 0)
107 {
108 __libdw_seterrno (DWARF_E_NO_DEBUG_RANGES);
109 return -1;
110 }
111
112 unsigned char *readp;
113 unsigned char *readendp;
114 if (offset == 0)
115 {
116 Dwarf_Attribute attr_mem;
117 Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges,
118 &attr_mem);
119 if (attr == NULL)
120 /* No PC attributes in this DIE at all, so an empty range list. */
121 return 0;
122
123 Dwarf_Word start_offset;
124 if ((readp = __libdw_formptr (attr, IDX_debug_ranges,
125 DWARF_E_NO_DEBUG_RANGES,
126 &readendp, &start_offset)) == NULL)
127 return -1;
128
129 offset = start_offset;
130 assert ((Dwarf_Word) offset == start_offset);
131
132 /* Fetch the CU's base address. */
133 Dwarf_Die cudie = CUDIE (attr->cu);
134
135 /* Find the base address of the compilation unit. It will
136 normally be specified by DW_AT_low_pc. In DWARF-3 draft 4,
137 the base address could be overridden by DW_AT_entry_pc. It's
138 been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc
139 for compilation units with discontinuous ranges. */
140 if (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0)
141 && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
142 DW_AT_entry_pc,
143 &attr_mem),
144 basep) != 0)
145 *basep = (Dwarf_Addr) -1;
146 }
147 else
148 {
149 if (__libdw_offset_in_section (die->cu->dbg,
150 IDX_debug_ranges, offset, 1))
151 return -1l;
152
153 readp = d->d_buf + offset;
154 readendp = d->d_buf + d->d_size;
155 }
156
157 next:
158 if (readendp - readp < die->cu->address_size * 2)
159 goto invalid;
160
161 Dwarf_Addr begin;
162 Dwarf_Addr end;
163
164 switch (__libdw_read_begin_end_pair_inc (die->cu->dbg, IDX_debug_ranges,
165 &readp, die->cu->address_size,
166 &begin, &end, basep))
167 {
168 case 0:
169 break;
170 case 1:
171 goto next;
172 case 2:
173 return 0;
174 default:
175 return -1l;
176 }
177
178 /* We have an address range entry. Check that we have a base. */
179 if (*basep == (Dwarf_Addr) -1)
180 {
181 if (INTUSE(dwarf_errno) () == 0)
182 {
183 invalid:
184 __libdw_seterrno (DWARF_E_INVALID_DWARF);
185 }
186 return -1;
187 }
188
189 *startp = *basep + begin;
190 *endp = *basep + end;
191 return readp - (unsigned char *) d->d_buf;
192 }
193 INTDEF (dwarf_ranges)
194