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 
     68     default:
     69       return 0;
     70     }
     71 }
     72 
     73 /* Returns zero when value was read successfully, minus one otherwise.  */
     74 static inline int __attribute__ ((unused))
     75 __libdw_cfi_read_address_inc (const Dwarf_CFI *cache,
     76 			      const unsigned char **addrp,
     77 			      int width, Dwarf_Addr *ret)
     78 {
     79   width = width ?: cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
     80 
     81   if (cache->dbg != NULL)
     82     return __libdw_read_address_inc (cache->dbg, IDX_debug_frame,
     83 				     addrp, width, ret);
     84 
     85   /* Only .debug_frame might have relocation to consider.
     86      Read plain values from .eh_frame data.  */
     87 
     88   const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
     89   Dwarf eh_dbg = { .other_byte_order = MY_ELFDATA != cache->e_ident[EI_DATA] };
     90 
     91   if (width == 4)
     92     {
     93       if (unlikely (*addrp + 4 > endp))
     94 	{
     95 	invalid_data:
     96 	  __libdw_seterrno (DWARF_E_INVALID_CFI);
     97 	  return -1;
     98 	}
     99       *ret = read_4ubyte_unaligned_inc (&eh_dbg, *addrp);
    100     }
    101   else
    102     {
    103       if (unlikely (*addrp + 8 > endp))
    104 	goto invalid_data;
    105       *ret = read_8ubyte_unaligned_inc (&eh_dbg, *addrp);
    106     }
    107   return 0;
    108 }
    109 
    110 /* Returns true on error, false otherwise. */
    111 static bool __attribute__ ((unused))
    112 read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding,
    113 		    const uint8_t **p, Dwarf_Addr *result)
    114 {
    115   *result = 0;
    116   switch (encoding & 0x70)
    117     {
    118     case DW_EH_PE_absptr:
    119       break;
    120     case DW_EH_PE_pcrel:
    121       *result = (cache->frame_vaddr
    122 		 + (*p - (const uint8_t *) cache->data->d.d_buf));
    123       break;
    124     case DW_EH_PE_textrel:
    125       // ia64: segrel
    126       *result = cache->textrel;
    127       break;
    128     case DW_EH_PE_datarel:
    129       // i386: GOTOFF
    130       // ia64: gprel
    131       *result = cache->datarel;
    132       break;
    133     case DW_EH_PE_funcrel:	/* XXX */
    134       break;
    135     case DW_EH_PE_aligned:
    136       {
    137 	const size_t size = encoded_value_size (&cache->data->d,
    138 						cache->e_ident,
    139 						encoding, *p);
    140 	if (unlikely (size == 0))
    141 	  return true;
    142 	size_t align = ((cache->frame_vaddr
    143 			 + (*p - (const uint8_t *) cache->data->d.d_buf))
    144 			& (size - 1));
    145 	if (align != 0)
    146 	  *p += size - align;
    147 	break;
    148       }
    149 
    150     default:
    151       __libdw_seterrno (DWARF_E_INVALID_CFI);
    152       return true;
    153     }
    154 
    155   Dwarf_Addr value = 0;
    156   const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
    157   switch (encoding & 0x0f)
    158     {
    159     case DW_EH_PE_udata2:
    160       if (unlikely (*p + 2 > endp))
    161 	{
    162 	invalid_data:
    163 	  __libdw_seterrno (DWARF_E_INVALID_CFI);
    164 	  return true;
    165 	}
    166       value = read_2ubyte_unaligned_inc (cache, *p);
    167       break;
    168 
    169     case DW_EH_PE_sdata2:
    170       if (unlikely (*p + 2 > endp))
    171 	goto invalid_data;
    172       value = read_2sbyte_unaligned_inc (cache, *p);
    173       break;
    174 
    175     case DW_EH_PE_udata4:
    176       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
    177 	return true;
    178       break;
    179 
    180     case DW_EH_PE_sdata4:
    181       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
    182 	return true;
    183       value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend.  */
    184       break;
    185 
    186     case DW_EH_PE_udata8:
    187     case DW_EH_PE_sdata8:
    188       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 8, &value) != 0))
    189 	return true;
    190       break;
    191 
    192     case DW_EH_PE_absptr:
    193       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 0, &value) != 0))
    194 	return true;
    195       break;
    196 
    197     case DW_EH_PE_uleb128:
    198       get_uleb128 (value, *p, endp);
    199       break;
    200 
    201     case DW_EH_PE_sleb128:
    202       get_sleb128 (value, *p, endp);
    203       break;
    204 
    205     default:
    206       __libdw_seterrno (DWARF_E_INVALID_CFI);
    207       return true;
    208     }
    209 
    210   *result += value;
    211 
    212   if (encoding & DW_EH_PE_indirect)
    213     {
    214       if (unlikely (*result < cache->frame_vaddr))
    215 	return true;
    216       *result -= cache->frame_vaddr;
    217       size_t ptrsize = encoded_value_size (NULL, cache->e_ident,
    218 					   DW_EH_PE_absptr, NULL);
    219       if (unlikely (cache->data->d.d_size < ptrsize
    220 		    || *result > (cache->data->d.d_size - ptrsize)))
    221 	return true;
    222       const uint8_t *ptr = cache->data->d.d_buf + *result;
    223       if (unlikely (__libdw_cfi_read_address_inc (cache, &ptr, 0, result)
    224 		    != 0))
    225 	return true;
    226     }
    227 
    228   return false;
    229 }
    230 
    231 #endif	/* encoded-value.h */
    232