• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Enumerate the PC ranges covered by a DIE.
2    Copyright (C) 2005, 2007, 2009, 2018 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 a default location, set `*beginp' (0), `*endp' (-1) and return 0.
40     - If it's base address selection record, set up `*basep' and return 1.
41     - If it's end of rangelist, don't set anything and return 2
42     - If an error occurs, don't set anything and return -1.  */
43 internal_function int
__libdw_read_begin_end_pair_inc(Dwarf_CU * cu,int sec_index,const unsigned char ** addrp,const unsigned char * addrend,int width,Dwarf_Addr * beginp,Dwarf_Addr * endp,Dwarf_Addr * basep)44 __libdw_read_begin_end_pair_inc (Dwarf_CU *cu, int sec_index,
45 				 const unsigned char **addrp,
46 				 const unsigned char *addrend,
47 				 int width,
48 				 Dwarf_Addr *beginp, Dwarf_Addr *endp,
49 				 Dwarf_Addr *basep)
50 {
51   Dwarf *dbg = cu->dbg;
52   if (sec_index == IDX_debug_loc
53       && cu->version < 5
54       && cu->unit_type == DW_UT_split_compile)
55     {
56       /* GNU DebugFission.  */
57       const unsigned char *addr = *addrp;
58       if (addrend - addr < 1)
59 	goto invalid;
60 
61       const char code = *addr++;
62       uint64_t begin = 0, end = 0, base = *basep, addr_idx;
63       switch (code)
64 	{
65 	case DW_LLE_GNU_end_of_list_entry:
66 	  *addrp = addr;
67 	  return 2;
68 
69 	case DW_LLE_GNU_base_address_selection_entry:
70 	  if (addrend - addr < 1)
71 	    goto invalid;
72 	  get_uleb128 (addr_idx, addr, addrend);
73 	  if (__libdw_addrx (cu, addr_idx, &base) != 0)
74 	    return -1;
75 	  *basep = base;
76 	  *addrp = addr;
77 	  return 1;
78 
79 	case DW_LLE_GNU_start_end_entry:
80 	  if (addrend - addr < 1)
81 	    goto invalid;
82 	  get_uleb128 (addr_idx, addr, addrend);
83 	  if (__libdw_addrx (cu, addr_idx, &begin) != 0)
84 	    return -1;
85 	  if (addrend - addr < 1)
86 	    goto invalid;
87 	  get_uleb128 (addr_idx, addr, addrend);
88 	  if (__libdw_addrx (cu, addr_idx, &end) != 0)
89 	    return -1;
90 
91 	  *beginp = begin;
92 	  *endp = end;
93 	  *addrp = addr;
94 	  return 0;
95 
96 	case DW_LLE_GNU_start_length_entry:
97 	  if (addrend - addr < 1)
98 	    goto invalid;
99 	  get_uleb128 (addr_idx, addr, addrend);
100 	  if (__libdw_addrx (cu, addr_idx, &begin) != 0)
101 	    return -1;
102 	  if (addrend - addr < 4)
103 	    goto invalid;
104 	  end = read_4ubyte_unaligned_inc (dbg, addr);
105 
106 	  *beginp = begin;
107 	  *endp = begin + end;
108 	  *addrp = addr;
109 	  return 0;
110 
111 	default:
112 	  goto invalid;
113 	}
114     }
115   else if (sec_index == IDX_debug_ranges || sec_index == IDX_debug_loc)
116     {
117       Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1
118 			   : (Elf64_Addr) (Elf32_Addr) -1);
119       Dwarf_Addr begin;
120       Dwarf_Addr end;
121 
122       const unsigned char *addr = *addrp;
123       if (addrend - addr < width * 2)
124 	{
125 	invalid:
126 	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
127 	  return -1;
128 	}
129 
130       bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address,
131 						begin);
132       bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address,
133 					      end);
134       *addrp = addr;
135 
136       /* Unrelocated escape for begin means base address selection.  */
137       if (begin == escape && !begin_relocated)
138 	{
139 	  if (unlikely (end == escape))
140 	    goto invalid;
141 
142 	  *basep = end;
143 	  return 1;
144 	}
145 
146       /* Unrelocated pair of zeroes means end of range list.  */
147       if (begin == 0 && end == 0 && !begin_relocated && !end_relocated)
148 	return 2;
149 
150       /* Don't check for begin_relocated == end_relocated.  Serve the data
151 	 to the client even though it may be buggy.  */
152       *beginp = begin + *basep;
153       *endp = end + *basep;
154 
155       return 0;
156     }
157   else if (sec_index == IDX_debug_rnglists)
158     {
159       const unsigned char *addr = *addrp;
160       if (addrend - addr < 1)
161 	goto invalid;
162 
163       const char code = *addr++;
164       uint64_t begin = 0, end = 0, base = *basep, addr_idx;
165       switch (code)
166 	{
167 	case DW_RLE_end_of_list:
168 	  *addrp = addr;
169 	  return 2;
170 
171 	case DW_RLE_base_addressx:
172 	  if (addrend - addr < 1)
173 	    goto invalid;
174 	  get_uleb128 (addr_idx, addr, addrend);
175 	  if (__libdw_addrx (cu, addr_idx, &base) != 0)
176 	    return -1;
177 
178 	  *basep = base;
179 	  *addrp = addr;
180 	  return 1;
181 
182 	case DW_RLE_startx_endx:
183 	  if (addrend - addr < 1)
184 	    goto invalid;
185 	  get_uleb128 (addr_idx, addr, addrend);
186 	  if (__libdw_addrx (cu, addr_idx, &begin) != 0)
187 	    return -1;
188 	  if (addrend - addr < 1)
189 	    goto invalid;
190 	  get_uleb128 (addr_idx, addr, addrend);
191 	  if (__libdw_addrx (cu, addr_idx, &end) != 0)
192 	    return -1;
193 
194 	  *beginp = begin;
195 	  *endp = end;
196 	  *addrp = addr;
197 	  return 0;
198 
199 	case DW_RLE_startx_length:
200 	  if (addrend - addr < 1)
201 	    goto invalid;
202 	  get_uleb128 (addr_idx, addr, addrend);
203 	  if (__libdw_addrx (cu, addr_idx, &begin) != 0)
204 	    return -1;
205 	  if (addrend - addr < 1)
206 	    goto invalid;
207 	  get_uleb128 (end, addr, addrend);
208 
209 	  *beginp = begin;
210 	  *endp = begin + end;
211 	  *addrp = addr;
212 	  return 0;
213 
214 	case DW_RLE_offset_pair:
215 	  if (addrend - addr < 1)
216 	    goto invalid;
217 	  get_uleb128 (begin, addr, addrend);
218 	  if (addrend - addr < 1)
219 	    goto invalid;
220 	  get_uleb128 (end, addr, addrend);
221 
222 	  *beginp = begin + base;
223 	  *endp = end + base;
224 	  *addrp = addr;
225 	  return 0;
226 
227 	case DW_RLE_base_address:
228 	  if (addrend - addr < width)
229 	    goto invalid;
230 	  __libdw_read_address_inc (dbg, sec_index, &addr, width, &base);
231 
232 	  *basep = base;
233 	  *addrp = addr;
234 	  return 1;
235 
236 	case DW_RLE_start_end:
237 	  if (addrend - addr < 2 * width)
238 	    goto invalid;
239 	  __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
240 	  __libdw_read_address_inc (dbg, sec_index, &addr, width, &end);
241 
242 	  *beginp = begin;
243 	  *endp = end;
244 	  *addrp = addr;
245 	  return 0;
246 
247 	case DW_RLE_start_length:
248 	  if (addrend - addr < width)
249 	    goto invalid;
250 	  __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
251 	  if (addrend - addr < 1)
252 	    goto invalid;
253 	  get_uleb128 (end, addr, addrend);
254 
255 	  *beginp = begin;
256 	  *endp = begin + end;
257 	  *addrp = addr;
258 	  return 0;
259 
260 	default:
261 	  goto invalid;
262 	}
263     }
264   else if (sec_index == IDX_debug_loclists)
265     {
266       const unsigned char *addr = *addrp;
267       if (addrend - addr < 1)
268 	goto invalid;
269 
270       const char code = *addr++;
271       uint64_t begin = 0, end = 0, base = *basep, addr_idx;
272       switch (code)
273 	{
274 	case DW_LLE_end_of_list:
275 	  *addrp = addr;
276 	  return 2;
277 
278 	case DW_LLE_base_addressx:
279 	  if (addrend - addr < 1)
280 	    goto invalid;
281 	  get_uleb128 (addr_idx, addr, addrend);
282 	  if (__libdw_addrx (cu, addr_idx, &base) != 0)
283 	    return -1;
284 
285 	  *basep = base;
286 	  *addrp = addr;
287 	  return 1;
288 
289 	case DW_LLE_startx_endx:
290 	  if (addrend - addr < 1)
291 	    goto invalid;
292 	  get_uleb128 (addr_idx, addr, addrend);
293 	  if (__libdw_addrx (cu, addr_idx, &begin) != 0)
294 	    return -1;
295 	  if (addrend - addr < 1)
296 	    goto invalid;
297 	  get_uleb128 (addr_idx, addr, addrend);
298 	  if (__libdw_addrx (cu, addr_idx, &end) != 0)
299 	    return -1;
300 
301 	  *beginp = begin;
302 	  *endp = end;
303 	  *addrp = addr;
304 	  return 0;
305 
306 	case DW_LLE_startx_length:
307 	  if (addrend - addr < 1)
308 	    goto invalid;
309 	  get_uleb128 (addr_idx, addr, addrend);
310 	  if (__libdw_addrx (cu, addr_idx, &begin) != 0)
311 	    return -1;
312 	  if (addrend - addr < 1)
313 	    goto invalid;
314 	  get_uleb128 (end, addr, addrend);
315 
316 	  *beginp = begin;
317 	  *endp = begin + end;
318 	  *addrp = addr;
319 	  return 0;
320 
321 	case DW_LLE_offset_pair:
322 	  if (addrend - addr < 1)
323 	    goto invalid;
324 	  get_uleb128 (begin, addr, addrend);
325 	  if (addrend - addr < 1)
326 	    goto invalid;
327 	  get_uleb128 (end, addr, addrend);
328 
329 	  *beginp = begin + base;
330 	  *endp = end + base;
331 	  *addrp = addr;
332 	  return 0;
333 
334 	case DW_LLE_default_location:
335 	  *beginp = 0;
336 	  *endp = (Dwarf_Addr) -1;
337 	  *addrp = addr;
338 	  return 0;
339 
340 	case DW_LLE_base_address:
341 	  if (addrend - addr < width)
342 	    goto invalid;
343 	  __libdw_read_address_inc (dbg, sec_index, &addr, width, &base);
344 
345 	  *basep = base;
346 	  *addrp = addr;
347 	  return 1;
348 
349 	case DW_LLE_start_end:
350 	  if (addrend - addr < 2 * width)
351 	    goto invalid;
352 	  __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
353 	  __libdw_read_address_inc (dbg, sec_index, &addr, width, &end);
354 
355 	  *beginp = begin;
356 	  *endp = end;
357 	  *addrp = addr;
358 	  return 0;
359 
360 	case DW_LLE_start_length:
361 	  if (addrend - addr < width)
362 	    goto invalid;
363 	  __libdw_read_address_inc (dbg, sec_index, &addr, width, &begin);
364 	  if (addrend - addr < 1)
365 	    goto invalid;
366 	  get_uleb128 (end, addr, addrend);
367 
368 	  *beginp = begin;
369 	  *endp = begin + end;
370 	  *addrp = addr;
371 	  return 0;
372 
373 	default:
374 	  goto invalid;
375 	}
376     }
377   else
378     {
379       __libdw_seterrno (DWARF_E_INVALID_DWARF);
380       return -1;
381     }
382 }
383 
384 static int
initial_offset(Dwarf_Attribute * attr,ptrdiff_t * offset)385 initial_offset (Dwarf_Attribute *attr, ptrdiff_t *offset)
386 {
387   size_t secidx = (attr->cu->version < 5
388 		   ? IDX_debug_ranges : IDX_debug_rnglists);
389 
390   Dwarf_Word start_offset;
391   if (attr->form == DW_FORM_rnglistx)
392     {
393       Dwarf_Word idx;
394       Dwarf_CU *cu = attr->cu;
395       const unsigned char *datap = attr->valp;
396       const unsigned char *endp = cu->endp;
397       if (datap >= endp)
398 	{
399 	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
400 	  return -1;
401 	}
402       get_uleb128 (idx, datap, endp);
403 
404       Elf_Data *data = cu->dbg->sectiondata[secidx];
405       if (data == NULL && cu->unit_type == DW_UT_split_compile)
406 	{
407 	  cu = __libdw_find_split_unit (cu);
408 	  if (cu != NULL)
409 	    data = cu->dbg->sectiondata[secidx];
410 	}
411 
412       if (data == NULL)
413 	{
414 	  __libdw_seterrno (secidx == IDX_debug_ranges
415                             ? DWARF_E_NO_DEBUG_RANGES
416                             : DWARF_E_NO_DEBUG_RNGLISTS);
417 	  return -1;
418 	}
419 
420       Dwarf_Off range_base_off = __libdw_cu_ranges_base (cu);
421 
422       /* The section should at least contain room for one offset.  */
423       size_t sec_size = cu->dbg->sectiondata[secidx]->d_size;
424       size_t offset_size = cu->offset_size;
425       if (offset_size > sec_size)
426 	{
427 	invalid_offset:
428 	  __libdw_seterrno (DWARF_E_INVALID_OFFSET);
429 	  return -1;
430 	}
431 
432       /* And the base offset should be at least inside the section.  */
433       if (range_base_off > (sec_size - offset_size))
434 	goto invalid_offset;
435 
436       size_t max_idx = (sec_size - offset_size - range_base_off) / offset_size;
437       if (idx > max_idx)
438 	goto invalid_offset;
439 
440       datap = (cu->dbg->sectiondata[secidx]->d_buf
441 	       + range_base_off + (idx * offset_size));
442       if (offset_size == 4)
443 	start_offset = read_4ubyte_unaligned (cu->dbg, datap);
444       else
445 	start_offset = read_8ubyte_unaligned (cu->dbg, datap);
446 
447       start_offset += range_base_off;
448     }
449   else
450     {
451       if (__libdw_formptr (attr, secidx,
452 			   (secidx == IDX_debug_ranges
453 			    ? DWARF_E_NO_DEBUG_RANGES
454 			    : DWARF_E_NO_DEBUG_RNGLISTS),
455 			   NULL, &start_offset) == NULL)
456 	return -1;
457     }
458 
459   *offset = start_offset;
460   return 0;
461 }
462 
463 ptrdiff_t
dwarf_ranges(Dwarf_Die * die,ptrdiff_t offset,Dwarf_Addr * basep,Dwarf_Addr * startp,Dwarf_Addr * endp)464 dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep,
465 	      Dwarf_Addr *startp, Dwarf_Addr *endp)
466 {
467   if (die == NULL)
468     return -1;
469 
470   if (offset == 0
471       /* Usually there is a single contiguous range.  */
472       && INTUSE(dwarf_highpc) (die, endp) == 0
473       && INTUSE(dwarf_lowpc) (die, startp) == 0)
474     /* A offset into .debug_ranges will never be 1, it must be at least a
475        multiple of 4.  So we can return 1 as a special case value to mark
476        there are no ranges to look for on the next call.  */
477     return 1;
478 
479   if (offset == 1)
480     return 0;
481 
482   /* We have to look for a noncontiguous range.  */
483   Dwarf_CU *cu = die->cu;
484   if (cu == NULL)
485     {
486       __libdw_seterrno (DWARF_E_INVALID_DWARF);
487       return -1;
488     }
489 
490   size_t secidx = (cu->version < 5 ? IDX_debug_ranges : IDX_debug_rnglists);
491   const Elf_Data *d = cu->dbg->sectiondata[secidx];
492   if (d == NULL && cu->unit_type == DW_UT_split_compile)
493     {
494       Dwarf_CU *skel = __libdw_find_split_unit (cu);
495       if (skel != NULL)
496 	{
497 	  cu = skel;
498 	  d = cu->dbg->sectiondata[secidx];
499 	}
500     }
501 
502   const unsigned char *readp;
503   const unsigned char *readendp;
504   if (offset == 0)
505     {
506       Dwarf_Attribute attr_mem;
507       Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges,
508 						  &attr_mem);
509       if (attr == NULL
510 	  && is_cudie (die)
511 	  && die->cu->unit_type == DW_UT_split_compile)
512 	attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_ranges, &attr_mem);
513       if (attr == NULL)
514 	/* No PC attributes in this DIE at all, so an empty range list.  */
515 	return 0;
516 
517       *basep = __libdw_cu_base_address (attr->cu);
518       if (*basep == (Dwarf_Addr) -1)
519 	return -1;
520 
521       if (initial_offset (attr, &offset) != 0)
522 	return -1;
523     }
524   else
525     {
526       if (__libdw_offset_in_section (cu->dbg,
527 				     secidx, offset, 1))
528 	return -1;
529     }
530 
531   readp = d->d_buf + offset;
532   readendp = d->d_buf + d->d_size;
533 
534   Dwarf_Addr begin;
535   Dwarf_Addr end;
536 
537  next:
538   switch (__libdw_read_begin_end_pair_inc (cu, secidx,
539 					   &readp, readendp,
540 					   cu->address_size,
541 					   &begin, &end, basep))
542     {
543     case 0:
544       break;
545     case 1:
546       goto next;
547     case 2:
548       return 0;
549     default:
550       return -1;
551     }
552 
553   *startp = begin;
554   *endp = end;
555   return readp - (unsigned char *) d->d_buf;
556 }
557 INTDEF (dwarf_ranges)
558