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