Home | History | Annotate | Download | only in libdw
      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
     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
     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