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.  */
42internal_function int
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
84ptrdiff_t
85dwarf_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}
193INTDEF (dwarf_ranges)
194