1 /* Exception handling and frame unwind runtime interface routines.
2    Copyright (C) 2001-2021 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.  */
57 extern unsigned int size_of_encoded_value (unsigned char encoding)
58   attribute_hidden;
59 
60 extern 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 
65 extern 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 
70 extern const unsigned char * read_uleb128 (const unsigned char *p,
71 					   _Unwind_Word *val)
72   attribute_hidden;
73 extern 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 
90 STATIC unsigned int
size_of_encoded_value(unsigned char encoding)91 size_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 
117 STATIC _Unwind_Ptr
base_of_encoded_value(unsigned char encoding,struct _Unwind_Context * context)118 base_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 
147 STATIC const unsigned char *
read_uleb128(const unsigned char * p,_Unwind_Word * val)148 read_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 
169 STATIC const unsigned char *
read_sleb128(const unsigned char * p,_Unwind_Sword * val)170 read_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 
197 STATIC const unsigned char *
read_encoded_value_with_base(unsigned char encoding,_Unwind_Ptr base,const unsigned char * p,_Unwind_Ptr * val)198 read_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 
295 STATIC const unsigned char *
read_encoded_value(struct _Unwind_Context * context,unsigned char encoding,const unsigned char * p,_Unwind_Ptr * val)296 read_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