llvm-capstone/llgo/include/unwind-pe.h
Chandler Carruth 57b08b0944 Update more file headers across all of the LLVM projects in the monorepo
to reflect the new license. These used slightly different spellings that
defeated my regular expressions.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351648
2019-01-19 10:56:40 +00:00

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