1/* Exception handling and frame unwind runtime interface routines.
2 Copyright (C) 2001-2024 Free Software Foundation, Inc.
3
4 This file is part of the GNU C Library.
5
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <https://www.gnu.org/licenses/>. */
19
20/* @@@ Really this should be out of line, but this also causes link
21 compatibility problems with the base ABI. This is slightly better
22 than duplicating code, however. */
23
24/* If using C++, references to abort have to be qualified with std::. */
25#ifdef __cplusplus
26#define __gxx_abort std::abort
27#else
28#define __gxx_abort abort
29#endif
30
31/* Pointer encodings, from dwarf2.h. */
32#define DW_EH_PE_absptr 0x00
33#define DW_EH_PE_omit 0xff
34
35#define DW_EH_PE_uleb128 0x01
36#define DW_EH_PE_udata2 0x02
37#define DW_EH_PE_udata4 0x03
38#define DW_EH_PE_udata8 0x04
39#define DW_EH_PE_sleb128 0x09
40#define DW_EH_PE_sdata2 0x0A
41#define DW_EH_PE_sdata4 0x0B
42#define DW_EH_PE_sdata8 0x0C
43#define DW_EH_PE_signed 0x08
44
45#define DW_EH_PE_pcrel 0x10
46#define DW_EH_PE_textrel 0x20
47#define DW_EH_PE_datarel 0x30
48#define DW_EH_PE_funcrel 0x40
49#define DW_EH_PE_aligned 0x50
50
51#define DW_EH_PE_indirect 0x80
52
53
54#if defined(_LIBC)
55
56/* Prototypes. */
57extern unsigned int size_of_encoded_value (unsigned char encoding)
58 attribute_hidden;
59
60extern const unsigned char *read_encoded_value_with_base
61 (unsigned char encoding, _Unwind_Ptr base,
62 const unsigned char *p, _Unwind_Ptr *val)
63 attribute_hidden;
64
65extern const unsigned char * read_encoded_value
66 (struct _Unwind_Context *context, unsigned char encoding,
67 const unsigned char *p, _Unwind_Ptr *val)
68 attribute_hidden;
69
70extern const unsigned char * read_uleb128 (const unsigned char *p,
71 _Unwind_Word *val)
72 attribute_hidden;
73extern const unsigned char * read_sleb128 (const unsigned char *p,
74 _Unwind_Sword *val)
75 attribute_hidden;
76
77#endif
78#if defined(_LIBC) && defined(_LIBC_DEFINITIONS)
79
80#ifdef _LIBC
81#define STATIC
82#else
83#define STATIC static
84#endif
85
86/* Given an encoding, return the number of bytes the format occupies.
87 This is only defined for fixed-size encodings, and so does not
88 include leb128. */
89
90STATIC unsigned int
91size_of_encoded_value (unsigned char encoding)
92{
93 if (encoding == DW_EH_PE_omit)
94 return 0;
95
96 switch (encoding & 0x07)
97 {
98 case DW_EH_PE_absptr:
99 return sizeof (void *);
100 case DW_EH_PE_udata2:
101 return 2;
102 case DW_EH_PE_udata4:
103 return 4;
104 case DW_EH_PE_udata8:
105 return 8;
106 }
107 __gxx_abort ();
108}
109
110#ifndef NO_BASE_OF_ENCODED_VALUE
111
112/* Given an encoding and an _Unwind_Context, return the base to which
113 the encoding is relative. This base may then be passed to
114 read_encoded_value_with_base for use when the _Unwind_Context is
115 not available. */
116
117STATIC _Unwind_Ptr
118base_of_encoded_value (unsigned char encoding, struct _Unwind_Context *context)
119{
120 if (encoding == DW_EH_PE_omit)
121 return 0;
122
123 switch (encoding & 0x70)
124 {
125 case DW_EH_PE_absptr:
126 case DW_EH_PE_pcrel:
127 case DW_EH_PE_aligned:
128 return 0;
129
130 case DW_EH_PE_textrel:
131 return _Unwind_GetTextRelBase (context);
132 case DW_EH_PE_datarel:
133 return _Unwind_GetDataRelBase (context);
134 case DW_EH_PE_funcrel:
135 return _Unwind_GetRegionStart (context);
136 }
137 __gxx_abort ();
138}
139
140#endif
141
142/* Read an unsigned leb128 value from P, store the value in VAL, return
143 P incremented past the value. We assume that a word is large enough to
144 hold any value so encoded; if it is smaller than a pointer on some target,
145 pointers should not be leb128 encoded on that target. */
146
147STATIC const unsigned char *
148read_uleb128 (const unsigned char *p, _Unwind_Word *val)
149{
150 unsigned int shift = 0;
151 unsigned char byte;
152 _Unwind_Word result;
153
154 result = 0;
155 do
156 {
157 byte = *p++;
158 result |= (byte & 0x7f) << shift;
159 shift += 7;
160 }
161 while (byte & 0x80);
162
163 *val = result;
164 return p;
165}
166
167/* Similar, but read a signed leb128 value. */
168
169STATIC const unsigned char *
170read_sleb128 (const unsigned char *p, _Unwind_Sword *val)
171{
172 unsigned int shift = 0;
173 unsigned char byte;
174 _Unwind_Word result;
175
176 result = 0;
177 do
178 {
179 byte = *p++;
180 result |= (byte & 0x7f) << shift;
181 shift += 7;
182 }
183 while (byte & 0x80);
184
185 /* Sign-extend a negative value. */
186 if (shift < 8 * sizeof (result) && (byte & 0x40) != 0)
187 result |= -(1L << shift);
188
189 *val = (_Unwind_Sword) result;
190 return p;
191}
192
193/* Load an encoded value from memory at P. The value is returned in VAL;
194 The function returns P incremented past the value. BASE is as given
195 by base_of_encoded_value for this encoding in the appropriate context. */
196
197STATIC const unsigned char *
198read_encoded_value_with_base (unsigned char encoding, _Unwind_Ptr base,
199 const unsigned char *p, _Unwind_Ptr *val)
200{
201 union unaligned
202 {
203 void *ptr;
204 unsigned u2 __attribute__ ((mode (HI)));
205 unsigned u4 __attribute__ ((mode (SI)));
206 unsigned u8 __attribute__ ((mode (DI)));
207 signed s2 __attribute__ ((mode (HI)));
208 signed s4 __attribute__ ((mode (SI)));
209 signed s8 __attribute__ ((mode (DI)));
210 } __attribute__((__packed__));
211
212 union unaligned *u = (union unaligned *) p;
213 _Unwind_Internal_Ptr result;
214
215 if (encoding == DW_EH_PE_aligned)
216 {
217 _Unwind_Internal_Ptr a = (_Unwind_Internal_Ptr) p;
218 a = (a + sizeof (void *) - 1) & - sizeof (void *);
219 result = *(_Unwind_Internal_Ptr *) a;
220 p = (const unsigned char *) (a + sizeof (void *));
221 }
222 else
223 {
224 switch (encoding & 0x0f)
225 {
226 case DW_EH_PE_absptr:
227 result = (_Unwind_Internal_Ptr) u->ptr;
228 p += sizeof (void *);
229 break;
230
231 case DW_EH_PE_uleb128:
232 {
233 _Unwind_Word tmp;
234 p = read_uleb128 (p, &tmp);
235 result = (_Unwind_Internal_Ptr) tmp;
236 }
237 break;
238
239 case DW_EH_PE_sleb128:
240 {
241 _Unwind_Sword tmp;
242 p = read_sleb128 (p, &tmp);
243 result = (_Unwind_Internal_Ptr) tmp;
244 }
245 break;
246
247 case DW_EH_PE_udata2:
248 result = u->u2;
249 p += 2;
250 break;
251 case DW_EH_PE_udata4:
252 result = u->u4;
253 p += 4;
254 break;
255 case DW_EH_PE_udata8:
256 result = u->u8;
257 p += 8;
258 break;
259
260 case DW_EH_PE_sdata2:
261 result = u->s2;
262 p += 2;
263 break;
264 case DW_EH_PE_sdata4:
265 result = u->s4;
266 p += 4;
267 break;
268 case DW_EH_PE_sdata8:
269 result = u->s8;
270 p += 8;
271 break;
272
273 default:
274 __gxx_abort ();
275 }
276
277 if (result != 0)
278 {
279 result += ((encoding & 0x70) == DW_EH_PE_pcrel
280 ? (_Unwind_Internal_Ptr) u : base);
281 if (encoding & DW_EH_PE_indirect)
282 result = *(_Unwind_Internal_Ptr *) result;
283 }
284 }
285
286 *val = result;
287 return p;
288}
289
290#ifndef NO_BASE_OF_ENCODED_VALUE
291
292/* Like read_encoded_value_with_base, but get the base from the context
293 rather than providing it directly. */
294
295STATIC const unsigned char *
296read_encoded_value (struct _Unwind_Context *context, unsigned char encoding,
297 const unsigned char *p, _Unwind_Ptr *val)
298{
299 return read_encoded_value_with_base (encoding,
300 base_of_encoded_value (encoding, context),
301 p, val);
302}
303
304#endif
305#endif /* _LIBC */
306

source code of glibc/sysdeps/generic/unwind-pe.h