Home | History | Annotate | Download | only in libdw
      1 /* DW_EH_PE_* support for libdw unwinder.
      2    Copyright (C) 2009-2010, 2014 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 
     36 
     37 static size_t __attribute__ ((unused))
     38 encoded_value_size (const Elf_Data *data, const unsigned char e_ident[],
     39 		    uint8_t encoding, const uint8_t *p)
     40 {
     41   if (encoding == DW_EH_PE_omit)
     42     return 0;
     43 
     44   switch (encoding & 0x07)
     45     {
     46     case DW_EH_PE_udata2:
     47       return 2;
     48     case DW_EH_PE_udata4:
     49       return 4;
     50     case DW_EH_PE_udata8:
     51       return 8;
     52 
     53     case DW_EH_PE_absptr:
     54       return e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
     55 
     56     case DW_EH_PE_uleb128:
     57       if (p != NULL)
     58 	{
     59 	  const uint8_t *end = p;
     60 	  while (end < (uint8_t *) data->d_buf + data->d_size)
     61 	    if (*end++ & 0x80u)
     62 	      return end - p;
     63 	}
     64 
     65     default:
     66       abort ();
     67       return 0;
     68     }
     69 }
     70 
     71 static inline int __attribute__ ((unused))
     72 __libdw_cfi_read_address_inc (const Dwarf_CFI *cache,
     73 			      const unsigned char **addrp,
     74 			      int width, Dwarf_Addr *ret)
     75 {
     76   width = width ?: cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
     77 
     78   if (cache->dbg != NULL)
     79     return __libdw_read_address_inc (cache->dbg, IDX_debug_frame,
     80 				     addrp, width, ret);
     81 
     82   /* Only .debug_frame might have relocation to consider.
     83      Read plain values from .eh_frame data.  */
     84 
     85   if (width == 4)
     86     *ret = read_4ubyte_unaligned_inc (cache, *addrp);
     87   else
     88     *ret = read_8ubyte_unaligned_inc (cache, *addrp);
     89   return 0;
     90 }
     91 
     92 static bool __attribute__ ((unused))
     93 read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding, const uint8_t **p,
     94 		    Dwarf_Addr *result)
     95 {
     96   *result = 0;
     97   switch (encoding & 0x70)
     98     {
     99     case DW_EH_PE_absptr:
    100       break;
    101     case DW_EH_PE_pcrel:
    102       *result = (cache->frame_vaddr
    103 		 + (*p - (const uint8_t *) cache->data->d.d_buf));
    104       break;
    105     case DW_EH_PE_textrel:
    106       // ia64: segrel
    107       *result = cache->textrel;
    108       break;
    109     case DW_EH_PE_datarel:
    110       // i386: GOTOFF
    111       // ia64: gprel
    112       *result = cache->datarel;
    113       break;
    114     case DW_EH_PE_funcrel:	/* XXX */
    115       break;
    116     case DW_EH_PE_aligned:
    117       {
    118 	const size_t size = encoded_value_size (&cache->data->d, cache->e_ident,
    119 						encoding, *p);
    120 	size_t align = ((cache->frame_vaddr
    121 			 + (*p - (const uint8_t *) cache->data->d.d_buf))
    122 			& (size - 1));
    123 	if (align != 0)
    124 	  *p += size - align;
    125 	break;
    126       }
    127 
    128     default:
    129       abort ();
    130     }
    131 
    132   Dwarf_Addr value;
    133   switch (encoding & 0x0f)
    134     {
    135     case DW_EH_PE_udata2:
    136       value = read_2ubyte_unaligned_inc (cache, *p);
    137       break;
    138 
    139     case DW_EH_PE_sdata2:
    140       value = read_2sbyte_unaligned_inc (cache, *p);
    141       break;
    142 
    143     case DW_EH_PE_udata4:
    144       if (__libdw_cfi_read_address_inc (cache, p, 4, &value))
    145 	return true;
    146       break;
    147 
    148     case DW_EH_PE_sdata4:
    149       if (__libdw_cfi_read_address_inc (cache, p, 4, &value))
    150 	return true;
    151       value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend.  */
    152       break;
    153 
    154     case DW_EH_PE_udata8:
    155     case DW_EH_PE_sdata8:
    156       if (__libdw_cfi_read_address_inc (cache, p, 8, &value))
    157 	return true;
    158       break;
    159 
    160     case DW_EH_PE_absptr:
    161       if (__libdw_cfi_read_address_inc (cache, p, 0, &value))
    162 	return true;
    163       break;
    164 
    165     case DW_EH_PE_uleb128:
    166       // XXX we trust there is enough data.
    167       get_uleb128 (value, *p, *p + len_leb128 (Dwarf_Addr));
    168       break;
    169 
    170     case DW_EH_PE_sleb128:
    171       // XXX we trust there is enough data.
    172       get_sleb128 (value, *p, *p + len_leb128 (Dwarf_Addr));
    173       break;
    174 
    175     default:
    176       abort ();
    177     }
    178 
    179   *result += value;
    180 
    181   if (encoding & DW_EH_PE_indirect)
    182     {
    183       if (unlikely (*result < cache->frame_vaddr))
    184 	return true;
    185       *result -= cache->frame_vaddr;
    186       if (unlikely (*result > (cache->data->d.d_size
    187 			       - encoded_value_size (NULL, cache->e_ident,
    188 						     DW_EH_PE_absptr, NULL))))
    189 	return true;
    190       const uint8_t *ptr = cache->data->d.d_buf + *result;
    191       return __libdw_cfi_read_address_inc (cache, &ptr, 0, result);
    192     }
    193 
    194   return false;
    195 }
    196 
    197 #endif	/* encoded-value.h */
    198