| /* Exception handling and frame unwind runtime interface routines. |
| Copyright (C) 2001-2024 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
| License for more details. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* @@@ Really this should be out of line, but this also causes link |
| compatibility problems with the base ABI. This is slightly better |
| than duplicating code, however. */ |
| |
| #ifndef GCC_UNWIND_PE_H |
| #define GCC_UNWIND_PE_H |
| |
| /* If using C++, references to abort have to be qualified with std::. */ |
| #if __cplusplus |
| #define __gxx_abort std::abort |
| #else |
| #define __gxx_abort abort |
| #endif |
| |
| /* Pointer encodings, from dwarf2.h. */ |
| #define DW_EH_PE_absptr 0x00 |
| #define DW_EH_PE_omit 0xff |
| |
| #define DW_EH_PE_uleb128 0x01 |
| #define DW_EH_PE_udata2 0x02 |
| #define DW_EH_PE_udata4 0x03 |
| #define DW_EH_PE_udata8 0x04 |
| #define DW_EH_PE_sleb128 0x09 |
| #define DW_EH_PE_sdata2 0x0A |
| #define DW_EH_PE_sdata4 0x0B |
| #define DW_EH_PE_sdata8 0x0C |
| #define DW_EH_PE_signed 0x08 |
| |
| #define DW_EH_PE_pcrel 0x10 |
| #define DW_EH_PE_textrel 0x20 |
| #define DW_EH_PE_datarel 0x30 |
| #define DW_EH_PE_funcrel 0x40 |
| #define DW_EH_PE_aligned 0x50 |
| |
| #define DW_EH_PE_indirect 0x80 |
| |
| |
| #ifndef NO_SIZE_OF_ENCODED_VALUE |
| |
| /* Given an encoding, return the number of bytes the format occupies. |
| This is only defined for fixed-size encodings, and so does not |
| include leb128. */ |
| |
| static unsigned int |
| size_of_encoded_value (unsigned char encoding) __attribute__ ((unused)); |
| |
| static unsigned int |
| size_of_encoded_value (unsigned char encoding) |
| { |
| if (encoding == DW_EH_PE_omit) |
| return 0; |
| |
| switch (encoding & 0x07) |
| { |
| case DW_EH_PE_absptr: |
| return sizeof (void *); |
| case DW_EH_PE_udata2: |
| return 2; |
| case DW_EH_PE_udata4: |
| return 4; |
| case DW_EH_PE_udata8: |
| return 8; |
| } |
| __gxx_abort (); |
| } |
| |
| #endif |
| |
| #ifndef NO_BASE_OF_ENCODED_VALUE |
| |
| /* Given an encoding and an _Unwind_Context, return the base to which |
| the encoding is relative. This base may then be passed to |
| read_encoded_value_with_base for use when the _Unwind_Context is |
| not available. */ |
| |
| static _Unwind_Ptr |
| base_of_encoded_value (unsigned char encoding, struct _Unwind_Context *context) |
| { |
| if (encoding == DW_EH_PE_omit) |
| return 0; |
| |
| switch (encoding & 0x70) |
| { |
| case DW_EH_PE_absptr: |
| case DW_EH_PE_pcrel: |
| case DW_EH_PE_aligned: |
| return 0; |
| |
| case DW_EH_PE_textrel: |
| return _Unwind_GetTextRelBase (context); |
| case DW_EH_PE_datarel: |
| return _Unwind_GetDataRelBase (context); |
| case DW_EH_PE_funcrel: |
| return _Unwind_GetRegionStart (context); |
| } |
| __gxx_abort (); |
| } |
| |
| #endif |
| |
| /* Read an unsigned leb128 value from P, store the value in VAL, return |
| P incremented past the value. We assume that a word is large enough to |
| hold any value so encoded; if it is smaller than a pointer on some target, |
| pointers should not be leb128 encoded on that target. */ |
| |
| static const unsigned char * |
| read_uleb128 (const unsigned char *p, _uleb128_t *val) |
| { |
| unsigned int shift = 0; |
| unsigned char byte; |
| _uleb128_t result; |
| |
| result = 0; |
| do |
| { |
| byte = *p++; |
| result |= ((_uleb128_t)byte & 0x7f) << shift; |
| shift += 7; |
| } |
| while (byte & 0x80); |
| |
| *val = result; |
| return p; |
| } |
| |
| /* Similar, but read a signed leb128 value. */ |
| |
| static const unsigned char * |
| read_sleb128 (const unsigned char *p, _sleb128_t *val) |
| { |
| unsigned int shift = 0; |
| unsigned char byte; |
| _uleb128_t result; |
| |
| result = 0; |
| do |
| { |
| byte = *p++; |
| result |= ((_uleb128_t)byte & 0x7f) << shift; |
| shift += 7; |
| } |
| while (byte & 0x80); |
| |
| /* Sign-extend a negative value. */ |
| if (shift < 8 * sizeof(result) && (byte & 0x40) != 0) |
| result |= -(((_uleb128_t)1L) << shift); |
| |
| *val = (_sleb128_t) result; |
| return p; |
| } |
| |
| extern _Unwind_Ptr _Unwind_gnu_Find_got (_Unwind_Ptr); |
| |
| /* Load an encoded value from memory at P. The value is returned in VAL; |
| The function returns P incremented past the value. BASE is as given |
| by base_of_encoded_value for this encoding in the appropriate context. */ |
| |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Waddress-of-packed-member" |
| |
| static const unsigned char * |
| read_encoded_value_with_base (unsigned char encoding, _Unwind_Ptr base, |
| const unsigned char *p, _Unwind_Ptr *val) |
| { |
| union unaligned |
| { |
| void *ptr; |
| unsigned u2 __attribute__ ((mode (HI))); |
| unsigned u4 __attribute__ ((mode (SI))); |
| unsigned u8 __attribute__ ((mode (DI))); |
| signed s2 __attribute__ ((mode (HI))); |
| signed s4 __attribute__ ((mode (SI))); |
| signed s8 __attribute__ ((mode (DI))); |
| } __attribute__((__packed__)); |
| |
| const union unaligned *u = (const union unaligned *) p; |
| _Unwind_Internal_Ptr result; |
| |
| if (encoding == DW_EH_PE_aligned) |
| { |
| _Unwind_Internal_Ptr a = (_Unwind_Internal_Ptr) p; |
| a = (a + sizeof (void *) - 1) & - sizeof(void *); |
| result = *(_Unwind_Internal_Ptr *) a; |
| p = (const unsigned char *) (_Unwind_Internal_Ptr) (a + sizeof (void *)); |
| } |
| else |
| { |
| switch (encoding & 0x0f) |
| { |
| case DW_EH_PE_absptr: |
| result = (_Unwind_Internal_Ptr) u->ptr; |
| p += sizeof (void *); |
| break; |
| |
| case DW_EH_PE_uleb128: |
| { |
| _uleb128_t tmp; |
| p = read_uleb128 (p, &tmp); |
| result = (_Unwind_Internal_Ptr) tmp; |
| } |
| break; |
| |
| case DW_EH_PE_sleb128: |
| { |
| _sleb128_t tmp; |
| p = read_sleb128 (p, &tmp); |
| result = (_Unwind_Internal_Ptr) tmp; |
| } |
| break; |
| |
| case DW_EH_PE_udata2: |
| result = u->u2; |
| p += 2; |
| break; |
| case DW_EH_PE_udata4: |
| result = u->u4; |
| p += 4; |
| break; |
| case DW_EH_PE_udata8: |
| result = u->u8; |
| p += 8; |
| break; |
| |
| case DW_EH_PE_sdata2: |
| result = u->s2; |
| p += 2; |
| break; |
| case DW_EH_PE_sdata4: |
| result = u->s4; |
| p += 4; |
| break; |
| case DW_EH_PE_sdata8: |
| result = u->s8; |
| p += 8; |
| break; |
| |
| default: |
| __gxx_abort (); |
| } |
| |
| if (result != 0) |
| { |
| #if __FDPIC__ |
| /* FDPIC relative addresses imply taking the GOT address |
| into account. */ |
| if ((encoding & DW_EH_PE_pcrel) && (encoding & DW_EH_PE_indirect)) |
| { |
| result += _Unwind_gnu_Find_got ((_Unwind_Ptr) u); |
| result = *(_Unwind_Internal_Ptr *) result; |
| } |
| else |
| { |
| result += ((encoding & 0x70) == DW_EH_PE_pcrel |
| ? (_Unwind_Internal_Ptr) u : base); |
| if (encoding & DW_EH_PE_indirect) |
| result = *(_Unwind_Internal_Ptr *) result; |
| } |
| #else |
| result += ((encoding & 0x70) == DW_EH_PE_pcrel |
| ? (_Unwind_Internal_Ptr) u : base); |
| if (encoding & DW_EH_PE_indirect) |
| result = *(_Unwind_Internal_Ptr *) result; |
| #endif |
| } |
| } |
| |
| *val = result; |
| return p; |
| } |
| |
| #pragma GCC diagnostic pop |
| |
| #ifndef NO_BASE_OF_ENCODED_VALUE |
| |
| /* Like read_encoded_value_with_base, but get the base from the context |
| rather than providing it directly. */ |
| |
| static inline const unsigned char * |
| read_encoded_value (struct _Unwind_Context *context, unsigned char encoding, |
| const unsigned char *p, _Unwind_Ptr *val) |
| { |
| return read_encoded_value_with_base (encoding, |
| base_of_encoded_value (encoding, context), |
| p, val); |
| } |
| |
| #endif |
| |
| #endif /* unwind-pe.h */ |