| // Exception handling and frame unwind runtime interface routines. |
| // Copyright (C) 2011-2021 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(ref const(ubyte)* p) |
| { |
| _uleb128_t result = 0; |
| uint shift = 0; |
| |
| while (1) |
| { |
| ubyte b = *p++; |
| result |= cast(_uleb128_t)(b & 0x7F) << shift; |
| if ((b & 0x80) == 0) |
| break; |
| shift += 7; |
| } |
| |
| return result; |
| } |
| |
| // Similar, but read a signed leb128 value. |
| _sleb128_t read_sleb128(ref const(ubyte)* p) |
| { |
| _sleb128_t result = 0; |
| uint shift = 0; |
| ubyte b = void; |
| |
| while (1) |
| { |
| b = *p++; |
| 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); |
| |
| return result; |
| } |
| |
| // Similar, but read an unaligned value of type T. |
| pragma(inline, true) |
| private T read_unaligned(T)(ref const(ubyte)* p) |
| { |
| version (X86) enum hasUnalignedLoads = true; |
| else version (X86_64) enum hasUnalignedLoads = true; |
| else enum hasUnalignedLoads = false; |
| |
| static if (hasUnalignedLoads) |
| { |
| T result = *cast(T*)p; |
| } |
| else |
| { |
| import core.stdc.string : memcpy; |
| T result = void; |
| memcpy(&result, p, T.sizeof); |
| } |
| p += T.sizeof; |
| return result; |
| } |
| |
| // Load an encoded value from memory at P. The function returns the |
| // encoded value. P is 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, |
| ref const(ubyte)* p) |
| { |
| auto psave = p; |
| _Unwind_Internal_Ptr result; |
| |
| if (encoding == DW_EH_PE_aligned) |
| { |
| _Unwind_Internal_Ptr a = cast(_Unwind_Internal_Ptr)p; |
| a = cast(_Unwind_Internal_Ptr)((a + (void*).sizeof - 1) & - (void*).sizeof); |
| result = *cast(_Unwind_Internal_Ptr*)a; |
| p = 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(p); |
| break; |
| |
| case DW_EH_PE_sleb128: |
| result = cast(_Unwind_Internal_Ptr)read_sleb128(p); |
| break; |
| |
| case DW_EH_PE_udata2: |
| result = cast(_Unwind_Internal_Ptr)read_unaligned!ushort(p); |
| break; |
| case DW_EH_PE_udata4: |
| result = cast(_Unwind_Internal_Ptr)read_unaligned!uint(p); |
| break; |
| case DW_EH_PE_udata8: |
| result = cast(_Unwind_Internal_Ptr)read_unaligned!ulong(p); |
| break; |
| |
| case DW_EH_PE_sdata2: |
| result = cast(_Unwind_Internal_Ptr)read_unaligned!short(p); |
| break; |
| case DW_EH_PE_sdata4: |
| result = cast(_Unwind_Internal_Ptr)read_unaligned!int(p); |
| break; |
| case DW_EH_PE_sdata8: |
| result = cast(_Unwind_Internal_Ptr)read_unaligned!long(p); |
| break; |
| |
| case DW_EH_PE_absptr: |
| result = cast(_Unwind_Internal_Ptr)read_unaligned!(size_t)(p); |
| break; |
| |
| default: |
| __builtin_abort(); |
| } |
| |
| if (result != 0) |
| { |
| result += ((encoding & 0x70) == DW_EH_PE_pcrel |
| ? cast(_Unwind_Internal_Ptr)psave : base); |
| if (encoding & DW_EH_PE_indirect) |
| result = *cast(_Unwind_Internal_Ptr*)result; |
| } |
| } |
| |
| 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, |
| ref const(ubyte)* p) |
| { |
| auto base = base_of_encoded_value(encoding, context); |
| return read_encoded_value_with_base(encoding, base, p); |
| } |