//===----------------------------- unwind-pe.h ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // Pointer-Encoding decoder. Derived from: // - libcxxabi/src/Unwind/dwarf2.h // - libcxxabi/src/Unwind/AddressSpace.h // //===----------------------------------------------------------------------===// #ifndef UNWIND_PE_H #define UNWIND_PE_H #include #include #include // FSF exception handling Pointer-Encoding constants // Used in CFI augmentation by GCC enum { DW_EH_PE_ptr = 0x00, DW_EH_PE_uleb128 = 0x01, DW_EH_PE_udata2 = 0x02, DW_EH_PE_udata4 = 0x03, DW_EH_PE_udata8 = 0x04, DW_EH_PE_signed = 0x08, DW_EH_PE_sleb128 = 0x09, DW_EH_PE_sdata2 = 0x0A, DW_EH_PE_sdata4 = 0x0B, DW_EH_PE_sdata8 = 0x0C, DW_EH_PE_absptr = 0x00, 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, DW_EH_PE_omit = 0xFF }; /// Read a ULEB128 into a 64-bit word. static uint64_t unw_getULEB128(uintptr_t *addr) { const uint8_t *p = (uint8_t *)*addr; uint64_t result = 0; int bit = 0; do { uint64_t b; b = *p & 0x7f; if (bit >= 64 || b << bit >> bit != b) { assert(!"malformed uleb128 expression"); } else { result |= b << bit; bit += 7; } } while (*p++ >= 0x80); *addr = (uintptr_t) p; return result; } /// Read a SLEB128 into a 64-bit word. static int64_t unw_getSLEB128(uintptr_t *addr) { const uint8_t *p = (uint8_t *)addr; int64_t result = 0; int bit = 0; uint8_t byte; do { byte = *p++; result |= ((byte & 0x7f) << bit); bit += 7; } while (byte & 0x80); // sign extend negative numbers if ((byte & 0x40) != 0) result |= (-1LL) << bit; *addr = (uintptr_t) p; return result; } static uint16_t unw_get16(uintptr_t addr) { uint16_t val; memcpy(&val, (void *)addr, sizeof(val)); return val; } static uint32_t unw_get32(uintptr_t addr) { uint32_t val; memcpy(&val, (void *)addr, sizeof(val)); return val; } static uint64_t unw_get64(uintptr_t addr) { uint64_t val; memcpy(&val, (void *)addr, sizeof(val)); return val; } static uintptr_t unw_getP(uintptr_t addr) { if (sizeof(uintptr_t) == 8) return unw_get64(addr); else return unw_get32(addr); } static const unsigned char *read_uleb128(const unsigned char *p, _uleb128_t *ret) { uintptr_t addr = (uintptr_t)p; *ret = unw_getULEB128(&addr); return (unsigned char *)addr; } static const unsigned char *read_encoded_value(struct _Unwind_Context *ctx, unsigned char encoding, const unsigned char *p, _Unwind_Ptr *ret) { uintptr_t addr = (uintptr_t)p; uintptr_t startAddr = addr; uintptr_t result; (void)ctx; // first get value switch (encoding & 0x0F) { case DW_EH_PE_ptr: result = unw_getP(addr); p += sizeof(uintptr_t); break; case DW_EH_PE_uleb128: result = (uintptr_t)unw_getULEB128(&addr); p = (const unsigned char *)addr; break; case DW_EH_PE_udata2: result = unw_get16(addr); p += 2; break; case DW_EH_PE_udata4: result = unw_get32(addr); p += 4; break; case DW_EH_PE_udata8: result = (uintptr_t)unw_get64(addr); p += 8; break; case DW_EH_PE_sleb128: result = (uintptr_t)unw_getSLEB128(&addr); p = (const unsigned char *)addr; break; case DW_EH_PE_sdata2: // Sign extend from signed 16-bit value. result = (uintptr_t)(int16_t)unw_get16(addr); p += 2; break; case DW_EH_PE_sdata4: // Sign extend from signed 32-bit value. result = (uintptr_t)(int32_t)unw_get32(addr); p += 4; break; case DW_EH_PE_sdata8: result = (uintptr_t)unw_get64(addr); p += 8; break; default: assert(!"unknown pointer encoding"); } // then add relative offset switch (encoding & 0x70) { case DW_EH_PE_absptr: // do nothing break; case DW_EH_PE_pcrel: result += startAddr; break; case DW_EH_PE_textrel: assert(!"DW_EH_PE_textrel pointer encoding not supported"); break; case DW_EH_PE_datarel: assert(!"DW_EH_PE_datarel pointer encoding not supported"); break; case DW_EH_PE_funcrel: assert(!"DW_EH_PE_funcrel pointer encoding not supported"); break; case DW_EH_PE_aligned: assert(!"DW_EH_PE_aligned pointer encoding not supported"); break; default: assert(!"unknown pointer encoding"); break; } if (encoding & DW_EH_PE_indirect) result = unw_getP(result); *ret = result; return p; } #endif // UNWIND_PE_H