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))
encoded_value_size(const Elf_Data * data,const unsigned char e_ident[],uint8_t encoding,const uint8_t * p)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))
__libdw_cfi_read_address_inc(const Dwarf_CFI * cache,const unsigned char ** addrp,int width,Dwarf_Addr * ret)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))
read_encoded_value(const Dwarf_CFI * cache,uint8_t encoding,const uint8_t ** p,Dwarf_Addr * result)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