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