github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/include/unwind-pe.h (about) 1 //===----------------------------- unwind-pe.h ----------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 // Pointer-Encoding decoder. Derived from: 8 // - libcxxabi/src/Unwind/dwarf2.h 9 // - libcxxabi/src/Unwind/AddressSpace.h 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef UNWIND_PE_H 14 #define UNWIND_PE_H 15 16 #include <assert.h> 17 #include <stdint.h> 18 #include <string.h> 19 20 // FSF exception handling Pointer-Encoding constants 21 // Used in CFI augmentation by GCC 22 enum { 23 DW_EH_PE_ptr = 0x00, 24 DW_EH_PE_uleb128 = 0x01, 25 DW_EH_PE_udata2 = 0x02, 26 DW_EH_PE_udata4 = 0x03, 27 DW_EH_PE_udata8 = 0x04, 28 DW_EH_PE_signed = 0x08, 29 DW_EH_PE_sleb128 = 0x09, 30 DW_EH_PE_sdata2 = 0x0A, 31 DW_EH_PE_sdata4 = 0x0B, 32 DW_EH_PE_sdata8 = 0x0C, 33 DW_EH_PE_absptr = 0x00, 34 DW_EH_PE_pcrel = 0x10, 35 DW_EH_PE_textrel = 0x20, 36 DW_EH_PE_datarel = 0x30, 37 DW_EH_PE_funcrel = 0x40, 38 DW_EH_PE_aligned = 0x50, 39 DW_EH_PE_indirect = 0x80, 40 DW_EH_PE_omit = 0xFF 41 }; 42 43 /// Read a ULEB128 into a 64-bit word. 44 static uint64_t unw_getULEB128(uintptr_t *addr) { 45 const uint8_t *p = (uint8_t *)*addr; 46 uint64_t result = 0; 47 int bit = 0; 48 do { 49 uint64_t b; 50 51 b = *p & 0x7f; 52 53 if (bit >= 64 || b << bit >> bit != b) { 54 assert(!"malformed uleb128 expression"); 55 } else { 56 result |= b << bit; 57 bit += 7; 58 } 59 } while (*p++ >= 0x80); 60 *addr = (uintptr_t) p; 61 return result; 62 } 63 64 /// Read a SLEB128 into a 64-bit word. 65 static int64_t unw_getSLEB128(uintptr_t *addr) { 66 const uint8_t *p = (uint8_t *)addr; 67 int64_t result = 0; 68 int bit = 0; 69 uint8_t byte; 70 do { 71 byte = *p++; 72 result |= ((byte & 0x7f) << bit); 73 bit += 7; 74 } while (byte & 0x80); 75 // sign extend negative numbers 76 if ((byte & 0x40) != 0) 77 result |= (-1LL) << bit; 78 *addr = (uintptr_t) p; 79 return result; 80 } 81 82 static uint16_t unw_get16(uintptr_t addr) { 83 uint16_t val; 84 memcpy(&val, (void *)addr, sizeof(val)); 85 return val; 86 } 87 88 static uint32_t unw_get32(uintptr_t addr) { 89 uint32_t val; 90 memcpy(&val, (void *)addr, sizeof(val)); 91 return val; 92 } 93 94 static uint64_t unw_get64(uintptr_t addr) { 95 uint64_t val; 96 memcpy(&val, (void *)addr, sizeof(val)); 97 return val; 98 } 99 100 static uintptr_t unw_getP(uintptr_t addr) { 101 if (sizeof(uintptr_t) == 8) 102 return unw_get64(addr); 103 else 104 return unw_get32(addr); 105 } 106 107 static const unsigned char *read_uleb128(const unsigned char *p, 108 _uleb128_t *ret) { 109 uintptr_t addr = (uintptr_t)p; 110 *ret = unw_getULEB128(&addr); 111 return (unsigned char *)addr; 112 } 113 114 static const unsigned char *read_encoded_value(struct _Unwind_Context *ctx, 115 unsigned char encoding, 116 const unsigned char *p, 117 _Unwind_Ptr *ret) { 118 uintptr_t addr = (uintptr_t)p; 119 uintptr_t startAddr = addr; 120 uintptr_t result; 121 122 (void)ctx; 123 124 // first get value 125 switch (encoding & 0x0F) { 126 case DW_EH_PE_ptr: 127 result = unw_getP(addr); 128 p += sizeof(uintptr_t); 129 break; 130 case DW_EH_PE_uleb128: 131 result = (uintptr_t)unw_getULEB128(&addr); 132 p = (const unsigned char *)addr; 133 break; 134 case DW_EH_PE_udata2: 135 result = unw_get16(addr); 136 p += 2; 137 break; 138 case DW_EH_PE_udata4: 139 result = unw_get32(addr); 140 p += 4; 141 break; 142 case DW_EH_PE_udata8: 143 result = (uintptr_t)unw_get64(addr); 144 p += 8; 145 break; 146 case DW_EH_PE_sleb128: 147 result = (uintptr_t)unw_getSLEB128(&addr); 148 p = (const unsigned char *)addr; 149 break; 150 case DW_EH_PE_sdata2: 151 // Sign extend from signed 16-bit value. 152 result = (uintptr_t)(int16_t)unw_get16(addr); 153 p += 2; 154 break; 155 case DW_EH_PE_sdata4: 156 // Sign extend from signed 32-bit value. 157 result = (uintptr_t)(int32_t)unw_get32(addr); 158 p += 4; 159 break; 160 case DW_EH_PE_sdata8: 161 result = (uintptr_t)unw_get64(addr); 162 p += 8; 163 break; 164 default: 165 assert(!"unknown pointer encoding"); 166 } 167 168 // then add relative offset 169 switch (encoding & 0x70) { 170 case DW_EH_PE_absptr: 171 // do nothing 172 break; 173 case DW_EH_PE_pcrel: 174 result += startAddr; 175 break; 176 case DW_EH_PE_textrel: 177 assert(!"DW_EH_PE_textrel pointer encoding not supported"); 178 break; 179 case DW_EH_PE_datarel: 180 assert(!"DW_EH_PE_datarel pointer encoding not supported"); 181 break; 182 case DW_EH_PE_funcrel: 183 assert(!"DW_EH_PE_funcrel pointer encoding not supported"); 184 break; 185 case DW_EH_PE_aligned: 186 assert(!"DW_EH_PE_aligned pointer encoding not supported"); 187 break; 188 default: 189 assert(!"unknown pointer encoding"); 190 break; 191 } 192 193 if (encoding & DW_EH_PE_indirect) 194 result = unw_getP(result); 195 196 *ret = result; 197 return p; 198 } 199 200 #endif // UNWIND_PE_H