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