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 Red Hat elfutils.
      4 
      5    Red Hat elfutils is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by the
      7    Free Software Foundation; version 2 of the License.
      8 
      9    Red Hat elfutils is distributed in the hope that it will be useful, but
     10    WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License along
     15    with Red Hat elfutils; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     17 
     18    In addition, as a special exception, Red Hat, Inc. gives You the
     19    additional right to link the code of Red Hat elfutils with code licensed
     20    under any Open Source Initiative certified open source license
     21    (http://www.opensource.org/licenses/index.php) which requires the
     22    distribution of source code with any binary distribution and to
     23    distribute linked combinations of the two.  Non-GPL Code permitted under
     24    this exception must only link to the code of Red Hat elfutils through
     25    those well defined interfaces identified in the file named EXCEPTION
     26    found in the source code files (the "Approved Interfaces").  The files
     27    of Non-GPL Code may instantiate templates or use macros or inline
     28    functions from the Approved Interfaces without causing the resulting
     29    work to be covered by the GNU General Public License.  Only Red Hat,
     30    Inc. may make changes or additions to the list of Approved Interfaces.
     31    Red Hat's grant of this exception is conditioned upon your not adding
     32    any new exceptions.  If you wish to add a new Approved Interface or
     33    exception, please contact Red Hat.  You must obey the GNU General Public
     34    License in all respects for all of the Red Hat elfutils code and other
     35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
     36    covered by this exception.  If you modify this file, you may extend this
     37    exception to your version of the file, but you are not obligated to do
     38    so.  If you do not wish to provide this exception without modification,
     39    you must delete this exception statement from your version and license
     40    this file solely under the GPL without exception.
     41 
     42    Red Hat elfutils is an included package of the Open Invention Network.
     43    An included package of the Open Invention Network is a package for which
     44    Open Invention Network licensees cross-license their patents.  No patent
     45    license is granted, either expressly or impliedly, by designation as an
     46    included package.  Should you wish to participate in the Open Invention
     47    Network licensing program, please visit www.openinventionnetwork.com
     48    <http://www.openinventionnetwork.com>.  */
     49 
     50 #ifdef HAVE_CONFIG_H
     51 # include <config.h>
     52 #endif
     53 
     54 #include "libdwP.h"
     55 #include <dwarf.h>
     56 #include <assert.h>
     57 
     58 /* Read up begin/end pair and increment read pointer.
     59     - If it's normal range record, set up `*beginp' and `*endp' and return 0.
     60     - If it's base address selection record, set up `*basep' and return 1.
     61     - If it's end of rangelist, don't set anything and return 2
     62     - If an error occurs, don't set anything and return -1.  */
     63 internal_function int
     64 __libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index,
     65 				 unsigned char **addrp, int width,
     66 				 Dwarf_Addr *beginp, Dwarf_Addr *endp,
     67 				 Dwarf_Addr *basep)
     68 {
     69   Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1
     70 		       : (Elf64_Addr) (Elf32_Addr) -1);
     71   Dwarf_Addr begin;
     72   Dwarf_Addr end;
     73 
     74   unsigned char *addr = *addrp;
     75   bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address, begin);
     76   bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address, end);
     77   *addrp = addr;
     78 
     79   /* Unrelocated escape for begin means base address selection.  */
     80   if (begin == escape && !begin_relocated)
     81     {
     82       if (unlikely (end == escape))
     83 	{
     84 	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
     85 	  return -1;
     86 	}
     87 
     88       if (basep != NULL)
     89 	*basep = end;
     90       return 1;
     91     }
     92 
     93   /* Unrelocated pair of zeroes means end of range list.  */
     94   if (begin == 0 && end == 0 && !begin_relocated && !end_relocated)
     95     return 2;
     96 
     97   /* Don't check for begin_relocated == end_relocated.  Serve the data
     98      to the client even though it may be buggy.  */
     99   *beginp = begin;
    100   *endp = end;
    101 
    102   return 0;
    103 }
    104 
    105 ptrdiff_t
    106 dwarf_ranges (Dwarf_Die *die, ptrdiff_t offset, Dwarf_Addr *basep,
    107 	      Dwarf_Addr *startp, Dwarf_Addr *endp)
    108 {
    109   if (die == NULL)
    110     return -1;
    111 
    112   if (offset == 0
    113       /* Usually there is a single contiguous range.  */
    114       && INTUSE(dwarf_highpc) (die, endp) == 0
    115       && INTUSE(dwarf_lowpc) (die, startp) == 0)
    116     /* A offset into .debug_ranges will never be 1, it must be at least a
    117        multiple of 4.  So we can return 1 as a special case value to mark
    118        there are no ranges to look for on the next call.  */
    119     return 1;
    120 
    121   if (offset == 1)
    122     return 0;
    123 
    124   /* We have to look for a noncontiguous range.  */
    125 
    126   const Elf_Data *d = die->cu->dbg->sectiondata[IDX_debug_ranges];
    127   if (d == NULL && offset != 0)
    128     {
    129       __libdw_seterrno (DWARF_E_NO_DEBUG_RANGES);
    130       return -1;
    131     }
    132 
    133   unsigned char *readp;
    134   unsigned char *readendp;
    135   if (offset == 0)
    136     {
    137       Dwarf_Attribute attr_mem;
    138       Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges,
    139 						  &attr_mem);
    140       if (attr == NULL)
    141 	/* No PC attributes in this DIE at all, so an empty range list.  */
    142 	return 0;
    143 
    144       Dwarf_Word start_offset;
    145       if ((readp = __libdw_formptr (attr, IDX_debug_ranges,
    146 				    DWARF_E_NO_DEBUG_RANGES,
    147 				    &readendp, &start_offset)) == NULL)
    148 	return -1;
    149 
    150       offset = start_offset;
    151       assert ((Dwarf_Word) offset == start_offset);
    152 
    153       /* Fetch the CU's base address.  */
    154       Dwarf_Die cudie = CUDIE (attr->cu);
    155 
    156       /* Find the base address of the compilation unit.  It will
    157 	 normally be specified by DW_AT_low_pc.  In DWARF-3 draft 4,
    158 	 the base address could be overridden by DW_AT_entry_pc.  It's
    159 	 been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc
    160 	 for compilation units with discontinuous ranges.  */
    161       if (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0)
    162 	  && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
    163 							 DW_AT_entry_pc,
    164 							 &attr_mem),
    165 				     basep) != 0)
    166 	{
    167 	  if (INTUSE(dwarf_errno) () == 0)
    168 	    {
    169 	    invalid:
    170 	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
    171 	    }
    172 	  return -1;
    173 	}
    174     }
    175   else
    176     {
    177       if (__libdw_offset_in_section (die->cu->dbg,
    178 				     IDX_debug_ranges, offset, 1))
    179 	return -1l;
    180 
    181       readp = d->d_buf + offset;
    182       readendp = d->d_buf + d->d_size;
    183     }
    184 
    185  next:
    186   if (readendp - readp < die->cu->address_size * 2)
    187     goto invalid;
    188 
    189   Dwarf_Addr begin;
    190   Dwarf_Addr end;
    191 
    192   switch (__libdw_read_begin_end_pair_inc (die->cu->dbg, IDX_debug_ranges,
    193 					   &readp, die->cu->address_size,
    194 					   &begin, &end, basep))
    195     {
    196     case 0:
    197       break;
    198     case 1:
    199       goto next;
    200     case 2:
    201       return 0;
    202     default:
    203       return -1l;
    204     }
    205 
    206   /* We have an address range entry.  */
    207   *startp = *basep + begin;
    208   *endp = *basep + end;
    209   return readp - (unsigned char *) d->d_buf;
    210 }
    211 INTDEF (dwarf_ranges)
    212