xref: /third_party/elfutils/libdw/dwarf_ranges.c (revision da0c48c4)
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.  */
43internal_function int
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
384static int
385initial_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
463ptrdiff_t
464dwarf_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      /* Note that above we use dwarf_attr, not dwarf_attr_integrate.
510	 The only case where the ranges can come from another DIE
511	 attribute are the split CU case. In that case we also have a
512	 different CU to check against. But that is already set up
513	 above using __libdw_find_split_unit.  */
514      if (attr == NULL
515	  && is_cudie (die)
516	  && die->cu->unit_type == DW_UT_split_compile)
517	attr = INTUSE(dwarf_attr_integrate) (die, DW_AT_ranges, &attr_mem);
518      if (attr == NULL)
519	/* No PC attributes in this DIE at all, so an empty range list.  */
520	return 0;
521
522      *basep = __libdw_cu_base_address (attr->cu);
523      if (*basep == (Dwarf_Addr) -1)
524	return -1;
525
526      if (initial_offset (attr, &offset) != 0)
527	return -1;
528    }
529  else
530    {
531      if (__libdw_offset_in_section (cu->dbg,
532				     secidx, offset, 1))
533	return -1;
534    }
535
536  readp = d->d_buf + offset;
537  readendp = d->d_buf + d->d_size;
538
539  Dwarf_Addr begin;
540  Dwarf_Addr end;
541
542 next:
543  switch (__libdw_read_begin_end_pair_inc (cu, secidx,
544					   &readp, readendp,
545					   cu->address_size,
546					   &begin, &end, basep))
547    {
548    case 0:
549      break;
550    case 1:
551      goto next;
552    case 2:
553      return 0;
554    default:
555      return -1;
556    }
557
558  *startp = begin;
559  *endp = end;
560  return readp - (unsigned char *) d->d_buf;
561}
562INTDEF (dwarf_ranges)
563