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