forked from OSchip/llvm-project
201 lines
4.9 KiB
C
201 lines
4.9 KiB
C
//===----------------------------- 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
|