Home | History | Annotate | Download | only in libdw
      1 /* DW_EH_PE_* support for libdw unwinder.
      2    Copyright (C) 2009-2010, 2014, 2015 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 #ifndef _ENCODED_VALUE_H
     30 #define _ENCODED_VALUE_H 1
     31 
     32 #include <dwarf.h>
     33 #include <stdlib.h>
     34 #include "libdwP.h"
     35 #include "../libelf/common.h"
     36 
     37 
     38 /* Returns zero if the value is omitted, the encoding is unknown or
     39    the (leb128) size cannot be determined.  */
     40 static size_t __attribute__ ((unused))
     41 encoded_value_size (const Elf_Data *data, const unsigned char e_ident[],
     42 		    uint8_t encoding, const uint8_t *p)
     43 {
     44   if (encoding == DW_EH_PE_omit)
     45     return 0;
     46 
     47   switch (encoding & 0x07)
     48     {
     49     case DW_EH_PE_udata2:
     50       return 2;
     51     case DW_EH_PE_udata4:
     52       return 4;
     53     case DW_EH_PE_udata8:
     54       return 8;
     55 
     56     case DW_EH_PE_absptr:
     57       return e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
     58 
     59     case DW_EH_PE_uleb128:
     60       if (p != NULL)
     61 	{
     62 	  const uint8_t *end = p;
     63 	  while (end < (uint8_t *) data->d_buf + data->d_size)
     64 	    if (*end++ & 0x80u)
     65 	      return end - p;
     66 	}
     67       return 0;
     68 
     69     default:
     70       return 0;
     71     }
     72 }
     73 
     74 /* Returns zero when value was read successfully, minus one otherwise.  */
     75 static inline int __attribute__ ((unused))
     76 __libdw_cfi_read_address_inc (const Dwarf_CFI *cache,
     77 			      const unsigned char **addrp,
     78 			      int width, Dwarf_Addr *ret)
     79 {
     80   width = width ?: cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
     81 
     82   if (cache->dbg != NULL)
     83     return __libdw_read_address_inc (cache->dbg, IDX_debug_frame,
     84 				     addrp, width, ret);
     85 
     86   /* Only .debug_frame might have relocation to consider.
     87      Read plain values from .eh_frame data.  */
     88 
     89   const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
     90   Dwarf eh_dbg = { .other_byte_order = MY_ELFDATA != cache->e_ident[EI_DATA] };
     91 
     92   if (width == 4)
     93     {
     94       if (unlikely (*addrp + 4 > endp))
     95 	{
     96 	invalid_data:
     97 	  __libdw_seterrno (DWARF_E_INVALID_CFI);
     98 	  return -1;
     99 	}
    100       *ret = read_4ubyte_unaligned_inc (&eh_dbg, *addrp);
    101     }
    102   else
    103     {
    104       if (unlikely (*addrp + 8 > endp))
    105 	goto invalid_data;
    106       *ret = read_8ubyte_unaligned_inc (&eh_dbg, *addrp);
    107     }
    108   return 0;
    109 }
    110 
    111 /* Returns true on error, false otherwise. */
    112 static bool __attribute__ ((unused))
    113 read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding,
    114 		    const uint8_t **p, Dwarf_Addr *result)
    115 {
    116   *result = 0;
    117   switch (encoding & 0x70)
    118     {
    119     case DW_EH_PE_absptr:
    120       break;
    121     case DW_EH_PE_pcrel:
    122       *result = (cache->frame_vaddr
    123 		 + (*p - (const uint8_t *) cache->data->d.d_buf));
    124       break;
    125     case DW_EH_PE_textrel:
    126       // ia64: segrel
    127       *result = cache->textrel;
    128       break;
    129     case DW_EH_PE_datarel:
    130       // i386: GOTOFF
    131       // ia64: gprel
    132       *result = cache->datarel;
    133       break;
    134     case DW_EH_PE_funcrel:	/* XXX */
    135       break;
    136     case DW_EH_PE_aligned:
    137       {
    138 	const size_t size = encoded_value_size (&cache->data->d,
    139 						cache->e_ident,
    140 						encoding, *p);
    141 	if (unlikely (size == 0))
    142 	  return true;
    143 	size_t align = ((cache->frame_vaddr
    144 			 + (*p - (const uint8_t *) cache->data->d.d_buf))
    145 			& (size - 1));
    146 	if (align != 0)
    147 	  *p += size - align;
    148 	break;
    149       }
    150 
    151     default:
    152       __libdw_seterrno (DWARF_E_INVALID_CFI);
    153       return true;
    154     }
    155 
    156   Dwarf_Addr value = 0;
    157   const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
    158   switch (encoding & 0x0f)
    159     {
    160     case DW_EH_PE_udata2:
    161       if (unlikely (*p + 2 > endp))
    162 	{
    163 	invalid_data:
    164 	  __libdw_seterrno (DWARF_E_INVALID_CFI);
    165 	  return true;
    166 	}
    167       value = read_2ubyte_unaligned_inc (cache, *p);
    168       break;
    169 
    170     case DW_EH_PE_sdata2:
    171       if (unlikely (*p + 2 > endp))
    172 	goto invalid_data;
    173       value = read_2sbyte_unaligned_inc (cache, *p);
    174       break;
    175 
    176     case DW_EH_PE_udata4:
    177       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
    178 	return true;
    179       break;
    180 
    181     case DW_EH_PE_sdata4:
    182       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
    183 	return true;
    184       value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend.  */
    185       break;
    186 
    187     case DW_EH_PE_udata8:
    188     case DW_EH_PE_sdata8:
    189       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 8, &value) != 0))
    190 	return true;
    191       break;
    192 
    193     case DW_EH_PE_absptr:
    194       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 0, &value) != 0))
    195 	return true;
    196       break;
    197 
    198     case DW_EH_PE_uleb128:
    199       get_uleb128 (value, *p, endp);
    200       break;
    201 
    202     case DW_EH_PE_sleb128:
    203       get_sleb128 (value, *p, endp);
    204       break;
    205 
    206     default:
    207       __libdw_seterrno (DWARF_E_INVALID_CFI);
    208       return true;
    209     }
    210 
    211   *result += value;
    212 
    213   if (encoding & DW_EH_PE_indirect)
    214     {
    215       if (unlikely (*result < cache->frame_vaddr))
    216 	return true;
    217       *result -= cache->frame_vaddr;
    218       size_t ptrsize = encoded_value_size (NULL, cache->e_ident,
    219 					   DW_EH_PE_absptr, NULL);
    220       if (unlikely (cache->data->d.d_size < ptrsize
    221 		    || *result > (cache->data->d.d_size - ptrsize)))
    222 	return true;
    223       const uint8_t *ptr = cache->data->d.d_buf + *result;
    224       if (unlikely (__libdw_cfi_read_address_inc (cache, &ptr, 0, result)
    225 		    != 0))
    226 	return true;
    227     }
    228 
    229   return false;
    230 }
    231 
    232 #endif	/* encoded-value.h */
    233