Line data Source code
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 21 : encoded_value_size (const Elf_Data *data, const unsigned char e_ident[],
42 : uint8_t encoding, const uint8_t *p)
43 : {
44 4700 : if (encoding == DW_EH_PE_omit)
45 : return 0;
46 :
47 4700 : switch (encoding & 0x07)
48 : {
49 : case DW_EH_PE_udata2:
50 : return 2;
51 4686 : case DW_EH_PE_udata4:
52 7 : return 4;
53 1 : case DW_EH_PE_udata8:
54 1 : return 8;
55 :
56 13 : case DW_EH_PE_absptr:
57 13 : return e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
58 :
59 0 : case DW_EH_PE_uleb128:
60 0 : if (p != NULL)
61 : {
62 : const uint8_t *end = p;
63 0 : while (end < (uint8_t *) data->d_buf + data->d_size)
64 0 : if (*end++ & 0x80u)
65 0 : return end - p;
66 : }
67 : return 0;
68 :
69 0 : default:
70 0 : return 0;
71 : }
72 : }
73 :
74 : /* Returns zero when value was read successfully, minus one otherwise. */
75 : static inline int __attribute__ ((unused))
76 115453 : __libdw_cfi_read_address_inc (const Dwarf_CFI *cache,
77 : const unsigned char **addrp,
78 : int width, Dwarf_Addr *ret)
79 : {
80 115453 : width = width ?: cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
81 :
82 115453 : if (cache->dbg != NULL)
83 168 : 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 115285 : const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
90 115285 : Dwarf eh_dbg = { .other_byte_order = MY_ELFDATA != cache->e_ident[EI_DATA] };
91 :
92 115285 : if (width == 4)
93 : {
94 115285 : if (unlikely (*addrp + 4 > endp))
95 : {
96 0 : invalid_data:
97 0 : __libdw_seterrno (DWARF_E_INVALID_CFI);
98 0 : return -1;
99 : }
100 124115 : *ret = read_4ubyte_unaligned_inc (&eh_dbg, *addrp);
101 : }
102 : else
103 : {
104 0 : if (unlikely (*addrp + 8 > endp))
105 : goto invalid_data;
106 0 : *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 115453 : read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding,
114 : const uint8_t **p, Dwarf_Addr *result)
115 : {
116 115453 : *result = 0;
117 115453 : switch (encoding & 0x70)
118 : {
119 : case DW_EH_PE_absptr:
120 : break;
121 17349 : case DW_EH_PE_pcrel:
122 34698 : *result = (cache->frame_vaddr
123 17349 : + (*p - (const uint8_t *) cache->data->d.d_buf));
124 17349 : break;
125 0 : case DW_EH_PE_textrel:
126 : // ia64: segrel
127 0 : *result = cache->textrel;
128 0 : break;
129 80577 : case DW_EH_PE_datarel:
130 : // i386: GOTOFF
131 : // ia64: gprel
132 80577 : *result = cache->datarel;
133 80577 : break;
134 : case DW_EH_PE_funcrel: /* XXX */
135 : break;
136 0 : case DW_EH_PE_aligned:
137 : {
138 0 : const size_t size = encoded_value_size (&cache->data->d,
139 : cache->e_ident,
140 : encoding, *p);
141 0 : if (unlikely (size == 0))
142 : return true;
143 0 : size_t align = ((cache->frame_vaddr
144 0 : + (*p - (const uint8_t *) cache->data->d.d_buf))
145 0 : & (size - 1));
146 0 : if (align != 0)
147 0 : *p += size - align;
148 : break;
149 : }
150 :
151 0 : default:
152 0 : __libdw_seterrno (DWARF_E_INVALID_CFI);
153 0 : return true;
154 : }
155 :
156 115453 : Dwarf_Addr value = 0;
157 115453 : const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
158 115453 : switch (encoding & 0x0f)
159 : {
160 0 : case DW_EH_PE_udata2:
161 0 : if (unlikely (*p + 2 > endp))
162 : {
163 0 : invalid_data:
164 0 : __libdw_seterrno (DWARF_E_INVALID_CFI);
165 0 : return true;
166 : }
167 0 : value = read_2ubyte_unaligned_inc (cache, *p);
168 0 : break;
169 :
170 0 : case DW_EH_PE_sdata2:
171 0 : if (unlikely (*p + 2 > endp))
172 : goto invalid_data;
173 0 : value = read_2sbyte_unaligned_inc (cache, *p);
174 0 : break;
175 :
176 111 : case DW_EH_PE_udata4:
177 111 : if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
178 : return true;
179 : break;
180 :
181 115206 : case DW_EH_PE_sdata4:
182 115206 : if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
183 : return true;
184 115206 : value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend. */
185 115206 : break;
186 :
187 136 : case DW_EH_PE_udata8:
188 : case DW_EH_PE_sdata8:
189 136 : if (unlikely (__libdw_cfi_read_address_inc (cache, p, 8, &value) != 0))
190 : return true;
191 : break;
192 :
193 0 : case DW_EH_PE_absptr:
194 0 : if (unlikely (__libdw_cfi_read_address_inc (cache, p, 0, &value) != 0))
195 : return true;
196 : break;
197 :
198 0 : case DW_EH_PE_uleb128:
199 0 : get_uleb128 (value, *p, endp);
200 0 : break;
201 :
202 0 : case DW_EH_PE_sleb128:
203 0 : get_sleb128 (value, *p, endp);
204 0 : break;
205 :
206 0 : default:
207 0 : __libdw_seterrno (DWARF_E_INVALID_CFI);
208 0 : return true;
209 : }
210 :
211 115453 : *result += value;
212 :
213 115453 : if (encoding & DW_EH_PE_indirect)
214 : {
215 0 : if (unlikely (*result < cache->frame_vaddr))
216 0 : return true;
217 0 : *result -= cache->frame_vaddr;
218 0 : size_t ptrsize = encoded_value_size (NULL, cache->e_ident,
219 : DW_EH_PE_absptr, NULL);
220 0 : if (unlikely (cache->data->d.d_size < ptrsize
221 : || *result > (cache->data->d.d_size - ptrsize)))
222 : return true;
223 0 : const uint8_t *ptr = cache->data->d.d_buf + *result;
224 0 : 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 */
|