blob: 8655a92e2fd26373f853aa36d3002438c647941e [file] [log] [blame]
//===----------------------------- 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 <assert.h>
#include <stdint.h>
#include <string.h>
// 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