| // Exception handling and frame unwind runtime interface routines. |
| // Copyright (C) 2011-2020 Free Software Foundation, Inc. |
| |
| // 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/>. |
| |
| // extern(C) interface for the GNU/GCC pointer encoding library. |
| // This corresponds to unwind-pe.h |
| |
| module gcc.unwind.pe; |
| |
| import gcc.unwind; |
| import gcc.builtins; |
| |
| @nogc: |
| |
| // Pointer encodings, from dwarf2.h. |
| enum |
| { |
| DW_EH_PE_absptr = 0x00, |
| DW_EH_PE_omit = 0xff, |
| |
| DW_EH_PE_uleb128 = 0x01, |
| DW_EH_PE_udata2 = 0x02, |
| DW_EH_PE_udata4 = 0x03, |
| DW_EH_PE_udata8 = 0x04, |
| DW_EH_PE_sleb128 = 0x09, |
| DW_EH_PE_sdata2 = 0x0A, |
| DW_EH_PE_sdata4 = 0x0B, |
| DW_EH_PE_sdata8 = 0x0C, |
| DW_EH_PE_signed = 0x08, |
| |
| DW_EH_PE_pcrel = 0x10, |
| DW_EH_PE_textrel = 0x20, |
| DW_EH_PE_datarel = 0x30, |
| DW_EH_PE_funcrel = 0x40, |
| DW_EH_PE_aligned = 0x50, |
| |
| DW_EH_PE_indirect = 0x80 |
| } |
| |
| // 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. |
| uint size_of_encoded_value(ubyte encoding) |
| { |
| if (encoding == DW_EH_PE_omit) |
| return 0; |
| |
| final switch (encoding & 0x07) |
| { |
| case DW_EH_PE_absptr: |
| return (void*).sizeof; |
| case DW_EH_PE_udata2: |
| return 2; |
| case DW_EH_PE_udata4: |
| return 4; |
| case DW_EH_PE_udata8: |
| return 8; |
| } |
| assert(0); |
| } |
| |
| // 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. |
| _Unwind_Ptr base_of_encoded_value(ubyte encoding, _Unwind_Context* context) |
| { |
| if (encoding == DW_EH_PE_omit) |
| return cast(_Unwind_Ptr) 0; |
| |
| final switch (encoding & 0x70) |
| { |
| case DW_EH_PE_absptr: |
| case DW_EH_PE_pcrel: |
| case DW_EH_PE_aligned: |
| return cast(_Unwind_Ptr) 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); |
| } |
| assert(0); |
| } |
| |
| // Read an unsigned leb128 value from P, *P is 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. |
| _uleb128_t read_uleb128(const(ubyte)** p) |
| { |
| auto q = *p; |
| _uleb128_t result = 0; |
| uint shift = 0; |
| |
| while (1) |
| { |
| ubyte b = *q++; |
| result |= cast(_uleb128_t)(b & 0x7F) << shift; |
| if ((b & 0x80) == 0) |
| break; |
| shift += 7; |
| } |
| |
| *p = q; |
| return result; |
| } |
| |
| // Similar, but read a signed leb128 value. |
| _sleb128_t read_sleb128(const(ubyte)** p) |
| { |
| auto q = *p; |
| _sleb128_t result = 0; |
| uint shift = 0; |
| ubyte b = void; |
| |
| while (1) |
| { |
| b = *q++; |
| result |= cast(_sleb128_t)(b & 0x7F) << shift; |
| shift += 7; |
| if ((b & 0x80) == 0) |
| break; |
| } |
| |
| // Sign-extend a negative value. |
| if (shift < result.sizeof * 8 && (b & 0x40)) |
| result |= -(cast(_sleb128_t)1 << shift); |
| |
| *p = q; |
| return result; |
| } |
| |
| // 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. |
| _Unwind_Ptr read_encoded_value_with_base(ubyte encoding, _Unwind_Ptr base, |
| const(ubyte)** p) |
| { |
| auto q = *p; |
| _Unwind_Internal_Ptr result; |
| |
| if (encoding == DW_EH_PE_aligned) |
| { |
| _Unwind_Internal_Ptr a = cast(_Unwind_Internal_Ptr)q; |
| a = cast(_Unwind_Internal_Ptr)((a + (void*).sizeof - 1) & - (void*).sizeof); |
| result = *cast(_Unwind_Internal_Ptr*)a; |
| q = cast(ubyte*) cast(_Unwind_Internal_Ptr)(a + (void*).sizeof); |
| } |
| else |
| { |
| switch (encoding & 0x0f) |
| { |
| case DW_EH_PE_uleb128: |
| result = cast(_Unwind_Internal_Ptr)read_uleb128(&q); |
| break; |
| |
| case DW_EH_PE_sleb128: |
| result = cast(_Unwind_Internal_Ptr)read_sleb128(&q); |
| break; |
| |
| case DW_EH_PE_udata2: |
| result = cast(_Unwind_Internal_Ptr) *cast(ushort*)q; |
| q += 2; |
| break; |
| case DW_EH_PE_udata4: |
| result = cast(_Unwind_Internal_Ptr) *cast(uint*)q; |
| q += 4; |
| break; |
| case DW_EH_PE_udata8: |
| result = cast(_Unwind_Internal_Ptr) *cast(ulong*)q; |
| q += 8; |
| break; |
| |
| case DW_EH_PE_sdata2: |
| result = cast(_Unwind_Internal_Ptr) *cast(short*)q; |
| q += 2; |
| break; |
| case DW_EH_PE_sdata4: |
| result = cast(_Unwind_Internal_Ptr) *cast(int*)q; |
| q += 4; |
| break; |
| case DW_EH_PE_sdata8: |
| result = cast(_Unwind_Internal_Ptr) *cast(long*)q; |
| q += 8; |
| break; |
| |
| case DW_EH_PE_absptr: |
| if (size_t.sizeof == 8) |
| goto case DW_EH_PE_udata8; |
| else |
| goto case DW_EH_PE_udata4; |
| |
| default: |
| __builtin_abort(); |
| } |
| |
| if (result != 0) |
| { |
| result += ((encoding & 0x70) == DW_EH_PE_pcrel |
| ? cast(_Unwind_Internal_Ptr)*p : base); |
| if (encoding & DW_EH_PE_indirect) |
| result = *cast(_Unwind_Internal_Ptr*)result; |
| } |
| } |
| |
| *p = q; |
| return result; |
| } |
| |
| // Like read_encoded_value_with_base, but get the base from the context |
| // rather than providing it directly. |
| _Unwind_Ptr read_encoded_value(_Unwind_Context* context, ubyte encoding, |
| const(ubyte)** p) |
| { |
| auto base = base_of_encoded_value(encoding, context); |
| return read_encoded_value_with_base(encoding, base, p); |
| } |