Home | History | Annotate | Line # | Download | only in unwind
      1 // Exception handling and frame unwind runtime interface routines.
      2 // Copyright (C) 2011-2022 Free Software Foundation, Inc.
      3 
      4 // GCC is free software; you can redistribute it and/or modify it under
      5 // the terms of the GNU General Public License as published by the Free
      6 // Software Foundation; either version 3, or (at your option) any later
      7 // version.
      8 
      9 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     10 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
     11 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     12 // for more details.
     13 
     14 // Under Section 7 of GPL version 3, you are granted additional
     15 // permissions described in the GCC Runtime Library Exception, version
     16 // 3.1, as published by the Free Software Foundation.
     17 
     18 // You should have received a copy of the GNU General Public License and
     19 // a copy of the GCC Runtime Library Exception along with this program;
     20 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     21 // <http://www.gnu.org/licenses/>.
     22 
     23 // extern(C) interface for the GNU/GCC pointer encoding library.
     24 // This corresponds to unwind-pe.h
     25 
     26 module gcc.unwind.pe;
     27 
     28 import gcc.unwind;
     29 import gcc.builtins;
     30 
     31 @nogc:
     32 
     33 // Pointer encodings, from dwarf2.h.
     34 enum
     35 {
     36     DW_EH_PE_absptr   = 0x00,
     37     DW_EH_PE_omit     = 0xff,
     38 
     39     DW_EH_PE_uleb128  = 0x01,
     40     DW_EH_PE_udata2   = 0x02,
     41     DW_EH_PE_udata4   = 0x03,
     42     DW_EH_PE_udata8   = 0x04,
     43     DW_EH_PE_sleb128  = 0x09,
     44     DW_EH_PE_sdata2   = 0x0A,
     45     DW_EH_PE_sdata4   = 0x0B,
     46     DW_EH_PE_sdata8   = 0x0C,
     47     DW_EH_PE_signed   = 0x08,
     48 
     49     DW_EH_PE_pcrel    = 0x10,
     50     DW_EH_PE_textrel  = 0x20,
     51     DW_EH_PE_datarel  = 0x30,
     52     DW_EH_PE_funcrel  = 0x40,
     53     DW_EH_PE_aligned  = 0x50,
     54 
     55     DW_EH_PE_indirect = 0x80
     56 }
     57 
     58 // Given an encoding, return the number of bytes the format occupies.
     59 // This is only defined for fixed-size encodings, and so does not
     60 // include leb128.
     61 uint size_of_encoded_value(ubyte encoding)
     62 {
     63     if (encoding == DW_EH_PE_omit)
     64         return 0;
     65 
     66     final switch (encoding & 0x07)
     67     {
     68         case DW_EH_PE_absptr:
     69             return (void*).sizeof;
     70         case DW_EH_PE_udata2:
     71             return 2;
     72         case DW_EH_PE_udata4:
     73             return 4;
     74         case DW_EH_PE_udata8:
     75             return 8;
     76     }
     77     assert(0);
     78 }
     79 
     80 // Given an encoding and an _Unwind_Context, return the base to which
     81 // the encoding is relative.  This base may then be passed to
     82 // read_encoded_value_with_base for use when the _Unwind_Context is
     83 // not available.
     84 _Unwind_Ptr base_of_encoded_value(ubyte encoding, _Unwind_Context* context)
     85 {
     86     if (encoding == DW_EH_PE_omit)
     87         return cast(_Unwind_Ptr) 0;
     88 
     89     final switch (encoding & 0x70)
     90     {
     91         case DW_EH_PE_absptr:
     92         case DW_EH_PE_pcrel:
     93         case DW_EH_PE_aligned:
     94             return cast(_Unwind_Ptr) 0;
     95 
     96         case DW_EH_PE_textrel:
     97             return _Unwind_GetTextRelBase(context);
     98         case DW_EH_PE_datarel:
     99             return _Unwind_GetDataRelBase(context);
    100         case DW_EH_PE_funcrel:
    101             return _Unwind_GetRegionStart(context);
    102     }
    103     assert(0);
    104 }
    105 
    106 // Read an unsigned leb128 value from P, P is incremented past the value.
    107 // We assume that a word is large enough to hold any value so encoded;
    108 // if it is smaller than a pointer on some target, pointers should not be
    109 // leb128 encoded on that target.
    110 _uleb128_t read_uleb128(ref const(ubyte)* p)
    111 {
    112     _uleb128_t result = 0;
    113     uint shift = 0;
    114 
    115     while (1)
    116     {
    117         ubyte b = *p++;
    118         result |= cast(_uleb128_t)(b & 0x7F) << shift;
    119         if ((b & 0x80) == 0)
    120             break;
    121         shift += 7;
    122     }
    123 
    124     return result;
    125 }
    126 
    127 // Similar, but read a signed leb128 value.
    128 _sleb128_t read_sleb128(ref const(ubyte)* p)
    129 {
    130     _sleb128_t result = 0;
    131     uint shift = 0;
    132     ubyte b = void;
    133 
    134     while (1)
    135     {
    136         b = *p++;
    137         result |= cast(_sleb128_t)(b & 0x7F) << shift;
    138         shift += 7;
    139         if ((b & 0x80) == 0)
    140             break;
    141     }
    142 
    143     // Sign-extend a negative value.
    144     if (shift < result.sizeof * 8 && (b & 0x40))
    145         result |= -(cast(_sleb128_t)1 << shift);
    146 
    147     return result;
    148 }
    149 
    150 // Similar, but read an unaligned value of type T.
    151 pragma(inline, true)
    152 private T read_unaligned(T)(ref const(ubyte)* p)
    153 {
    154     version (X86)         enum hasUnalignedLoads = true;
    155     else version (X86_64) enum hasUnalignedLoads = true;
    156     else                  enum hasUnalignedLoads = false;
    157 
    158     static if (hasUnalignedLoads)
    159     {
    160         T result = *cast(T*)p;
    161     }
    162     else
    163     {
    164         import core.stdc.string : memcpy;
    165         T result = void;
    166         memcpy(&result, p, T.sizeof);
    167     }
    168     p += T.sizeof;
    169     return result;
    170 }
    171 
    172 // Load an encoded value from memory at P.  The function returns the
    173 // encoded value.  P is incremented past the value.  BASE is as given
    174 // by base_of_encoded_value for this encoding in the appropriate context.
    175 _Unwind_Ptr read_encoded_value_with_base(ubyte encoding, _Unwind_Ptr base,
    176                                          ref const(ubyte)* p)
    177 {
    178     auto psave = p;
    179     _Unwind_Internal_Ptr result;
    180 
    181     if (encoding == DW_EH_PE_aligned)
    182     {
    183         _Unwind_Internal_Ptr a = cast(_Unwind_Internal_Ptr)p;
    184         a = cast(_Unwind_Internal_Ptr)((a + (void*).sizeof - 1) & - (void*).sizeof);
    185         result = *cast(_Unwind_Internal_Ptr*)a;
    186         p = cast(ubyte*) cast(_Unwind_Internal_Ptr)(a + (void*).sizeof);
    187     }
    188     else
    189     {
    190         switch (encoding & 0x0f)
    191         {
    192             case DW_EH_PE_uleb128:
    193                 result = cast(_Unwind_Internal_Ptr)read_uleb128(p);
    194                 break;
    195 
    196             case DW_EH_PE_sleb128:
    197                 result = cast(_Unwind_Internal_Ptr)read_sleb128(p);
    198                 break;
    199 
    200             case DW_EH_PE_udata2:
    201                 result = cast(_Unwind_Internal_Ptr)read_unaligned!ushort(p);
    202                 break;
    203             case DW_EH_PE_udata4:
    204                 result = cast(_Unwind_Internal_Ptr)read_unaligned!uint(p);
    205                 break;
    206             case DW_EH_PE_udata8:
    207                 result = cast(_Unwind_Internal_Ptr)read_unaligned!ulong(p);
    208                 break;
    209 
    210             case DW_EH_PE_sdata2:
    211                 result = cast(_Unwind_Internal_Ptr)read_unaligned!short(p);
    212                 break;
    213             case DW_EH_PE_sdata4:
    214                 result = cast(_Unwind_Internal_Ptr)read_unaligned!int(p);
    215                 break;
    216             case DW_EH_PE_sdata8:
    217                 result = cast(_Unwind_Internal_Ptr)read_unaligned!long(p);
    218                 break;
    219 
    220             case DW_EH_PE_absptr:
    221                 result = cast(_Unwind_Internal_Ptr)read_unaligned!(size_t)(p);
    222                 break;
    223 
    224             default:
    225                 __builtin_abort();
    226         }
    227 
    228         if (result != 0)
    229         {
    230             result += ((encoding & 0x70) == DW_EH_PE_pcrel
    231                        ? cast(_Unwind_Internal_Ptr)psave : base);
    232             if (encoding & DW_EH_PE_indirect)
    233                 result = *cast(_Unwind_Internal_Ptr*)result;
    234         }
    235     }
    236 
    237     return result;
    238 }
    239 
    240 // Like read_encoded_value_with_base, but get the base from the context
    241 // rather than providing it directly.
    242 _Unwind_Ptr read_encoded_value(_Unwind_Context* context, ubyte encoding,
    243                                ref const(ubyte)* p)
    244 {
    245     auto base = base_of_encoded_value(encoding, context);
    246     return read_encoded_value_with_base(encoding, base, p);
    247 }
    248