1// Exception handling and frame unwind runtime interface routines. 2// Copyright (C) 2011-2020 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 26module gcc.unwind.pe; 27 28import gcc.unwind; 29import gcc.builtins; 30 31@nogc: 32 33// Pointer encodings, from dwarf2.h. 34enum 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. 61uint 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(const(ubyte)** p) 111{ 112 auto q = *p; 113 _uleb128_t result = 0; 114 uint shift = 0; 115 116 while (1) 117 { 118 ubyte b = *q++; 119 result |= cast(_uleb128_t)(b & 0x7F) << shift; 120 if ((b & 0x80) == 0) 121 break; 122 shift += 7; 123 } 124 125 *p = q; 126 return result; 127} 128 129// Similar, but read a signed leb128 value. 130_sleb128_t read_sleb128(const(ubyte)** p) 131{ 132 auto q = *p; 133 _sleb128_t result = 0; 134 uint shift = 0; 135 ubyte b = void; 136 137 while (1) 138 { 139 b = *q++; 140 result |= cast(_sleb128_t)(b & 0x7F) << shift; 141 shift += 7; 142 if ((b & 0x80) == 0) 143 break; 144 } 145 146 // Sign-extend a negative value. 147 if (shift < result.sizeof * 8 && (b & 0x40)) 148 result |= -(cast(_sleb128_t)1 << shift); 149 150 *p = q; 151 return result; 152} 153 154// Load an encoded value from memory at P. The value is returned in VAL; 155// The function returns P incremented past the value. BASE is as given 156// by base_of_encoded_value for this encoding in the appropriate context. 157_Unwind_Ptr read_encoded_value_with_base(ubyte encoding, _Unwind_Ptr base, 158 const(ubyte)** p) 159{ 160 auto q = *p; 161 _Unwind_Internal_Ptr result; 162 163 if (encoding == DW_EH_PE_aligned) 164 { 165 _Unwind_Internal_Ptr a = cast(_Unwind_Internal_Ptr)q; 166 a = cast(_Unwind_Internal_Ptr)((a + (void*).sizeof - 1) & - (void*).sizeof); 167 result = *cast(_Unwind_Internal_Ptr*)a; 168 q = cast(ubyte*) cast(_Unwind_Internal_Ptr)(a + (void*).sizeof); 169 } 170 else 171 { 172 switch (encoding & 0x0f) 173 { 174 case DW_EH_PE_uleb128: 175 result = cast(_Unwind_Internal_Ptr)read_uleb128(&q); 176 break; 177 178 case DW_EH_PE_sleb128: 179 result = cast(_Unwind_Internal_Ptr)read_sleb128(&q); 180 break; 181 182 case DW_EH_PE_udata2: 183 result = cast(_Unwind_Internal_Ptr) *cast(ushort*)q; 184 q += 2; 185 break; 186 case DW_EH_PE_udata4: 187 result = cast(_Unwind_Internal_Ptr) *cast(uint*)q; 188 q += 4; 189 break; 190 case DW_EH_PE_udata8: 191 result = cast(_Unwind_Internal_Ptr) *cast(ulong*)q; 192 q += 8; 193 break; 194 195 case DW_EH_PE_sdata2: 196 result = cast(_Unwind_Internal_Ptr) *cast(short*)q; 197 q += 2; 198 break; 199 case DW_EH_PE_sdata4: 200 result = cast(_Unwind_Internal_Ptr) *cast(int*)q; 201 q += 4; 202 break; 203 case DW_EH_PE_sdata8: 204 result = cast(_Unwind_Internal_Ptr) *cast(long*)q; 205 q += 8; 206 break; 207 208 case DW_EH_PE_absptr: 209 if (size_t.sizeof == 8) 210 goto case DW_EH_PE_udata8; 211 else 212 goto case DW_EH_PE_udata4; 213 214 default: 215 __builtin_abort(); 216 } 217 218 if (result != 0) 219 { 220 result += ((encoding & 0x70) == DW_EH_PE_pcrel 221 ? cast(_Unwind_Internal_Ptr)*p : base); 222 if (encoding & DW_EH_PE_indirect) 223 result = *cast(_Unwind_Internal_Ptr*)result; 224 } 225 } 226 227 *p = q; 228 return result; 229} 230 231// Like read_encoded_value_with_base, but get the base from the context 232// rather than providing it directly. 233_Unwind_Ptr read_encoded_value(_Unwind_Context* context, ubyte encoding, 234 const(ubyte)** p) 235{ 236 auto base = base_of_encoded_value(encoding, context); 237 return read_encoded_value_with_base(encoding, base, p); 238} 239