mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
third_party: Add libunwind (#1053)
Added libunwind from LLVM 17.0.6. The library includes functions required for C++ exception handling.
This commit is contained in:
parent
91de6f1f5d
commit
b09096691a
29 changed files with 17372 additions and 0 deletions
1
Makefile
1
Makefile
|
@ -328,6 +328,7 @@ include third_party/python/BUILD.mk
|
|||
include tool/build/BUILD.mk
|
||||
include tool/curl/BUILD.mk
|
||||
include third_party/qemu/BUILD.mk
|
||||
include third_party/libunwind/BUILD.mk
|
||||
include examples/BUILD.mk
|
||||
include examples/pyapp/BUILD.mk
|
||||
include examples/pylife/BUILD.mk
|
||||
|
|
1
third_party/BUILD.mk
vendored
1
third_party/BUILD.mk
vendored
|
@ -17,6 +17,7 @@ o/$(MODE)/third_party: \
|
|||
o/$(MODE)/third_party/hiredis \
|
||||
o/$(MODE)/third_party/less \
|
||||
o/$(MODE)/third_party/libcxx \
|
||||
o/$(MODE)/third_party/libunwind \
|
||||
o/$(MODE)/third_party/linenoise \
|
||||
o/$(MODE)/third_party/lua \
|
||||
o/$(MODE)/third_party/lz4cli \
|
||||
|
|
697
third_party/libunwind/AddressSpace.hpp
vendored
Normal file
697
third_party/libunwind/AddressSpace.hpp
vendored
Normal file
|
@ -0,0 +1,697 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Abstracts accessing local vs remote address spaces.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __ADDRESSSPACE_HPP__
|
||||
#define __ADDRESSSPACE_HPP__
|
||||
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdlib.h"
|
||||
#include "libc/isystem/string.h"
|
||||
|
||||
#include "third_party/libunwind/include/libunwind.h"
|
||||
#include "third_party/libunwind/config.h"
|
||||
#include "third_party/libunwind/dwarf2.h"
|
||||
#include "third_party/libunwind/EHHeaderParser.hpp"
|
||||
#include "third_party/libunwind/Registers.hpp"
|
||||
|
||||
#ifndef _LIBUNWIND_USE_DLADDR
|
||||
#if !(defined(_LIBUNWIND_IS_BAREMETAL) || defined(_WIN32) || defined(_AIX))
|
||||
#define _LIBUNWIND_USE_DLADDR 1
|
||||
#else
|
||||
#define _LIBUNWIND_USE_DLADDR 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if _LIBUNWIND_USE_DLADDR
|
||||
#include <dlfcn.h>
|
||||
#if defined(__ELF__) && defined(_LIBUNWIND_LINK_DL_LIB)
|
||||
#pragma comment(lib, "dl")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||
struct EHABIIndexEntry {
|
||||
uint32_t functionOffset;
|
||||
uint32_t data;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(_AIX)
|
||||
namespace libunwind {
|
||||
char *getFuncNameFromTBTable(uintptr_t pc, uint16_t &NameLen,
|
||||
unw_word_t *offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
struct dyld_unwind_sections
|
||||
{
|
||||
const struct mach_header* mh;
|
||||
const void* dwarf_section;
|
||||
uintptr_t dwarf_section_length;
|
||||
const void* compact_unwind_section;
|
||||
uintptr_t compact_unwind_section_length;
|
||||
};
|
||||
|
||||
// In 10.7.0 or later, libSystem.dylib implements this function.
|
||||
extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *);
|
||||
|
||||
namespace libunwind {
|
||||
bool findDynamicUnwindSections(void *, unw_dynamic_unwind_sections *);
|
||||
}
|
||||
|
||||
#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
|
||||
// When statically linked on bare-metal, the symbols for the EH table are looked
|
||||
// up without going through the dynamic loader.
|
||||
|
||||
// The following linker script may be used to produce the necessary sections and symbols.
|
||||
// Unless the --eh-frame-hdr linker option is provided, the section is not generated
|
||||
// and does not take space in the output file.
|
||||
//
|
||||
// .eh_frame :
|
||||
// {
|
||||
// __eh_frame_start = .;
|
||||
// KEEP(*(.eh_frame))
|
||||
// __eh_frame_end = .;
|
||||
// }
|
||||
//
|
||||
// .eh_frame_hdr :
|
||||
// {
|
||||
// KEEP(*(.eh_frame_hdr))
|
||||
// }
|
||||
//
|
||||
// __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
|
||||
// __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
|
||||
|
||||
extern char __eh_frame_start;
|
||||
extern char __eh_frame_end;
|
||||
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
|
||||
extern char __eh_frame_hdr_start;
|
||||
extern char __eh_frame_hdr_end;
|
||||
#endif
|
||||
|
||||
#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
|
||||
// When statically linked on bare-metal, the symbols for the EH table are looked
|
||||
// up without going through the dynamic loader.
|
||||
extern char __exidx_start;
|
||||
extern char __exidx_end;
|
||||
|
||||
#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
|
||||
#elif defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) || \
|
||||
defined(_LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX)
|
||||
|
||||
#include <link.h>
|
||||
|
||||
#endif
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
/// Used by findUnwindSections() to return info about needed sections.
|
||||
struct UnwindInfoSections {
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) || \
|
||||
defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) || \
|
||||
defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
|
||||
// No dso_base for SEH.
|
||||
uintptr_t dso_base;
|
||||
#endif
|
||||
#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
|
||||
size_t text_segment_length;
|
||||
#endif
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||
uintptr_t dwarf_section;
|
||||
size_t dwarf_section_length;
|
||||
#endif
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
|
||||
uintptr_t dwarf_index_section;
|
||||
size_t dwarf_index_section_length;
|
||||
#endif
|
||||
#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
|
||||
uintptr_t compact_unwind_section;
|
||||
size_t compact_unwind_section_length;
|
||||
#endif
|
||||
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||
uintptr_t arm_section;
|
||||
size_t arm_section_length;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/// LocalAddressSpace is used as a template parameter to UnwindCursor when
|
||||
/// unwinding a thread in the same process. The wrappers compile away,
|
||||
/// making local unwinds fast.
|
||||
class _LIBUNWIND_HIDDEN LocalAddressSpace {
|
||||
public:
|
||||
typedef uintptr_t pint_t;
|
||||
typedef intptr_t sint_t;
|
||||
uint8_t get8(pint_t addr) {
|
||||
uint8_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
uint16_t get16(pint_t addr) {
|
||||
uint16_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
uint32_t get32(pint_t addr) {
|
||||
uint32_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
uint64_t get64(pint_t addr) {
|
||||
uint64_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
double getDouble(pint_t addr) {
|
||||
double val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
v128 getVector(pint_t addr) {
|
||||
v128 val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
uintptr_t getP(pint_t addr);
|
||||
uint64_t getRegister(pint_t addr);
|
||||
static uint64_t getULEB128(pint_t &addr, pint_t end);
|
||||
static int64_t getSLEB128(pint_t &addr, pint_t end);
|
||||
|
||||
pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||
pint_t datarelBase = 0);
|
||||
bool findFunctionName(pint_t addr, char *buf, size_t bufLen,
|
||||
unw_word_t *offset);
|
||||
bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info);
|
||||
bool findOtherFDE(pint_t targetAddr, pint_t &fde);
|
||||
|
||||
static LocalAddressSpace sThisAddressSpace;
|
||||
};
|
||||
|
||||
inline uintptr_t LocalAddressSpace::getP(pint_t addr) {
|
||||
#if __SIZEOF_POINTER__ == 8
|
||||
return get64(addr);
|
||||
#else
|
||||
return get32(addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint64_t LocalAddressSpace::getRegister(pint_t addr) {
|
||||
#if __SIZEOF_POINTER__ == 8 || defined(__mips64)
|
||||
return get64(addr);
|
||||
#else
|
||||
return get32(addr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Read a ULEB128 into a 64-bit word.
|
||||
inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) {
|
||||
const uint8_t *p = (uint8_t *)addr;
|
||||
const uint8_t *pend = (uint8_t *)end;
|
||||
uint64_t result = 0;
|
||||
int bit = 0;
|
||||
do {
|
||||
uint64_t b;
|
||||
|
||||
if (p == pend)
|
||||
_LIBUNWIND_ABORT("truncated uleb128 expression");
|
||||
|
||||
b = *p & 0x7f;
|
||||
|
||||
if (bit >= 64 || b << bit >> bit != b) {
|
||||
_LIBUNWIND_ABORT("malformed uleb128 expression");
|
||||
} else {
|
||||
result |= b << bit;
|
||||
bit += 7;
|
||||
}
|
||||
} while (*p++ >= 0x80);
|
||||
addr = (pint_t) p;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Read a SLEB128 into a 64-bit word.
|
||||
inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) {
|
||||
const uint8_t *p = (uint8_t *)addr;
|
||||
const uint8_t *pend = (uint8_t *)end;
|
||||
uint64_t result = 0;
|
||||
int bit = 0;
|
||||
uint8_t byte;
|
||||
do {
|
||||
if (p == pend)
|
||||
_LIBUNWIND_ABORT("truncated sleb128 expression");
|
||||
byte = *p++;
|
||||
result |= (uint64_t)(byte & 0x7f) << bit;
|
||||
bit += 7;
|
||||
} while (byte & 0x80);
|
||||
// sign extend negative numbers
|
||||
if ((byte & 0x40) != 0 && bit < 64)
|
||||
result |= (-1ULL) << bit;
|
||||
addr = (pint_t) p;
|
||||
return (int64_t)result;
|
||||
}
|
||||
|
||||
inline LocalAddressSpace::pint_t
|
||||
LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding,
|
||||
pint_t datarelBase) {
|
||||
pint_t startAddr = addr;
|
||||
const uint8_t *p = (uint8_t *)addr;
|
||||
pint_t result;
|
||||
|
||||
// first get value
|
||||
switch (encoding & 0x0F) {
|
||||
case DW_EH_PE_ptr:
|
||||
result = getP(addr);
|
||||
p += sizeof(pint_t);
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_uleb128:
|
||||
result = (pint_t)getULEB128(addr, end);
|
||||
break;
|
||||
case DW_EH_PE_udata2:
|
||||
result = get16(addr);
|
||||
p += 2;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_udata4:
|
||||
result = get32(addr);
|
||||
p += 4;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_udata8:
|
||||
result = (pint_t)get64(addr);
|
||||
p += 8;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_sleb128:
|
||||
result = (pint_t)getSLEB128(addr, end);
|
||||
break;
|
||||
case DW_EH_PE_sdata2:
|
||||
// Sign extend from signed 16-bit value.
|
||||
result = (pint_t)(int16_t)get16(addr);
|
||||
p += 2;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_sdata4:
|
||||
// Sign extend from signed 32-bit value.
|
||||
result = (pint_t)(int32_t)get32(addr);
|
||||
p += 4;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
case DW_EH_PE_sdata8:
|
||||
result = (pint_t)get64(addr);
|
||||
p += 8;
|
||||
addr = (pint_t) p;
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_ABORT("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:
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported");
|
||||
break;
|
||||
case DW_EH_PE_datarel:
|
||||
// DW_EH_PE_datarel is only valid in a few places, so the parameter has a
|
||||
// default value of 0, and we abort in the event that someone calls this
|
||||
// function with a datarelBase of 0 and DW_EH_PE_datarel encoding.
|
||||
if (datarelBase == 0)
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0");
|
||||
result += datarelBase;
|
||||
break;
|
||||
case DW_EH_PE_funcrel:
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported");
|
||||
break;
|
||||
case DW_EH_PE_aligned:
|
||||
_LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported");
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_ABORT("unknown pointer encoding");
|
||||
break;
|
||||
}
|
||||
|
||||
if (encoding & DW_EH_PE_indirect)
|
||||
result = getP(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
|
||||
|
||||
// The ElfW() macro for pointer-size independent ELF header traversal is not
|
||||
// provided by <link.h> on some systems (e.g., FreeBSD). On these systems the
|
||||
// data structures are just called Elf_XXX. Define ElfW() locally.
|
||||
#if !defined(ElfW)
|
||||
#define ElfW(type) Elf_##type
|
||||
#endif
|
||||
#if !defined(Elf_Half)
|
||||
typedef ElfW(Half) Elf_Half;
|
||||
#endif
|
||||
#if !defined(Elf_Phdr)
|
||||
typedef ElfW(Phdr) Elf_Phdr;
|
||||
#endif
|
||||
#if !defined(Elf_Addr)
|
||||
typedef ElfW(Addr) Elf_Addr;
|
||||
#endif
|
||||
|
||||
struct _LIBUNWIND_HIDDEN dl_iterate_cb_data {
|
||||
LocalAddressSpace *addressSpace;
|
||||
UnwindInfoSections *sects;
|
||||
uintptr_t targetAddr;
|
||||
};
|
||||
|
||||
#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE)
|
||||
#include "third_party/libunwind/FrameHeaderCache.hpp"
|
||||
|
||||
// Typically there is one cache per process, but when libunwind is built as a
|
||||
// hermetic static library, then each shared object may have its own cache.
|
||||
static FrameHeaderCache TheFrameHeaderCache;
|
||||
#endif
|
||||
|
||||
static bool checkAddrInSegment(const Elf_Phdr *phdr, size_t image_base,
|
||||
dl_iterate_cb_data *cbdata) {
|
||||
if (phdr->p_type == PT_LOAD) {
|
||||
uintptr_t begin = image_base + phdr->p_vaddr;
|
||||
uintptr_t end = begin + phdr->p_memsz;
|
||||
if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) {
|
||||
cbdata->sects->dso_base = begin;
|
||||
cbdata->sects->text_segment_length = phdr->p_memsz;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool checkForUnwindInfoSegment(const Elf_Phdr *phdr, size_t image_base,
|
||||
dl_iterate_cb_data *cbdata) {
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
|
||||
if (phdr->p_type == PT_GNU_EH_FRAME) {
|
||||
EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
|
||||
uintptr_t eh_frame_hdr_start = image_base + phdr->p_vaddr;
|
||||
cbdata->sects->dwarf_index_section = eh_frame_hdr_start;
|
||||
cbdata->sects->dwarf_index_section_length = phdr->p_memsz;
|
||||
if (EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
|
||||
*cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz,
|
||||
hdrInfo)) {
|
||||
// .eh_frame_hdr records the start of .eh_frame, but not its size.
|
||||
// Rely on a zero terminator to find the end of the section.
|
||||
cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr;
|
||||
cbdata->sects->dwarf_section_length = SIZE_MAX;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#elif defined(_LIBUNWIND_ARM_EHABI)
|
||||
if (phdr->p_type == PT_ARM_EXIDX) {
|
||||
uintptr_t exidx_start = image_base + phdr->p_vaddr;
|
||||
cbdata->sects->arm_section = exidx_start;
|
||||
cbdata->sects->arm_section_length = phdr->p_memsz;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
#error Need one of _LIBUNWIND_SUPPORT_DWARF_INDEX or _LIBUNWIND_ARM_EHABI
|
||||
#endif
|
||||
}
|
||||
|
||||
static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo,
|
||||
size_t pinfo_size, void *data) {
|
||||
auto cbdata = static_cast<dl_iterate_cb_data *>(data);
|
||||
if (pinfo->dlpi_phnum == 0 || cbdata->targetAddr < pinfo->dlpi_addr)
|
||||
return 0;
|
||||
#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE)
|
||||
if (TheFrameHeaderCache.find(pinfo, pinfo_size, data))
|
||||
return 1;
|
||||
#else
|
||||
// Avoid warning about unused variable.
|
||||
(void)pinfo_size;
|
||||
#endif
|
||||
|
||||
Elf_Addr image_base = pinfo->dlpi_addr;
|
||||
|
||||
// Most shared objects seen in this callback function likely don't contain the
|
||||
// target address, so optimize for that. Scan for a matching PT_LOAD segment
|
||||
// first and bail when it isn't found.
|
||||
bool found_text = false;
|
||||
for (Elf_Half i = 0; i < pinfo->dlpi_phnum; ++i) {
|
||||
if (checkAddrInSegment(&pinfo->dlpi_phdr[i], image_base, cbdata)) {
|
||||
found_text = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_text)
|
||||
return 0;
|
||||
|
||||
// PT_GNU_EH_FRAME and PT_ARM_EXIDX are usually near the end. Iterate
|
||||
// backward.
|
||||
bool found_unwind = false;
|
||||
for (Elf_Half i = pinfo->dlpi_phnum; i > 0; i--) {
|
||||
const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i - 1];
|
||||
if (checkForUnwindInfoSegment(phdr, image_base, cbdata)) {
|
||||
found_unwind = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_unwind)
|
||||
return 0;
|
||||
|
||||
#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE)
|
||||
TheFrameHeaderCache.add(cbdata->sects);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
|
||||
|
||||
|
||||
inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
|
||||
UnwindInfoSections &info) {
|
||||
#ifdef __APPLE__
|
||||
dyld_unwind_sections dyldInfo;
|
||||
if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) {
|
||||
info.dso_base = (uintptr_t)dyldInfo.mh;
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||
info.dwarf_section = (uintptr_t)dyldInfo.dwarf_section;
|
||||
info.dwarf_section_length = (size_t)dyldInfo.dwarf_section_length;
|
||||
#endif
|
||||
info.compact_unwind_section = (uintptr_t)dyldInfo.compact_unwind_section;
|
||||
info.compact_unwind_section_length = (size_t)dyldInfo.compact_unwind_section_length;
|
||||
return true;
|
||||
}
|
||||
|
||||
unw_dynamic_unwind_sections dynamicUnwindSectionInfo;
|
||||
if (findDynamicUnwindSections((void *)targetAddr,
|
||||
&dynamicUnwindSectionInfo)) {
|
||||
info.dso_base = dynamicUnwindSectionInfo.dso_base;
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||
info.dwarf_section = (uintptr_t)dynamicUnwindSectionInfo.dwarf_section;
|
||||
info.dwarf_section_length = dynamicUnwindSectionInfo.dwarf_section_length;
|
||||
#endif
|
||||
info.compact_unwind_section =
|
||||
(uintptr_t)dynamicUnwindSectionInfo.compact_unwind_section;
|
||||
info.compact_unwind_section_length =
|
||||
dynamicUnwindSectionInfo.compact_unwind_section_length;
|
||||
return true;
|
||||
}
|
||||
|
||||
#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
info.dso_base = 0;
|
||||
// Bare metal is statically linked, so no need to ask the dynamic loader
|
||||
info.dwarf_section_length = (size_t)(&__eh_frame_end - &__eh_frame_start);
|
||||
info.dwarf_section = (uintptr_t)(&__eh_frame_start);
|
||||
_LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p",
|
||||
(void *)info.dwarf_section, (void *)info.dwarf_section_length);
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
|
||||
info.dwarf_index_section = (uintptr_t)(&__eh_frame_hdr_start);
|
||||
info.dwarf_index_section_length = (size_t)(&__eh_frame_hdr_end - &__eh_frame_hdr_start);
|
||||
_LIBUNWIND_TRACE_UNWINDING("findUnwindSections: index section %p length %p",
|
||||
(void *)info.dwarf_index_section, (void *)info.dwarf_index_section_length);
|
||||
#endif
|
||||
if (info.dwarf_section_length)
|
||||
return true;
|
||||
#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
// Bare metal is statically linked, so no need to ask the dynamic loader
|
||||
info.arm_section = (uintptr_t)(&__exidx_start);
|
||||
info.arm_section_length = (size_t)(&__exidx_end - &__exidx_start);
|
||||
_LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p",
|
||||
(void *)info.arm_section, (void *)info.arm_section_length);
|
||||
if (info.arm_section && info.arm_section_length)
|
||||
return true;
|
||||
#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32)
|
||||
HMODULE mods[1024];
|
||||
HANDLE process = GetCurrentProcess();
|
||||
DWORD needed;
|
||||
|
||||
if (!EnumProcessModules(process, mods, sizeof(mods), &needed)) {
|
||||
DWORD err = GetLastError();
|
||||
_LIBUNWIND_TRACE_UNWINDING("findUnwindSections: EnumProcessModules failed, "
|
||||
"returned error %d", (int)err);
|
||||
(void)err;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < (needed / sizeof(HMODULE)); i++) {
|
||||
PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)mods[i];
|
||||
PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS)((BYTE *)pidh + pidh->e_lfanew);
|
||||
PIMAGE_FILE_HEADER pifh = (PIMAGE_FILE_HEADER)&pinh->FileHeader;
|
||||
PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinh);
|
||||
bool found_obj = false;
|
||||
bool found_hdr = false;
|
||||
|
||||
info.dso_base = (uintptr_t)mods[i];
|
||||
for (unsigned j = 0; j < pifh->NumberOfSections; j++, pish++) {
|
||||
uintptr_t begin = pish->VirtualAddress + (uintptr_t)mods[i];
|
||||
uintptr_t end = begin + pish->Misc.VirtualSize;
|
||||
if (!strncmp((const char *)pish->Name, ".text",
|
||||
IMAGE_SIZEOF_SHORT_NAME)) {
|
||||
if (targetAddr >= begin && targetAddr < end)
|
||||
found_obj = true;
|
||||
} else if (!strncmp((const char *)pish->Name, ".eh_frame",
|
||||
IMAGE_SIZEOF_SHORT_NAME)) {
|
||||
info.dwarf_section = begin;
|
||||
info.dwarf_section_length = pish->Misc.VirtualSize;
|
||||
found_hdr = true;
|
||||
}
|
||||
if (found_obj && found_hdr)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32)
|
||||
// Don't even bother, since Windows has functions that do all this stuff
|
||||
// for us.
|
||||
(void)targetAddr;
|
||||
(void)info;
|
||||
return true;
|
||||
#elif defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
|
||||
// The traceback table is used for unwinding.
|
||||
(void)targetAddr;
|
||||
(void)info;
|
||||
return true;
|
||||
#elif defined(_LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX)
|
||||
int length = 0;
|
||||
info.arm_section =
|
||||
(uintptr_t)dl_unwind_find_exidx((_Unwind_Ptr)targetAddr, &length);
|
||||
info.arm_section_length = (size_t)length * sizeof(EHABIIndexEntry);
|
||||
if (info.arm_section && info.arm_section_length)
|
||||
return true;
|
||||
#elif defined(_LIBUNWIND_USE_DL_ITERATE_PHDR)
|
||||
// Use DLFO_STRUCT_HAS_EH_DBASE to determine the existence of
|
||||
// `_dl_find_object`. Use _LIBUNWIND_SUPPORT_DWARF_INDEX, because libunwind
|
||||
// support for _dl_find_object on other unwind formats is not implemented,
|
||||
// yet.
|
||||
#if defined(DLFO_STRUCT_HAS_EH_DBASE) & defined(_LIBUNWIND_SUPPORT_DWARF_INDEX)
|
||||
// We expect `_dl_find_object` to return PT_GNU_EH_FRAME.
|
||||
#if DLFO_EH_SEGMENT_TYPE != PT_GNU_EH_FRAME
|
||||
#error _dl_find_object retrieves an unexpected section type
|
||||
#endif
|
||||
// We look-up `dl_find_object` dynamically at runtime to ensure backwards
|
||||
// compatibility with earlier version of glibc not yet providing it. On older
|
||||
// systems, we gracefully fallback to `dl_iterate_phdr`. Cache the pointer
|
||||
// so we only look it up once. Do manual lock to avoid _cxa_guard_acquire.
|
||||
static decltype(_dl_find_object) *dlFindObject;
|
||||
static bool dlFindObjectChecked = false;
|
||||
if (!dlFindObjectChecked) {
|
||||
dlFindObject = reinterpret_cast<decltype(_dl_find_object) *>(
|
||||
dlsym(RTLD_DEFAULT, "_dl_find_object"));
|
||||
dlFindObjectChecked = true;
|
||||
}
|
||||
// Try to find the unwind info using `dl_find_object`
|
||||
dl_find_object findResult;
|
||||
if (dlFindObject && dlFindObject((void *)targetAddr, &findResult) == 0) {
|
||||
if (findResult.dlfo_eh_frame == nullptr) {
|
||||
// Found an entry for `targetAddr`, but there is no unwind info.
|
||||
return false;
|
||||
}
|
||||
info.dso_base = reinterpret_cast<uintptr_t>(findResult.dlfo_map_start);
|
||||
info.text_segment_length = static_cast<size_t>(
|
||||
(char *)findResult.dlfo_map_end - (char *)findResult.dlfo_map_start);
|
||||
|
||||
// Record the start of PT_GNU_EH_FRAME.
|
||||
info.dwarf_index_section =
|
||||
reinterpret_cast<uintptr_t>(findResult.dlfo_eh_frame);
|
||||
// `_dl_find_object` does not give us the size of PT_GNU_EH_FRAME.
|
||||
// Setting length to `SIZE_MAX` effectively disables all range checks.
|
||||
info.dwarf_index_section_length = SIZE_MAX;
|
||||
EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
|
||||
if (!EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
|
||||
*this, info.dwarf_index_section, info.dwarf_index_section_length,
|
||||
hdrInfo)) {
|
||||
return false;
|
||||
}
|
||||
// Record the start of the FDE and use SIZE_MAX to indicate that we do
|
||||
// not know the end address.
|
||||
info.dwarf_section = hdrInfo.eh_frame_ptr;
|
||||
info.dwarf_section_length = SIZE_MAX;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
dl_iterate_cb_data cb_data = {this, &info, targetAddr};
|
||||
int found = dl_iterate_phdr(findUnwindSectionsByPhdr, &cb_data);
|
||||
return static_cast<bool>(found);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) {
|
||||
// TO DO: if OS has way to dynamically register FDEs, check that.
|
||||
(void)targetAddr;
|
||||
(void)fde;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf,
|
||||
size_t bufLen,
|
||||
unw_word_t *offset) {
|
||||
#if _LIBUNWIND_USE_DLADDR
|
||||
Dl_info dyldInfo;
|
||||
if (dladdr((void *)addr, &dyldInfo)) {
|
||||
if (dyldInfo.dli_sname != NULL) {
|
||||
snprintf(buf, bufLen, "%s", dyldInfo.dli_sname);
|
||||
*offset = (addr - (pint_t) dyldInfo.dli_saddr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#elif defined(_AIX)
|
||||
uint16_t nameLen;
|
||||
char *funcName = getFuncNameFromTBTable(addr, nameLen, offset);
|
||||
if (funcName != NULL) {
|
||||
snprintf(buf, bufLen, "%.*s", nameLen, funcName);
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
(void)addr;
|
||||
(void)buf;
|
||||
(void)bufLen;
|
||||
(void)offset;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
#endif // __ADDRESSSPACE_HPP__
|
88
third_party/libunwind/BUILD.mk
vendored
Normal file
88
third_party/libunwind/BUILD.mk
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
|
||||
|
||||
PKGS += THIRD_PARTY_LIBUNWIND
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_ARTIFACTS += THIRD_PARTY_LIBUNWIND_A
|
||||
THIRD_PARTY_LIBUNWIND = $(THIRD_PARTY_LIBUNWIND_A_DEPS) $(THIRD_PARTY_LIBUNWIND_A)
|
||||
THIRD_PARTY_LIBUNWIND_A = o/$(MODE)/third_party/libunwind/libunwind.a
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_A_INCS = \
|
||||
third_party/libunwind/include/unwind_arm_ehabi.h \
|
||||
third_party/libunwind/include/unwind_itanium.h \
|
||||
third_party/libunwind/AddressSpace.hpp \
|
||||
third_party/libunwind/FrameHeaderCache.hpp \
|
||||
third_party/libunwind/RWMutex.hpp \
|
||||
third_party/libunwind/UnwindCursor.hpp
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_A_HDRS = \
|
||||
third_party/libunwind/include/mach-o/compact_unwind_encoding.h \
|
||||
third_party/libunwind/include/__libunwind_config.h \
|
||||
third_party/libunwind/include/libunwind.h \
|
||||
third_party/libunwind/include/unwind.h \
|
||||
third_party/libunwind/config.h \
|
||||
third_party/libunwind/cet_unwind.h \
|
||||
third_party/libunwind/dwarf2.h \
|
||||
third_party/libunwind/libunwind_ext.h \
|
||||
third_party/libunwind/CompactUnwinder.hpp \
|
||||
third_party/libunwind/DwarfInstructions.hpp \
|
||||
third_party/libunwind/DwarfParser.hpp \
|
||||
third_party/libunwind/EHHeaderParser.hpp \
|
||||
third_party/libunwind/Registers.hpp \
|
||||
third_party/libunwind/Unwind-EHABI.h
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_A_SRCS_CC = \
|
||||
third_party/libunwind/libunwind.cc
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_A_SRCS_C = \
|
||||
third_party/libunwind/Unwind-sjlj.c \
|
||||
third_party/libunwind/UnwindLevel1-gcc-ext.c \
|
||||
third_party/libunwind/UnwindLevel1.c
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_A_SRCS = \
|
||||
$(THIRD_PARTY_LIBUNWIND_A_SRCS_C) \
|
||||
$(THIRD_PARTY_LIBUNWIND_A_SRCS_CC)
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_A_OBJS = \
|
||||
$(THIRD_PARTY_LIBUNWIND_A_SRCS_C:%.c=o/$(MODE)/%.o) \
|
||||
$(THIRD_PARTY_LIBUNWIND_A_SRCS_CC:%.cc=o/$(MODE)/%.o)
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_A_CHECKS = \
|
||||
$(THIRD_PARTY_LIBUNWIND_A).pkg \
|
||||
$(THIRD_PARTY_LIBUNWIND_A_HDRS:%=o/$(MODE)/%.okk)
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_INTRIN \
|
||||
LIBC_STDIO \
|
||||
THIRD_PARTY_LIBCXX
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(THIRD_PARTY_LIBUNWIND_A_DIRECTDEPS),$($(x))))
|
||||
|
||||
$(THIRD_PARTY_LIBUNWIND_A): \
|
||||
third_party/libunwind/ \
|
||||
$(THIRD_PARTY_LIBUNWIND_A).pkg \
|
||||
$(THIRD_PARTY_LIBUNWIND_A_OBJS)
|
||||
|
||||
$(THIRD_PARTY_LIBUNWIND_A).pkg: \
|
||||
$(THIRD_PARTY_LIBUNWIND_A_OBJS) \
|
||||
$(foreach x,$(THIRD_PARTY_LIBUNWIND_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
$(THIRD_PARTY_LIBUNWIND_A_OBJS): private \
|
||||
CXXFLAGS += \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
-D_LIBUNWIND_USE_DLADDR=0
|
||||
|
||||
THIRD_PARTY_LIBUNWIND_LIBS = $(foreach x,$(THIRD_PARTY_LIBUNWIND_ARTIFACTS),$($(x)))
|
||||
THIRD_PARTY_LIBUNWIND_SRCS = $(foreach x,$(THIRD_PARTY_LIBUNWIND_ARTIFACTS),$($(x)_SRCS))
|
||||
THIRD_PARTY_LIBUNWIND_HDRS = $(foreach x,$(THIRD_PARTY_LIBUNWIND_ARTIFACTS),$($(x)_HDRS))
|
||||
THIRD_PARTY_LIBUNWIND_INCS = $(foreach x,$(THIRD_PARTY_LIBUNWIND_ARTIFACTS),$($(x)_INCS))
|
||||
THIRD_PARTY_LIBUNWIND_CHECKS = $(foreach x,$(THIRD_PARTY_LIBUNWIND_ARTIFACTS),$($(x)_CHECKS))
|
||||
THIRD_PARTY_LIBUNWIND_OBJS = $(foreach x,$(THIRD_PARTY_LIBUNWIND_ARTIFACTS),$($(x)_OBJS))
|
||||
|
||||
.PHONY: o/$(MODE)/third_party/libunwind
|
||||
o/$(MODE)/third_party/libunwind: \
|
||||
$(THIRD_PARTY_LIBUNWIND_CHECKS) \
|
||||
$(THIRD_PARTY_LIBUNWIND_A)
|
698
third_party/libunwind/CompactUnwinder.hpp
vendored
Normal file
698
third_party/libunwind/CompactUnwinder.hpp
vendored
Normal file
|
@ -0,0 +1,698 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Does runtime stack unwinding using compact unwind encodings.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __COMPACT_UNWINDER_HPP__
|
||||
#define __COMPACT_UNWINDER_HPP__
|
||||
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "libc/isystem/stdlib.h"
|
||||
|
||||
#include "third_party/libunwind/include/libunwind.h"
|
||||
#include "third_party/libunwind/include/mach-o/compact_unwind_encoding.h"
|
||||
|
||||
#include "third_party/libunwind/Registers.hpp"
|
||||
#include "third_party/libunwind/libunwind_ext.h"
|
||||
|
||||
#define EXTRACT_BITS(value, mask) \
|
||||
((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1))
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_I386)
|
||||
/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka
|
||||
/// unwind) by modifying a Registers_x86 register set
|
||||
template <typename A>
|
||||
class CompactUnwinder_x86 {
|
||||
public:
|
||||
|
||||
static int stepWithCompactEncoding(compact_unwind_encoding_t info,
|
||||
uint32_t functionStart, A &addressSpace,
|
||||
Registers_x86 ®isters);
|
||||
|
||||
private:
|
||||
typename A::pint_t pint_t;
|
||||
|
||||
static void frameUnwind(A &addressSpace, Registers_x86 ®isters);
|
||||
static void framelessUnwind(A &addressSpace,
|
||||
typename A::pint_t returnAddressLocation,
|
||||
Registers_x86 ®isters);
|
||||
static int
|
||||
stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,
|
||||
uint32_t functionStart, A &addressSpace,
|
||||
Registers_x86 ®isters);
|
||||
static int stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
||||
A &addressSpace, Registers_x86 ®isters, bool indirectStackSize);
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86<A>::stepWithCompactEncoding(
|
||||
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
||||
A &addressSpace, Registers_x86 ®isters) {
|
||||
switch (compactEncoding & UNWIND_X86_MODE_MASK) {
|
||||
case UNWIND_X86_MODE_EBP_FRAME:
|
||||
return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart,
|
||||
addressSpace, registers);
|
||||
case UNWIND_X86_MODE_STACK_IMMD:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers, false);
|
||||
case UNWIND_X86_MODE_STACK_IND:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers, true);
|
||||
}
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86<A>::stepWithCompactEncodingEBPFrame(
|
||||
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
|
||||
A &addressSpace, Registers_x86 ®isters) {
|
||||
uint32_t savedRegistersOffset =
|
||||
EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET);
|
||||
uint32_t savedRegistersLocations =
|
||||
EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS);
|
||||
|
||||
uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
switch (savedRegistersLocations & 0x7) {
|
||||
case UNWIND_X86_REG_NONE:
|
||||
// no register saved in this slot
|
||||
break;
|
||||
case UNWIND_X86_REG_EBX:
|
||||
registers.setEBX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_ECX:
|
||||
registers.setECX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EDX:
|
||||
registers.setEDX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EDI:
|
||||
registers.setEDI(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_ESI:
|
||||
registers.setESI(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
default:
|
||||
(void)functionStart;
|
||||
_LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for "
|
||||
"function starting at 0x%X",
|
||||
compactEncoding, functionStart);
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
savedRegisters += 4;
|
||||
savedRegistersLocations = (savedRegistersLocations >> 3);
|
||||
}
|
||||
frameUnwind(addressSpace, registers);
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86<A>::stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t encoding, uint32_t functionStart,
|
||||
A &addressSpace, Registers_x86 ®isters, bool indirectStackSize) {
|
||||
uint32_t stackSizeEncoded =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
|
||||
uint32_t stackAdjust =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST);
|
||||
uint32_t regCount =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
|
||||
uint32_t permutation =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
|
||||
uint32_t stackSize = stackSizeEncoded * 4;
|
||||
if (indirectStackSize) {
|
||||
// stack size is encoded in subl $xxx,%esp instruction
|
||||
uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
|
||||
stackSize = subl + 4 * stackAdjust;
|
||||
}
|
||||
// decompress permutation
|
||||
uint32_t permunreg[6];
|
||||
switch (regCount) {
|
||||
case 6:
|
||||
permunreg[0] = permutation / 120;
|
||||
permutation -= (permunreg[0] * 120);
|
||||
permunreg[1] = permutation / 24;
|
||||
permutation -= (permunreg[1] * 24);
|
||||
permunreg[2] = permutation / 6;
|
||||
permutation -= (permunreg[2] * 6);
|
||||
permunreg[3] = permutation / 2;
|
||||
permutation -= (permunreg[3] * 2);
|
||||
permunreg[4] = permutation;
|
||||
permunreg[5] = 0;
|
||||
break;
|
||||
case 5:
|
||||
permunreg[0] = permutation / 120;
|
||||
permutation -= (permunreg[0] * 120);
|
||||
permunreg[1] = permutation / 24;
|
||||
permutation -= (permunreg[1] * 24);
|
||||
permunreg[2] = permutation / 6;
|
||||
permutation -= (permunreg[2] * 6);
|
||||
permunreg[3] = permutation / 2;
|
||||
permutation -= (permunreg[3] * 2);
|
||||
permunreg[4] = permutation;
|
||||
break;
|
||||
case 4:
|
||||
permunreg[0] = permutation / 60;
|
||||
permutation -= (permunreg[0] * 60);
|
||||
permunreg[1] = permutation / 12;
|
||||
permutation -= (permunreg[1] * 12);
|
||||
permunreg[2] = permutation / 3;
|
||||
permutation -= (permunreg[2] * 3);
|
||||
permunreg[3] = permutation;
|
||||
break;
|
||||
case 3:
|
||||
permunreg[0] = permutation / 20;
|
||||
permutation -= (permunreg[0] * 20);
|
||||
permunreg[1] = permutation / 4;
|
||||
permutation -= (permunreg[1] * 4);
|
||||
permunreg[2] = permutation;
|
||||
break;
|
||||
case 2:
|
||||
permunreg[0] = permutation / 5;
|
||||
permutation -= (permunreg[0] * 5);
|
||||
permunreg[1] = permutation;
|
||||
break;
|
||||
case 1:
|
||||
permunreg[0] = permutation;
|
||||
break;
|
||||
}
|
||||
// re-number registers back to standard numbers
|
||||
int registersSaved[6];
|
||||
bool used[7] = { false, false, false, false, false, false, false };
|
||||
for (uint32_t i = 0; i < regCount; ++i) {
|
||||
uint32_t renum = 0;
|
||||
for (int u = 1; u < 7; ++u) {
|
||||
if (!used[u]) {
|
||||
if (renum == permunreg[i]) {
|
||||
registersSaved[i] = u;
|
||||
used[u] = true;
|
||||
break;
|
||||
}
|
||||
++renum;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount;
|
||||
for (uint32_t i = 0; i < regCount; ++i) {
|
||||
switch (registersSaved[i]) {
|
||||
case UNWIND_X86_REG_EBX:
|
||||
registers.setEBX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_ECX:
|
||||
registers.setECX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EDX:
|
||||
registers.setEDX(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EDI:
|
||||
registers.setEDI(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_ESI:
|
||||
registers.setESI(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_REG_EBP:
|
||||
registers.setEBP(addressSpace.get32(savedRegisters));
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
|
||||
"function starting at 0x%X",
|
||||
encoding, functionStart);
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
savedRegisters += 4;
|
||||
}
|
||||
framelessUnwind(addressSpace, savedRegisters, registers);
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
template <typename A>
|
||||
void CompactUnwinder_x86<A>::frameUnwind(A &addressSpace,
|
||||
Registers_x86 ®isters) {
|
||||
typename A::pint_t bp = registers.getEBP();
|
||||
// ebp points to old ebp
|
||||
registers.setEBP(addressSpace.get32(bp));
|
||||
// old esp is ebp less saved ebp and return address
|
||||
registers.setSP((uint32_t)bp + 8);
|
||||
// pop return address into eip
|
||||
registers.setIP(addressSpace.get32(bp + 4));
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
void CompactUnwinder_x86<A>::framelessUnwind(
|
||||
A &addressSpace, typename A::pint_t returnAddressLocation,
|
||||
Registers_x86 ®isters) {
|
||||
// return address is on stack after last saved register
|
||||
registers.setIP(addressSpace.get32(returnAddressLocation));
|
||||
// old esp is before return address
|
||||
registers.setSP((uint32_t)returnAddressLocation + 4);
|
||||
}
|
||||
#endif // _LIBUNWIND_TARGET_I386
|
||||
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_X86_64)
|
||||
/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka
|
||||
/// unwind) by modifying a Registers_x86_64 register set
|
||||
template <typename A>
|
||||
class CompactUnwinder_x86_64 {
|
||||
public:
|
||||
|
||||
static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
|
||||
uint64_t functionStart, A &addressSpace,
|
||||
Registers_x86_64 ®isters);
|
||||
|
||||
private:
|
||||
typename A::pint_t pint_t;
|
||||
|
||||
static void frameUnwind(A &addressSpace, Registers_x86_64 ®isters);
|
||||
static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
|
||||
Registers_x86_64 ®isters);
|
||||
static int
|
||||
stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
|
||||
uint64_t functionStart, A &addressSpace,
|
||||
Registers_x86_64 ®isters);
|
||||
static int stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_x86_64 ®isters, bool indirectStackSize);
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_x86_64 ®isters) {
|
||||
switch (compactEncoding & UNWIND_X86_64_MODE_MASK) {
|
||||
case UNWIND_X86_64_MODE_RBP_FRAME:
|
||||
return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart,
|
||||
addressSpace, registers);
|
||||
case UNWIND_X86_64_MODE_STACK_IMMD:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers, false);
|
||||
case UNWIND_X86_64_MODE_STACK_IND:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers, true);
|
||||
}
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86_64<A>::stepWithCompactEncodingRBPFrame(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_x86_64 ®isters) {
|
||||
uint32_t savedRegistersOffset =
|
||||
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET);
|
||||
uint32_t savedRegistersLocations =
|
||||
EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS);
|
||||
|
||||
uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
switch (savedRegistersLocations & 0x7) {
|
||||
case UNWIND_X86_64_REG_NONE:
|
||||
// no register saved in this slot
|
||||
break;
|
||||
case UNWIND_X86_64_REG_RBX:
|
||||
registers.setRBX(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R12:
|
||||
registers.setR12(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R13:
|
||||
registers.setR13(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R14:
|
||||
registers.setR14(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R15:
|
||||
registers.setR15(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
default:
|
||||
(void)functionStart;
|
||||
_LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for "
|
||||
"function starting at 0x%llX",
|
||||
compactEncoding, functionStart);
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
savedRegisters += 8;
|
||||
savedRegistersLocations = (savedRegistersLocations >> 3);
|
||||
}
|
||||
frameUnwind(addressSpace, registers);
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_x86_64<A>::stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace,
|
||||
Registers_x86_64 ®isters, bool indirectStackSize) {
|
||||
uint32_t stackSizeEncoded =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE);
|
||||
uint32_t stackAdjust =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST);
|
||||
uint32_t regCount =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT);
|
||||
uint32_t permutation =
|
||||
EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION);
|
||||
uint32_t stackSize = stackSizeEncoded * 8;
|
||||
if (indirectStackSize) {
|
||||
// stack size is encoded in subl $xxx,%esp instruction
|
||||
uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded);
|
||||
stackSize = subl + 8 * stackAdjust;
|
||||
}
|
||||
// decompress permutation
|
||||
uint32_t permunreg[6];
|
||||
switch (regCount) {
|
||||
case 6:
|
||||
permunreg[0] = permutation / 120;
|
||||
permutation -= (permunreg[0] * 120);
|
||||
permunreg[1] = permutation / 24;
|
||||
permutation -= (permunreg[1] * 24);
|
||||
permunreg[2] = permutation / 6;
|
||||
permutation -= (permunreg[2] * 6);
|
||||
permunreg[3] = permutation / 2;
|
||||
permutation -= (permunreg[3] * 2);
|
||||
permunreg[4] = permutation;
|
||||
permunreg[5] = 0;
|
||||
break;
|
||||
case 5:
|
||||
permunreg[0] = permutation / 120;
|
||||
permutation -= (permunreg[0] * 120);
|
||||
permunreg[1] = permutation / 24;
|
||||
permutation -= (permunreg[1] * 24);
|
||||
permunreg[2] = permutation / 6;
|
||||
permutation -= (permunreg[2] * 6);
|
||||
permunreg[3] = permutation / 2;
|
||||
permutation -= (permunreg[3] * 2);
|
||||
permunreg[4] = permutation;
|
||||
break;
|
||||
case 4:
|
||||
permunreg[0] = permutation / 60;
|
||||
permutation -= (permunreg[0] * 60);
|
||||
permunreg[1] = permutation / 12;
|
||||
permutation -= (permunreg[1] * 12);
|
||||
permunreg[2] = permutation / 3;
|
||||
permutation -= (permunreg[2] * 3);
|
||||
permunreg[3] = permutation;
|
||||
break;
|
||||
case 3:
|
||||
permunreg[0] = permutation / 20;
|
||||
permutation -= (permunreg[0] * 20);
|
||||
permunreg[1] = permutation / 4;
|
||||
permutation -= (permunreg[1] * 4);
|
||||
permunreg[2] = permutation;
|
||||
break;
|
||||
case 2:
|
||||
permunreg[0] = permutation / 5;
|
||||
permutation -= (permunreg[0] * 5);
|
||||
permunreg[1] = permutation;
|
||||
break;
|
||||
case 1:
|
||||
permunreg[0] = permutation;
|
||||
break;
|
||||
}
|
||||
// re-number registers back to standard numbers
|
||||
int registersSaved[6];
|
||||
bool used[7] = { false, false, false, false, false, false, false };
|
||||
for (uint32_t i = 0; i < regCount; ++i) {
|
||||
uint32_t renum = 0;
|
||||
for (int u = 1; u < 7; ++u) {
|
||||
if (!used[u]) {
|
||||
if (renum == permunreg[i]) {
|
||||
registersSaved[i] = u;
|
||||
used[u] = true;
|
||||
break;
|
||||
}
|
||||
++renum;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount;
|
||||
for (uint32_t i = 0; i < regCount; ++i) {
|
||||
switch (registersSaved[i]) {
|
||||
case UNWIND_X86_64_REG_RBX:
|
||||
registers.setRBX(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R12:
|
||||
registers.setR12(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R13:
|
||||
registers.setR13(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R14:
|
||||
registers.setR14(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_R15:
|
||||
registers.setR15(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
case UNWIND_X86_64_REG_RBP:
|
||||
registers.setRBP(addressSpace.get64(savedRegisters));
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for "
|
||||
"function starting at 0x%llX",
|
||||
encoding, functionStart);
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
savedRegisters += 8;
|
||||
}
|
||||
framelessUnwind(addressSpace, savedRegisters, registers);
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
template <typename A>
|
||||
void CompactUnwinder_x86_64<A>::frameUnwind(A &addressSpace,
|
||||
Registers_x86_64 ®isters) {
|
||||
uint64_t rbp = registers.getRBP();
|
||||
// ebp points to old ebp
|
||||
registers.setRBP(addressSpace.get64(rbp));
|
||||
// old esp is ebp less saved ebp and return address
|
||||
registers.setSP(rbp + 16);
|
||||
// pop return address into eip
|
||||
registers.setIP(addressSpace.get64(rbp + 8));
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
void CompactUnwinder_x86_64<A>::framelessUnwind(A &addressSpace,
|
||||
uint64_t returnAddressLocation,
|
||||
Registers_x86_64 ®isters) {
|
||||
// return address is on stack after last saved register
|
||||
registers.setIP(addressSpace.get64(returnAddressLocation));
|
||||
// old esp is before return address
|
||||
registers.setSP(returnAddressLocation + 8);
|
||||
}
|
||||
#endif // _LIBUNWIND_TARGET_X86_64
|
||||
|
||||
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
/// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka
|
||||
/// unwind) by modifying a Registers_arm64 register set
|
||||
template <typename A>
|
||||
class CompactUnwinder_arm64 {
|
||||
public:
|
||||
|
||||
static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding,
|
||||
uint64_t functionStart, A &addressSpace,
|
||||
Registers_arm64 ®isters);
|
||||
|
||||
private:
|
||||
typename A::pint_t pint_t;
|
||||
|
||||
static int
|
||||
stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
|
||||
uint64_t functionStart, A &addressSpace,
|
||||
Registers_arm64 ®isters);
|
||||
static int stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_arm64 ®isters);
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
|
||||
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
|
||||
A &addressSpace, Registers_arm64 ®isters) {
|
||||
switch (compactEncoding & UNWIND_ARM64_MODE_MASK) {
|
||||
case UNWIND_ARM64_MODE_FRAME:
|
||||
return stepWithCompactEncodingFrame(compactEncoding, functionStart,
|
||||
addressSpace, registers);
|
||||
case UNWIND_ARM64_MODE_FRAMELESS:
|
||||
return stepWithCompactEncodingFrameless(compactEncoding, functionStart,
|
||||
addressSpace, registers);
|
||||
}
|
||||
_LIBUNWIND_ABORT("invalid compact unwind encoding");
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrameless(
|
||||
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
|
||||
Registers_arm64 ®isters) {
|
||||
uint32_t stackSize =
|
||||
16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK);
|
||||
|
||||
uint64_t savedRegisterLoc = registers.getSP() + stackSize;
|
||||
|
||||
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
|
||||
if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
|
||||
registers.setFloatRegister(UNW_AARCH64_V8,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_AARCH64_V9,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
|
||||
registers.setFloatRegister(UNW_AARCH64_V10,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_AARCH64_V11,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
|
||||
registers.setFloatRegister(UNW_AARCH64_V12,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_AARCH64_V13,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
|
||||
registers.setFloatRegister(UNW_AARCH64_V14,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_AARCH64_V15,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
|
||||
// subtract stack size off of sp
|
||||
registers.setSP(savedRegisterLoc);
|
||||
|
||||
// set pc to be value in lr
|
||||
registers.setIP(registers.getRegister(UNW_AARCH64_LR));
|
||||
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
int CompactUnwinder_arm64<A>::stepWithCompactEncodingFrame(
|
||||
compact_unwind_encoding_t encoding, uint64_t, A &addressSpace,
|
||||
Registers_arm64 ®isters) {
|
||||
uint64_t savedRegisterLoc = registers.getFP() - 8;
|
||||
|
||||
if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) {
|
||||
registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
|
||||
if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) {
|
||||
registers.setFloatRegister(UNW_AARCH64_V8,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_AARCH64_V9,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) {
|
||||
registers.setFloatRegister(UNW_AARCH64_V10,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_AARCH64_V11,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) {
|
||||
registers.setFloatRegister(UNW_AARCH64_V12,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_AARCH64_V13,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) {
|
||||
registers.setFloatRegister(UNW_AARCH64_V14,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
registers.setFloatRegister(UNW_AARCH64_V15,
|
||||
addressSpace.getDouble(savedRegisterLoc));
|
||||
savedRegisterLoc -= 8;
|
||||
}
|
||||
|
||||
uint64_t fp = registers.getFP();
|
||||
// fp points to old fp
|
||||
registers.setFP(addressSpace.get64(fp));
|
||||
// old sp is fp less saved fp and lr
|
||||
registers.setSP(fp + 16);
|
||||
// pop return address into pc
|
||||
registers.setIP(addressSpace.get64(fp + 8));
|
||||
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
#endif // _LIBUNWIND_TARGET_AARCH64
|
||||
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
#endif // __COMPACT_UNWINDER_HPP__
|
924
third_party/libunwind/DwarfInstructions.hpp
vendored
Normal file
924
third_party/libunwind/DwarfInstructions.hpp
vendored
Normal file
|
@ -0,0 +1,924 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Processor specific interpretation of DWARF unwind info.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __DWARF_INSTRUCTIONS_HPP__
|
||||
#define __DWARF_INSTRUCTIONS_HPP__
|
||||
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/isystem/stdlib.h"
|
||||
|
||||
#include "third_party/libunwind/DwarfParser.hpp"
|
||||
#include "third_party/libunwind/Registers.hpp"
|
||||
#include "third_party/libunwind/config.h"
|
||||
#include "third_party/libunwind/dwarf2.h"
|
||||
#include "third_party/libunwind/libunwind_ext.h"
|
||||
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
|
||||
/// DwarfInstructions maps abstract DWARF unwind instructions to a particular
|
||||
/// architecture
|
||||
template <typename A, typename R>
|
||||
class DwarfInstructions {
|
||||
public:
|
||||
typedef typename A::pint_t pint_t;
|
||||
typedef typename A::sint_t sint_t;
|
||||
|
||||
static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart,
|
||||
R ®isters, bool &isSignalFrame, bool stage2);
|
||||
|
||||
private:
|
||||
|
||||
enum {
|
||||
DW_X86_64_RET_ADDR = 16
|
||||
};
|
||||
|
||||
enum {
|
||||
DW_X86_RET_ADDR = 8
|
||||
};
|
||||
|
||||
typedef typename CFI_Parser<A>::RegisterLocation RegisterLocation;
|
||||
typedef typename CFI_Parser<A>::PrologInfo PrologInfo;
|
||||
typedef typename CFI_Parser<A>::FDE_Info FDE_Info;
|
||||
typedef typename CFI_Parser<A>::CIE_Info CIE_Info;
|
||||
|
||||
static pint_t evaluateExpression(pint_t expression, A &addressSpace,
|
||||
const R ®isters,
|
||||
pint_t initialStackValue);
|
||||
static pint_t getSavedRegister(A &addressSpace, const R ®isters,
|
||||
pint_t cfa, const RegisterLocation &savedReg);
|
||||
static double getSavedFloatRegister(A &addressSpace, const R ®isters,
|
||||
pint_t cfa, const RegisterLocation &savedReg);
|
||||
static v128 getSavedVectorRegister(A &addressSpace, const R ®isters,
|
||||
pint_t cfa, const RegisterLocation &savedReg);
|
||||
|
||||
static pint_t getCFA(A &addressSpace, const PrologInfo &prolog,
|
||||
const R ®isters) {
|
||||
if (prolog.cfaRegister != 0)
|
||||
return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) +
|
||||
prolog.cfaRegisterOffset);
|
||||
if (prolog.cfaExpression != 0)
|
||||
return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace,
|
||||
registers, 0);
|
||||
assert(0 && "getCFA(): unknown location");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
static bool getRA_SIGN_STATE(A &addressSpace, R registers, pint_t cfa,
|
||||
PrologInfo &prolog);
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename R>
|
||||
auto getSparcWCookie(const R &r, int) -> decltype(r.getWCookie()) {
|
||||
return r.getWCookie();
|
||||
}
|
||||
template <typename R> uint64_t getSparcWCookie(const R &, long) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename A, typename R>
|
||||
typename A::pint_t DwarfInstructions<A, R>::getSavedRegister(
|
||||
A &addressSpace, const R ®isters, pint_t cfa,
|
||||
const RegisterLocation &savedReg) {
|
||||
switch (savedReg.location) {
|
||||
case CFI_Parser<A>::kRegisterInCFA:
|
||||
return (pint_t)addressSpace.getRegister(cfa + (pint_t)savedReg.value);
|
||||
|
||||
case CFI_Parser<A>::kRegisterInCFADecrypt: // sparc64 specific
|
||||
return (pint_t)(addressSpace.getP(cfa + (pint_t)savedReg.value) ^
|
||||
getSparcWCookie(registers, 0));
|
||||
|
||||
case CFI_Parser<A>::kRegisterAtExpression:
|
||||
return (pint_t)addressSpace.getRegister(evaluateExpression(
|
||||
(pint_t)savedReg.value, addressSpace, registers, cfa));
|
||||
|
||||
case CFI_Parser<A>::kRegisterIsExpression:
|
||||
return evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||
registers, cfa);
|
||||
|
||||
case CFI_Parser<A>::kRegisterInRegister:
|
||||
return registers.getRegister((int)savedReg.value);
|
||||
case CFI_Parser<A>::kRegisterUndefined:
|
||||
return 0;
|
||||
case CFI_Parser<A>::kRegisterUnused:
|
||||
case CFI_Parser<A>::kRegisterOffsetFromCFA:
|
||||
// FIX ME
|
||||
break;
|
||||
}
|
||||
_LIBUNWIND_ABORT("unsupported restore location for register");
|
||||
}
|
||||
|
||||
template <typename A, typename R>
|
||||
double DwarfInstructions<A, R>::getSavedFloatRegister(
|
||||
A &addressSpace, const R ®isters, pint_t cfa,
|
||||
const RegisterLocation &savedReg) {
|
||||
switch (savedReg.location) {
|
||||
case CFI_Parser<A>::kRegisterInCFA:
|
||||
return addressSpace.getDouble(cfa + (pint_t)savedReg.value);
|
||||
|
||||
case CFI_Parser<A>::kRegisterAtExpression:
|
||||
return addressSpace.getDouble(
|
||||
evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||
registers, cfa));
|
||||
case CFI_Parser<A>::kRegisterUndefined:
|
||||
return 0.0;
|
||||
case CFI_Parser<A>::kRegisterInRegister:
|
||||
#ifndef _LIBUNWIND_TARGET_ARM
|
||||
return registers.getFloatRegister((int)savedReg.value);
|
||||
#endif
|
||||
case CFI_Parser<A>::kRegisterIsExpression:
|
||||
case CFI_Parser<A>::kRegisterUnused:
|
||||
case CFI_Parser<A>::kRegisterOffsetFromCFA:
|
||||
case CFI_Parser<A>::kRegisterInCFADecrypt:
|
||||
// FIX ME
|
||||
break;
|
||||
}
|
||||
_LIBUNWIND_ABORT("unsupported restore location for float register");
|
||||
}
|
||||
|
||||
template <typename A, typename R>
|
||||
v128 DwarfInstructions<A, R>::getSavedVectorRegister(
|
||||
A &addressSpace, const R ®isters, pint_t cfa,
|
||||
const RegisterLocation &savedReg) {
|
||||
switch (savedReg.location) {
|
||||
case CFI_Parser<A>::kRegisterInCFA:
|
||||
return addressSpace.getVector(cfa + (pint_t)savedReg.value);
|
||||
|
||||
case CFI_Parser<A>::kRegisterAtExpression:
|
||||
return addressSpace.getVector(
|
||||
evaluateExpression((pint_t)savedReg.value, addressSpace,
|
||||
registers, cfa));
|
||||
|
||||
case CFI_Parser<A>::kRegisterIsExpression:
|
||||
case CFI_Parser<A>::kRegisterUnused:
|
||||
case CFI_Parser<A>::kRegisterUndefined:
|
||||
case CFI_Parser<A>::kRegisterOffsetFromCFA:
|
||||
case CFI_Parser<A>::kRegisterInRegister:
|
||||
case CFI_Parser<A>::kRegisterInCFADecrypt:
|
||||
// FIX ME
|
||||
break;
|
||||
}
|
||||
_LIBUNWIND_ABORT("unsupported restore location for vector register");
|
||||
}
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
template <typename A, typename R>
|
||||
bool DwarfInstructions<A, R>::getRA_SIGN_STATE(A &addressSpace, R registers,
|
||||
pint_t cfa, PrologInfo &prolog) {
|
||||
pint_t raSignState;
|
||||
auto regloc = prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE];
|
||||
if (regloc.location == CFI_Parser<A>::kRegisterUnused)
|
||||
raSignState = static_cast<pint_t>(regloc.value);
|
||||
else
|
||||
raSignState = getSavedRegister(addressSpace, registers, cfa, regloc);
|
||||
|
||||
// Only bit[0] is meaningful.
|
||||
return raSignState & 0x01;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename A, typename R>
|
||||
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
|
||||
pint_t fdeStart, R ®isters,
|
||||
bool &isSignalFrame, bool stage2) {
|
||||
FDE_Info fdeInfo;
|
||||
CIE_Info cieInfo;
|
||||
if (CFI_Parser<A>::decodeFDE(addressSpace, fdeStart, &fdeInfo,
|
||||
&cieInfo) == NULL) {
|
||||
PrologInfo prolog;
|
||||
if (CFI_Parser<A>::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc,
|
||||
R::getArch(), &prolog)) {
|
||||
// get pointer to cfa (architecture specific)
|
||||
pint_t cfa = getCFA(addressSpace, prolog, registers);
|
||||
|
||||
(void)stage2;
|
||||
// __unw_step_stage2 is not used for cross unwinding, so we use
|
||||
// __aarch64__ rather than LIBUNWIND_TARGET_AARCH64 to make sure we are
|
||||
// building for AArch64 natively.
|
||||
#if defined(__aarch64__)
|
||||
if (stage2 && cieInfo.mteTaggedFrame) {
|
||||
pint_t sp = registers.getSP();
|
||||
pint_t p = sp;
|
||||
// AArch64 doesn't require the value of SP to be 16-byte aligned at
|
||||
// all times, only at memory accesses and public interfaces [1]. Thus,
|
||||
// a signal could arrive at a point where SP is not aligned properly.
|
||||
// In that case, the kernel fixes up [2] the signal frame, but we
|
||||
// still have a misaligned SP in the previous frame. If that signal
|
||||
// handler caused stack unwinding, we would have an unaligned SP.
|
||||
// We do not need to fix up the CFA, as that is the SP at a "public
|
||||
// interface".
|
||||
// [1]:
|
||||
// https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#622the-stack
|
||||
// [2]:
|
||||
// https://github.com/torvalds/linux/blob/1930a6e739c4b4a654a69164dbe39e554d228915/arch/arm64/kernel/signal.c#L718
|
||||
p &= ~0xfULL;
|
||||
// CFA is the bottom of the current stack frame.
|
||||
for (; p < cfa; p += 16) {
|
||||
__asm__ __volatile__(".arch armv8.5-a\n"
|
||||
".arch_extension memtag\n"
|
||||
"stg %[Ptr], [%[Ptr]]\n"
|
||||
:
|
||||
: [Ptr] "r"(p)
|
||||
: "memory");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// restore registers that DWARF says were saved
|
||||
R newRegisters = registers;
|
||||
|
||||
// Typically, the CFA is the stack pointer at the call site in
|
||||
// the previous frame. However, there are scenarios in which this is not
|
||||
// true. For example, if we switched to a new stack. In that case, the
|
||||
// value of the previous SP might be indicated by a CFI directive.
|
||||
//
|
||||
// We set the SP here to the CFA, allowing for it to be overridden
|
||||
// by a CFI directive later on.
|
||||
newRegisters.setSP(cfa);
|
||||
|
||||
pint_t returnAddress = 0;
|
||||
constexpr int lastReg = R::lastDwarfRegNum();
|
||||
static_assert(static_cast<int>(CFI_Parser<A>::kMaxRegisterNumber) >=
|
||||
lastReg,
|
||||
"register range too large");
|
||||
assert(lastReg >= (int)cieInfo.returnAddressRegister &&
|
||||
"register range does not contain return address register");
|
||||
for (int i = 0; i <= lastReg; ++i) {
|
||||
if (prolog.savedRegisters[i].location !=
|
||||
CFI_Parser<A>::kRegisterUnused) {
|
||||
if (registers.validFloatRegister(i))
|
||||
newRegisters.setFloatRegister(
|
||||
i, getSavedFloatRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[i]));
|
||||
else if (registers.validVectorRegister(i))
|
||||
newRegisters.setVectorRegister(
|
||||
i, getSavedVectorRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[i]));
|
||||
else if (i == (int)cieInfo.returnAddressRegister)
|
||||
returnAddress = getSavedRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[i]);
|
||||
else if (registers.validRegister(i))
|
||||
newRegisters.setRegister(
|
||||
i, getSavedRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[i]));
|
||||
else
|
||||
return UNW_EBADREG;
|
||||
} else if (i == (int)cieInfo.returnAddressRegister) {
|
||||
// Leaf function keeps the return address in register and there is no
|
||||
// explicit instructions how to restore it.
|
||||
returnAddress = registers.getRegister(cieInfo.returnAddressRegister);
|
||||
}
|
||||
}
|
||||
|
||||
isSignalFrame = cieInfo.isSignalFrame;
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
// If the target is aarch64 then the return address may have been signed
|
||||
// using the v8.3 pointer authentication extensions. The original
|
||||
// return address needs to be authenticated before the return address is
|
||||
// restored. autia1716 is used instead of autia as autia1716 assembles
|
||||
// to a NOP on pre-v8.3a architectures.
|
||||
if ((R::getArch() == REGISTERS_ARM64) &&
|
||||
getRA_SIGN_STATE(addressSpace, registers, cfa, prolog) &&
|
||||
returnAddress != 0) {
|
||||
#if !defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||
return UNW_ECROSSRASIGNING;
|
||||
#else
|
||||
register unsigned long long x17 __asm("x17") = returnAddress;
|
||||
register unsigned long long x16 __asm("x16") = cfa;
|
||||
|
||||
// These are the autia1716/autib1716 instructions. The hint instructions
|
||||
// are used here as gcc does not assemble autia1716/autib1716 for pre
|
||||
// armv8.3a targets.
|
||||
if (cieInfo.addressesSignedWithBKey)
|
||||
asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716
|
||||
else
|
||||
asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716
|
||||
returnAddress = x17;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_IS_NATIVE_ONLY) && defined(_LIBUNWIND_TARGET_ARM) && \
|
||||
defined(__ARM_FEATURE_PAUTH)
|
||||
if ((R::getArch() == REGISTERS_ARM) &&
|
||||
prolog.savedRegisters[UNW_ARM_RA_AUTH_CODE].value) {
|
||||
pint_t pac =
|
||||
getSavedRegister(addressSpace, registers, cfa,
|
||||
prolog.savedRegisters[UNW_ARM_RA_AUTH_CODE]);
|
||||
__asm__ __volatile__("autg %0, %1, %2"
|
||||
:
|
||||
: "r"(pac), "r"(returnAddress), "r"(cfa)
|
||||
:);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_SPARC)
|
||||
if (R::getArch() == REGISTERS_SPARC) {
|
||||
// Skip call site instruction and delay slot
|
||||
returnAddress += 8;
|
||||
// Skip unimp instruction if function returns a struct
|
||||
if ((addressSpace.get32(returnAddress) & 0xC1C00000) == 0)
|
||||
returnAddress += 4;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_SPARC64)
|
||||
// Skip call site instruction and delay slot.
|
||||
if (R::getArch() == REGISTERS_SPARC64)
|
||||
returnAddress += 8;
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_PPC64)
|
||||
#define PPC64_ELFV1_R2_LOAD_INST_ENCODING 0xe8410028u // ld r2,40(r1)
|
||||
#define PPC64_ELFV1_R2_OFFSET 40
|
||||
#define PPC64_ELFV2_R2_LOAD_INST_ENCODING 0xe8410018u // ld r2,24(r1)
|
||||
#define PPC64_ELFV2_R2_OFFSET 24
|
||||
// If the instruction at return address is a TOC (r2) restore,
|
||||
// then r2 was saved and needs to be restored.
|
||||
// ELFv2 ABI specifies that the TOC Pointer must be saved at SP + 24,
|
||||
// while in ELFv1 ABI it is saved at SP + 40.
|
||||
if (R::getArch() == REGISTERS_PPC64 && returnAddress != 0) {
|
||||
pint_t sp = newRegisters.getRegister(UNW_REG_SP);
|
||||
pint_t r2 = 0;
|
||||
switch (addressSpace.get32(returnAddress)) {
|
||||
case PPC64_ELFV1_R2_LOAD_INST_ENCODING:
|
||||
r2 = addressSpace.get64(sp + PPC64_ELFV1_R2_OFFSET);
|
||||
break;
|
||||
case PPC64_ELFV2_R2_LOAD_INST_ENCODING:
|
||||
r2 = addressSpace.get64(sp + PPC64_ELFV2_R2_OFFSET);
|
||||
break;
|
||||
}
|
||||
if (r2)
|
||||
newRegisters.setRegister(UNW_PPC64_R2, r2);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Return address is address after call site instruction, so setting IP to
|
||||
// that does simulates a return.
|
||||
newRegisters.setIP(returnAddress);
|
||||
|
||||
// Simulate the step by replacing the register set with the new ones.
|
||||
registers = newRegisters;
|
||||
|
||||
return UNW_STEP_SUCCESS;
|
||||
}
|
||||
}
|
||||
return UNW_EBADFRAME;
|
||||
}
|
||||
|
||||
template <typename A, typename R>
|
||||
typename A::pint_t
|
||||
DwarfInstructions<A, R>::evaluateExpression(pint_t expression, A &addressSpace,
|
||||
const R ®isters,
|
||||
pint_t initialStackValue) {
|
||||
const bool log = false;
|
||||
pint_t p = expression;
|
||||
pint_t expressionEnd = expression + 20; // temp, until len read
|
||||
pint_t length = (pint_t)addressSpace.getULEB128(p, expressionEnd);
|
||||
expressionEnd = p + length;
|
||||
if (log)
|
||||
fprintf(stderr, "evaluateExpression(): length=%" PRIu64 "\n",
|
||||
(uint64_t)length);
|
||||
pint_t stack[100];
|
||||
pint_t *sp = stack;
|
||||
*(++sp) = initialStackValue;
|
||||
|
||||
while (p < expressionEnd) {
|
||||
if (log) {
|
||||
for (pint_t *t = sp; t > stack; --t) {
|
||||
fprintf(stderr, "sp[] = 0x%" PRIx64 "\n", (uint64_t)(*t));
|
||||
}
|
||||
}
|
||||
uint8_t opcode = addressSpace.get8(p++);
|
||||
sint_t svalue, svalue2;
|
||||
pint_t value;
|
||||
uint32_t reg;
|
||||
switch (opcode) {
|
||||
case DW_OP_addr:
|
||||
// push immediate address sized value
|
||||
value = addressSpace.getP(p);
|
||||
p += sizeof(pint_t);
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_deref:
|
||||
// pop stack, dereference, push result
|
||||
value = *sp--;
|
||||
*(++sp) = addressSpace.getP(value);
|
||||
if (log)
|
||||
fprintf(stderr, "dereference 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const1u:
|
||||
// push immediate 1 byte value
|
||||
value = addressSpace.get8(p);
|
||||
p += 1;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const1s:
|
||||
// push immediate 1 byte signed value
|
||||
svalue = (int8_t) addressSpace.get8(p);
|
||||
p += 1;
|
||||
*(++sp) = (pint_t)svalue;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_const2u:
|
||||
// push immediate 2 byte value
|
||||
value = addressSpace.get16(p);
|
||||
p += 2;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const2s:
|
||||
// push immediate 2 byte signed value
|
||||
svalue = (int16_t) addressSpace.get16(p);
|
||||
p += 2;
|
||||
*(++sp) = (pint_t)svalue;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_const4u:
|
||||
// push immediate 4 byte value
|
||||
value = addressSpace.get32(p);
|
||||
p += 4;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const4s:
|
||||
// push immediate 4 byte signed value
|
||||
svalue = (int32_t)addressSpace.get32(p);
|
||||
p += 4;
|
||||
*(++sp) = (pint_t)svalue;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_const8u:
|
||||
// push immediate 8 byte value
|
||||
value = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_const8s:
|
||||
// push immediate 8 byte signed value
|
||||
value = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_constu:
|
||||
// push immediate ULEB128 value
|
||||
value = (pint_t)addressSpace.getULEB128(p, expressionEnd);
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_consts:
|
||||
// push immediate SLEB128 value
|
||||
svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd);
|
||||
*(++sp) = (pint_t)svalue;
|
||||
if (log)
|
||||
fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_dup:
|
||||
// push top of stack
|
||||
value = *sp;
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "duplicate top of stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_drop:
|
||||
// pop
|
||||
--sp;
|
||||
if (log)
|
||||
fprintf(stderr, "pop top of stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_over:
|
||||
// dup second
|
||||
value = sp[-1];
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "duplicate second in stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_pick:
|
||||
// pick from
|
||||
reg = addressSpace.get8(p);
|
||||
p += 1;
|
||||
value = sp[-(int)reg];
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "duplicate %d in stack\n", reg);
|
||||
break;
|
||||
|
||||
case DW_OP_swap:
|
||||
// swap top two
|
||||
value = sp[0];
|
||||
sp[0] = sp[-1];
|
||||
sp[-1] = value;
|
||||
if (log)
|
||||
fprintf(stderr, "swap top of stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_rot:
|
||||
// rotate top three
|
||||
value = sp[0];
|
||||
sp[0] = sp[-1];
|
||||
sp[-1] = sp[-2];
|
||||
sp[-2] = value;
|
||||
if (log)
|
||||
fprintf(stderr, "rotate top three of stack\n");
|
||||
break;
|
||||
|
||||
case DW_OP_xderef:
|
||||
// pop stack, dereference, push result
|
||||
value = *sp--;
|
||||
*sp = *((pint_t*)value);
|
||||
if (log)
|
||||
fprintf(stderr, "x-dereference 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_abs:
|
||||
svalue = (sint_t)*sp;
|
||||
if (svalue < 0)
|
||||
*sp = (pint_t)(-svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "abs\n");
|
||||
break;
|
||||
|
||||
case DW_OP_and:
|
||||
value = *sp--;
|
||||
*sp &= value;
|
||||
if (log)
|
||||
fprintf(stderr, "and\n");
|
||||
break;
|
||||
|
||||
case DW_OP_div:
|
||||
svalue = (sint_t)(*sp--);
|
||||
svalue2 = (sint_t)*sp;
|
||||
*sp = (pint_t)(svalue2 / svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "div\n");
|
||||
break;
|
||||
|
||||
case DW_OP_minus:
|
||||
value = *sp--;
|
||||
*sp = *sp - value;
|
||||
if (log)
|
||||
fprintf(stderr, "minus\n");
|
||||
break;
|
||||
|
||||
case DW_OP_mod:
|
||||
svalue = (sint_t)(*sp--);
|
||||
svalue2 = (sint_t)*sp;
|
||||
*sp = (pint_t)(svalue2 % svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "module\n");
|
||||
break;
|
||||
|
||||
case DW_OP_mul:
|
||||
svalue = (sint_t)(*sp--);
|
||||
svalue2 = (sint_t)*sp;
|
||||
*sp = (pint_t)(svalue2 * svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "mul\n");
|
||||
break;
|
||||
|
||||
case DW_OP_neg:
|
||||
*sp = 0 - *sp;
|
||||
if (log)
|
||||
fprintf(stderr, "neg\n");
|
||||
break;
|
||||
|
||||
case DW_OP_not:
|
||||
svalue = (sint_t)(*sp);
|
||||
*sp = (pint_t)(~svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "not\n");
|
||||
break;
|
||||
|
||||
case DW_OP_or:
|
||||
value = *sp--;
|
||||
*sp |= value;
|
||||
if (log)
|
||||
fprintf(stderr, "or\n");
|
||||
break;
|
||||
|
||||
case DW_OP_plus:
|
||||
value = *sp--;
|
||||
*sp += value;
|
||||
if (log)
|
||||
fprintf(stderr, "plus\n");
|
||||
break;
|
||||
|
||||
case DW_OP_plus_uconst:
|
||||
// pop stack, add uelb128 constant, push result
|
||||
*sp += static_cast<pint_t>(addressSpace.getULEB128(p, expressionEnd));
|
||||
if (log)
|
||||
fprintf(stderr, "add constant\n");
|
||||
break;
|
||||
|
||||
case DW_OP_shl:
|
||||
value = *sp--;
|
||||
*sp = *sp << value;
|
||||
if (log)
|
||||
fprintf(stderr, "shift left\n");
|
||||
break;
|
||||
|
||||
case DW_OP_shr:
|
||||
value = *sp--;
|
||||
*sp = *sp >> value;
|
||||
if (log)
|
||||
fprintf(stderr, "shift left\n");
|
||||
break;
|
||||
|
||||
case DW_OP_shra:
|
||||
value = *sp--;
|
||||
svalue = (sint_t)*sp;
|
||||
*sp = (pint_t)(svalue >> value);
|
||||
if (log)
|
||||
fprintf(stderr, "shift left arithmetic\n");
|
||||
break;
|
||||
|
||||
case DW_OP_xor:
|
||||
value = *sp--;
|
||||
*sp ^= value;
|
||||
if (log)
|
||||
fprintf(stderr, "xor\n");
|
||||
break;
|
||||
|
||||
case DW_OP_skip:
|
||||
svalue = (int16_t) addressSpace.get16(p);
|
||||
p += 2;
|
||||
p = (pint_t)((sint_t)p + svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "skip %" PRIu64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_bra:
|
||||
svalue = (int16_t) addressSpace.get16(p);
|
||||
p += 2;
|
||||
if (*sp--)
|
||||
p = (pint_t)((sint_t)p + svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "bra %" PRIu64 "\n", (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_eq:
|
||||
value = *sp--;
|
||||
*sp = (*sp == value);
|
||||
if (log)
|
||||
fprintf(stderr, "eq\n");
|
||||
break;
|
||||
|
||||
case DW_OP_ge:
|
||||
value = *sp--;
|
||||
*sp = (*sp >= value);
|
||||
if (log)
|
||||
fprintf(stderr, "ge\n");
|
||||
break;
|
||||
|
||||
case DW_OP_gt:
|
||||
value = *sp--;
|
||||
*sp = (*sp > value);
|
||||
if (log)
|
||||
fprintf(stderr, "gt\n");
|
||||
break;
|
||||
|
||||
case DW_OP_le:
|
||||
value = *sp--;
|
||||
*sp = (*sp <= value);
|
||||
if (log)
|
||||
fprintf(stderr, "le\n");
|
||||
break;
|
||||
|
||||
case DW_OP_lt:
|
||||
value = *sp--;
|
||||
*sp = (*sp < value);
|
||||
if (log)
|
||||
fprintf(stderr, "lt\n");
|
||||
break;
|
||||
|
||||
case DW_OP_ne:
|
||||
value = *sp--;
|
||||
*sp = (*sp != value);
|
||||
if (log)
|
||||
fprintf(stderr, "ne\n");
|
||||
break;
|
||||
|
||||
case DW_OP_lit0:
|
||||
case DW_OP_lit1:
|
||||
case DW_OP_lit2:
|
||||
case DW_OP_lit3:
|
||||
case DW_OP_lit4:
|
||||
case DW_OP_lit5:
|
||||
case DW_OP_lit6:
|
||||
case DW_OP_lit7:
|
||||
case DW_OP_lit8:
|
||||
case DW_OP_lit9:
|
||||
case DW_OP_lit10:
|
||||
case DW_OP_lit11:
|
||||
case DW_OP_lit12:
|
||||
case DW_OP_lit13:
|
||||
case DW_OP_lit14:
|
||||
case DW_OP_lit15:
|
||||
case DW_OP_lit16:
|
||||
case DW_OP_lit17:
|
||||
case DW_OP_lit18:
|
||||
case DW_OP_lit19:
|
||||
case DW_OP_lit20:
|
||||
case DW_OP_lit21:
|
||||
case DW_OP_lit22:
|
||||
case DW_OP_lit23:
|
||||
case DW_OP_lit24:
|
||||
case DW_OP_lit25:
|
||||
case DW_OP_lit26:
|
||||
case DW_OP_lit27:
|
||||
case DW_OP_lit28:
|
||||
case DW_OP_lit29:
|
||||
case DW_OP_lit30:
|
||||
case DW_OP_lit31:
|
||||
value = static_cast<pint_t>(opcode - DW_OP_lit0);
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "push literal 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_reg0:
|
||||
case DW_OP_reg1:
|
||||
case DW_OP_reg2:
|
||||
case DW_OP_reg3:
|
||||
case DW_OP_reg4:
|
||||
case DW_OP_reg5:
|
||||
case DW_OP_reg6:
|
||||
case DW_OP_reg7:
|
||||
case DW_OP_reg8:
|
||||
case DW_OP_reg9:
|
||||
case DW_OP_reg10:
|
||||
case DW_OP_reg11:
|
||||
case DW_OP_reg12:
|
||||
case DW_OP_reg13:
|
||||
case DW_OP_reg14:
|
||||
case DW_OP_reg15:
|
||||
case DW_OP_reg16:
|
||||
case DW_OP_reg17:
|
||||
case DW_OP_reg18:
|
||||
case DW_OP_reg19:
|
||||
case DW_OP_reg20:
|
||||
case DW_OP_reg21:
|
||||
case DW_OP_reg22:
|
||||
case DW_OP_reg23:
|
||||
case DW_OP_reg24:
|
||||
case DW_OP_reg25:
|
||||
case DW_OP_reg26:
|
||||
case DW_OP_reg27:
|
||||
case DW_OP_reg28:
|
||||
case DW_OP_reg29:
|
||||
case DW_OP_reg30:
|
||||
case DW_OP_reg31:
|
||||
reg = static_cast<uint32_t>(opcode - DW_OP_reg0);
|
||||
*(++sp) = registers.getRegister((int)reg);
|
||||
if (log)
|
||||
fprintf(stderr, "push reg %d\n", reg);
|
||||
break;
|
||||
|
||||
case DW_OP_regx:
|
||||
reg = static_cast<uint32_t>(addressSpace.getULEB128(p, expressionEnd));
|
||||
*(++sp) = registers.getRegister((int)reg);
|
||||
if (log)
|
||||
fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_breg0:
|
||||
case DW_OP_breg1:
|
||||
case DW_OP_breg2:
|
||||
case DW_OP_breg3:
|
||||
case DW_OP_breg4:
|
||||
case DW_OP_breg5:
|
||||
case DW_OP_breg6:
|
||||
case DW_OP_breg7:
|
||||
case DW_OP_breg8:
|
||||
case DW_OP_breg9:
|
||||
case DW_OP_breg10:
|
||||
case DW_OP_breg11:
|
||||
case DW_OP_breg12:
|
||||
case DW_OP_breg13:
|
||||
case DW_OP_breg14:
|
||||
case DW_OP_breg15:
|
||||
case DW_OP_breg16:
|
||||
case DW_OP_breg17:
|
||||
case DW_OP_breg18:
|
||||
case DW_OP_breg19:
|
||||
case DW_OP_breg20:
|
||||
case DW_OP_breg21:
|
||||
case DW_OP_breg22:
|
||||
case DW_OP_breg23:
|
||||
case DW_OP_breg24:
|
||||
case DW_OP_breg25:
|
||||
case DW_OP_breg26:
|
||||
case DW_OP_breg27:
|
||||
case DW_OP_breg28:
|
||||
case DW_OP_breg29:
|
||||
case DW_OP_breg30:
|
||||
case DW_OP_breg31:
|
||||
reg = static_cast<uint32_t>(opcode - DW_OP_breg0);
|
||||
svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd);
|
||||
svalue += static_cast<sint_t>(registers.getRegister((int)reg));
|
||||
*(++sp) = (pint_t)(svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_bregx:
|
||||
reg = static_cast<uint32_t>(addressSpace.getULEB128(p, expressionEnd));
|
||||
svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd);
|
||||
svalue += static_cast<sint_t>(registers.getRegister((int)reg));
|
||||
*(++sp) = (pint_t)(svalue);
|
||||
if (log)
|
||||
fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue);
|
||||
break;
|
||||
|
||||
case DW_OP_fbreg:
|
||||
_LIBUNWIND_ABORT("DW_OP_fbreg not implemented");
|
||||
break;
|
||||
|
||||
case DW_OP_piece:
|
||||
_LIBUNWIND_ABORT("DW_OP_piece not implemented");
|
||||
break;
|
||||
|
||||
case DW_OP_deref_size:
|
||||
// pop stack, dereference, push result
|
||||
value = *sp--;
|
||||
switch (addressSpace.get8(p++)) {
|
||||
case 1:
|
||||
value = addressSpace.get8(value);
|
||||
break;
|
||||
case 2:
|
||||
value = addressSpace.get16(value);
|
||||
break;
|
||||
case 4:
|
||||
value = addressSpace.get32(value);
|
||||
break;
|
||||
case 8:
|
||||
value = (pint_t)addressSpace.get64(value);
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_ABORT("DW_OP_deref_size with bad size");
|
||||
}
|
||||
*(++sp) = value;
|
||||
if (log)
|
||||
fprintf(stderr, "sized dereference 0x%" PRIx64 "\n", (uint64_t)value);
|
||||
break;
|
||||
|
||||
case DW_OP_xderef_size:
|
||||
case DW_OP_nop:
|
||||
case DW_OP_push_object_addres:
|
||||
case DW_OP_call2:
|
||||
case DW_OP_call4:
|
||||
case DW_OP_call_ref:
|
||||
default:
|
||||
_LIBUNWIND_ABORT("DWARF opcode not implemented");
|
||||
}
|
||||
|
||||
}
|
||||
if (log)
|
||||
fprintf(stderr, "expression evaluates to 0x%" PRIx64 "\n", (uint64_t)*sp);
|
||||
return *sp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
#endif // __DWARF_INSTRUCTIONS_HPP__
|
853
third_party/libunwind/DwarfParser.hpp
vendored
Normal file
853
third_party/libunwind/DwarfParser.hpp
vendored
Normal file
|
@ -0,0 +1,853 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Parses DWARF CFIs (FDEs and CIEs).
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __DWARF_PARSER_HPP__
|
||||
#define __DWARF_PARSER_HPP__
|
||||
|
||||
#include "libc/isystem/inttypes.h"
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/isystem/stdlib.h"
|
||||
|
||||
#include "third_party/libunwind/include/libunwind.h"
|
||||
#include "third_party/libunwind/dwarf2.h"
|
||||
#include "third_party/libunwind/Registers.hpp"
|
||||
|
||||
#include "third_party/libunwind/config.h"
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records.
|
||||
/// See DWARF Spec for details:
|
||||
/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
|
||||
///
|
||||
template <typename A>
|
||||
class CFI_Parser {
|
||||
public:
|
||||
typedef typename A::pint_t pint_t;
|
||||
|
||||
/// Information encoded in a CIE (Common Information Entry)
|
||||
struct CIE_Info {
|
||||
pint_t cieStart;
|
||||
pint_t cieLength;
|
||||
pint_t cieInstructions;
|
||||
uint8_t pointerEncoding;
|
||||
uint8_t lsdaEncoding;
|
||||
uint8_t personalityEncoding;
|
||||
uint8_t personalityOffsetInCIE;
|
||||
pint_t personality;
|
||||
uint32_t codeAlignFactor;
|
||||
int dataAlignFactor;
|
||||
bool isSignalFrame;
|
||||
bool fdesHaveAugmentationData;
|
||||
uint8_t returnAddressRegister;
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
bool addressesSignedWithBKey;
|
||||
bool mteTaggedFrame;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// Information about an FDE (Frame Description Entry)
|
||||
struct FDE_Info {
|
||||
pint_t fdeStart;
|
||||
pint_t fdeLength;
|
||||
pint_t fdeInstructions;
|
||||
pint_t pcStart;
|
||||
pint_t pcEnd;
|
||||
pint_t lsda;
|
||||
};
|
||||
|
||||
enum {
|
||||
kMaxRegisterNumber = _LIBUNWIND_HIGHEST_DWARF_REGISTER
|
||||
};
|
||||
enum RegisterSavedWhere {
|
||||
kRegisterUnused,
|
||||
kRegisterUndefined,
|
||||
kRegisterInCFA,
|
||||
kRegisterInCFADecrypt, // sparc64 specific
|
||||
kRegisterOffsetFromCFA,
|
||||
kRegisterInRegister,
|
||||
kRegisterAtExpression,
|
||||
kRegisterIsExpression
|
||||
};
|
||||
struct RegisterLocation {
|
||||
RegisterSavedWhere location;
|
||||
bool initialStateSaved;
|
||||
int64_t value;
|
||||
};
|
||||
/// Information about a frame layout and registers saved determined
|
||||
/// by "running" the DWARF FDE "instructions"
|
||||
struct PrologInfo {
|
||||
uint32_t cfaRegister;
|
||||
int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset
|
||||
int64_t cfaExpression; // CFA = expression
|
||||
uint32_t spExtraArgSize;
|
||||
RegisterLocation savedRegisters[kMaxRegisterNumber + 1];
|
||||
enum class InitializeTime { kLazy, kNormal };
|
||||
|
||||
// When saving registers, this data structure is lazily initialized.
|
||||
PrologInfo(InitializeTime IT = InitializeTime::kNormal) {
|
||||
if (IT == InitializeTime::kNormal)
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
void checkSaveRegister(uint64_t reg, PrologInfo &initialState) {
|
||||
if (!savedRegisters[reg].initialStateSaved) {
|
||||
initialState.savedRegisters[reg] = savedRegisters[reg];
|
||||
savedRegisters[reg].initialStateSaved = true;
|
||||
}
|
||||
}
|
||||
void setRegister(uint64_t reg, RegisterSavedWhere newLocation,
|
||||
int64_t newValue, PrologInfo &initialState) {
|
||||
checkSaveRegister(reg, initialState);
|
||||
savedRegisters[reg].location = newLocation;
|
||||
savedRegisters[reg].value = newValue;
|
||||
}
|
||||
void setRegisterLocation(uint64_t reg, RegisterSavedWhere newLocation,
|
||||
PrologInfo &initialState) {
|
||||
checkSaveRegister(reg, initialState);
|
||||
savedRegisters[reg].location = newLocation;
|
||||
}
|
||||
void setRegisterValue(uint64_t reg, int64_t newValue,
|
||||
PrologInfo &initialState) {
|
||||
checkSaveRegister(reg, initialState);
|
||||
savedRegisters[reg].value = newValue;
|
||||
}
|
||||
void restoreRegisterToInitialState(uint64_t reg, PrologInfo &initialState) {
|
||||
if (savedRegisters[reg].initialStateSaved)
|
||||
savedRegisters[reg] = initialState.savedRegisters[reg];
|
||||
// else the register still holds its initial state
|
||||
}
|
||||
};
|
||||
|
||||
struct PrologInfoStackEntry {
|
||||
PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i)
|
||||
: next(n), info(i) {}
|
||||
PrologInfoStackEntry *next;
|
||||
PrologInfo info;
|
||||
};
|
||||
|
||||
struct RememberStack {
|
||||
PrologInfoStackEntry *entry;
|
||||
RememberStack() : entry(nullptr) {}
|
||||
~RememberStack() {
|
||||
#if defined(_LIBUNWIND_REMEMBER_CLEANUP_NEEDED)
|
||||
// Clean up rememberStack. Even in the case where every
|
||||
// DW_CFA_remember_state is paired with a DW_CFA_restore_state,
|
||||
// parseInstructions can skip restore opcodes if it reaches the target PC
|
||||
// and stops interpreting, so we have to make sure we don't leak memory.
|
||||
while (entry) {
|
||||
PrologInfoStackEntry *next = entry->next;
|
||||
_LIBUNWIND_REMEMBER_FREE(entry);
|
||||
entry = next;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
|
||||
size_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo,
|
||||
CIE_Info *cieInfo);
|
||||
static const char *decodeFDE(A &addressSpace, pint_t fdeStart,
|
||||
FDE_Info *fdeInfo, CIE_Info *cieInfo,
|
||||
bool useCIEInfo = false);
|
||||
static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo,
|
||||
const CIE_Info &cieInfo, pint_t upToPC,
|
||||
int arch, PrologInfo *results);
|
||||
|
||||
static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo);
|
||||
};
|
||||
|
||||
/// Parse a FDE into a CIE_Info and an FDE_Info. If useCIEInfo is
|
||||
/// true, treat cieInfo as already-parsed CIE_Info (whose start offset
|
||||
/// must match the one specified by the FDE) rather than parsing the
|
||||
/// one indicated within the FDE.
|
||||
template <typename A>
|
||||
const char *CFI_Parser<A>::decodeFDE(A &addressSpace, pint_t fdeStart,
|
||||
FDE_Info *fdeInfo, CIE_Info *cieInfo,
|
||||
bool useCIEInfo) {
|
||||
pint_t p = fdeStart;
|
||||
pint_t cfiLength = (pint_t)addressSpace.get32(p);
|
||||
p += 4;
|
||||
if (cfiLength == 0xffffffff) {
|
||||
// 0xffffffff means length is really next 8 bytes
|
||||
cfiLength = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
}
|
||||
if (cfiLength == 0)
|
||||
return "FDE has zero length"; // zero terminator
|
||||
uint32_t ciePointer = addressSpace.get32(p);
|
||||
if (ciePointer == 0)
|
||||
return "FDE is really a CIE"; // this is a CIE not an FDE
|
||||
pint_t nextCFI = p + cfiLength;
|
||||
pint_t cieStart = p - ciePointer;
|
||||
if (useCIEInfo) {
|
||||
if (cieInfo->cieStart != cieStart)
|
||||
return "CIE start does not match";
|
||||
} else {
|
||||
const char *err = parseCIE(addressSpace, cieStart, cieInfo);
|
||||
if (err != NULL)
|
||||
return err;
|
||||
}
|
||||
p += 4;
|
||||
// Parse pc begin and range.
|
||||
pint_t pcStart =
|
||||
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
|
||||
pint_t pcRange =
|
||||
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F);
|
||||
// Parse rest of info.
|
||||
fdeInfo->lsda = 0;
|
||||
// Check for augmentation length.
|
||||
if (cieInfo->fdesHaveAugmentationData) {
|
||||
pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
|
||||
pint_t endOfAug = p + augLen;
|
||||
if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
|
||||
// Peek at value (without indirection). Zero means no LSDA.
|
||||
pint_t lsdaStart = p;
|
||||
if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) !=
|
||||
0) {
|
||||
// Reset pointer and re-parse LSDA address.
|
||||
p = lsdaStart;
|
||||
fdeInfo->lsda =
|
||||
addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
|
||||
}
|
||||
}
|
||||
p = endOfAug;
|
||||
}
|
||||
fdeInfo->fdeStart = fdeStart;
|
||||
fdeInfo->fdeLength = nextCFI - fdeStart;
|
||||
fdeInfo->fdeInstructions = p;
|
||||
fdeInfo->pcStart = pcStart;
|
||||
fdeInfo->pcEnd = pcStart + pcRange;
|
||||
return NULL; // success
|
||||
}
|
||||
|
||||
/// Scan an eh_frame section to find an FDE for a pc
|
||||
template <typename A>
|
||||
bool CFI_Parser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart,
|
||||
size_t sectionLength, pint_t fdeHint,
|
||||
FDE_Info *fdeInfo, CIE_Info *cieInfo) {
|
||||
//fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc);
|
||||
pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart;
|
||||
const pint_t ehSectionEnd = (sectionLength == SIZE_MAX)
|
||||
? static_cast<pint_t>(-1)
|
||||
: (ehSectionStart + sectionLength);
|
||||
while (p < ehSectionEnd) {
|
||||
pint_t currentCFI = p;
|
||||
//fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p);
|
||||
pint_t cfiLength = addressSpace.get32(p);
|
||||
p += 4;
|
||||
if (cfiLength == 0xffffffff) {
|
||||
// 0xffffffff means length is really next 8 bytes
|
||||
cfiLength = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
}
|
||||
if (cfiLength == 0)
|
||||
return false; // zero terminator
|
||||
uint32_t id = addressSpace.get32(p);
|
||||
if (id == 0) {
|
||||
// Skip over CIEs.
|
||||
p += cfiLength;
|
||||
} else {
|
||||
// Process FDE to see if it covers pc.
|
||||
pint_t nextCFI = p + cfiLength;
|
||||
uint32_t ciePointer = addressSpace.get32(p);
|
||||
pint_t cieStart = p - ciePointer;
|
||||
// Validate pointer to CIE is within section.
|
||||
if ((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)) {
|
||||
if (parseCIE(addressSpace, cieStart, cieInfo) == NULL) {
|
||||
p += 4;
|
||||
// Parse pc begin and range.
|
||||
pint_t pcStart =
|
||||
addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding);
|
||||
pint_t pcRange = addressSpace.getEncodedP(
|
||||
p, nextCFI, cieInfo->pointerEncoding & 0x0F);
|
||||
// Test if pc is within the function this FDE covers.
|
||||
if ((pcStart < pc) && (pc <= pcStart + pcRange)) {
|
||||
// parse rest of info
|
||||
fdeInfo->lsda = 0;
|
||||
// check for augmentation length
|
||||
if (cieInfo->fdesHaveAugmentationData) {
|
||||
pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI);
|
||||
pint_t endOfAug = p + augLen;
|
||||
if (cieInfo->lsdaEncoding != DW_EH_PE_omit) {
|
||||
// Peek at value (without indirection). Zero means no LSDA.
|
||||
pint_t lsdaStart = p;
|
||||
if (addressSpace.getEncodedP(
|
||||
p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0) {
|
||||
// Reset pointer and re-parse LSDA address.
|
||||
p = lsdaStart;
|
||||
fdeInfo->lsda = addressSpace
|
||||
.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding);
|
||||
}
|
||||
}
|
||||
p = endOfAug;
|
||||
}
|
||||
fdeInfo->fdeStart = currentCFI;
|
||||
fdeInfo->fdeLength = nextCFI - currentCFI;
|
||||
fdeInfo->fdeInstructions = p;
|
||||
fdeInfo->pcStart = pcStart;
|
||||
fdeInfo->pcEnd = pcStart + pcRange;
|
||||
return true;
|
||||
} else {
|
||||
// pc is not in begin/range, skip this FDE
|
||||
}
|
||||
} else {
|
||||
// Malformed CIE, now augmentation describing pc range encoding.
|
||||
}
|
||||
} else {
|
||||
// malformed FDE. CIE is bad
|
||||
}
|
||||
p = nextCFI;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Extract info from a CIE
|
||||
template <typename A>
|
||||
const char *CFI_Parser<A>::parseCIE(A &addressSpace, pint_t cie,
|
||||
CIE_Info *cieInfo) {
|
||||
cieInfo->pointerEncoding = 0;
|
||||
cieInfo->lsdaEncoding = DW_EH_PE_omit;
|
||||
cieInfo->personalityEncoding = 0;
|
||||
cieInfo->personalityOffsetInCIE = 0;
|
||||
cieInfo->personality = 0;
|
||||
cieInfo->codeAlignFactor = 0;
|
||||
cieInfo->dataAlignFactor = 0;
|
||||
cieInfo->isSignalFrame = false;
|
||||
cieInfo->fdesHaveAugmentationData = false;
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
cieInfo->addressesSignedWithBKey = false;
|
||||
cieInfo->mteTaggedFrame = false;
|
||||
#endif
|
||||
cieInfo->cieStart = cie;
|
||||
pint_t p = cie;
|
||||
pint_t cieLength = (pint_t)addressSpace.get32(p);
|
||||
p += 4;
|
||||
pint_t cieContentEnd = p + cieLength;
|
||||
if (cieLength == 0xffffffff) {
|
||||
// 0xffffffff means length is really next 8 bytes
|
||||
cieLength = (pint_t)addressSpace.get64(p);
|
||||
p += 8;
|
||||
cieContentEnd = p + cieLength;
|
||||
}
|
||||
if (cieLength == 0)
|
||||
return NULL;
|
||||
// CIE ID is always 0
|
||||
if (addressSpace.get32(p) != 0)
|
||||
return "CIE ID is not zero";
|
||||
p += 4;
|
||||
// Version is always 1 or 3
|
||||
uint8_t version = addressSpace.get8(p);
|
||||
if ((version != 1) && (version != 3))
|
||||
return "CIE version is not 1 or 3";
|
||||
++p;
|
||||
// save start of augmentation string and find end
|
||||
pint_t strStart = p;
|
||||
while (addressSpace.get8(p) != 0)
|
||||
++p;
|
||||
++p;
|
||||
// parse code alignment factor
|
||||
cieInfo->codeAlignFactor = (uint32_t)addressSpace.getULEB128(p, cieContentEnd);
|
||||
// parse data alignment factor
|
||||
cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd);
|
||||
// parse return address register
|
||||
uint64_t raReg = (version == 1) ? addressSpace.get8(p++)
|
||||
: addressSpace.getULEB128(p, cieContentEnd);
|
||||
assert(raReg < 255 && "return address register too large");
|
||||
cieInfo->returnAddressRegister = (uint8_t)raReg;
|
||||
// parse augmentation data based on augmentation string
|
||||
const char *result = NULL;
|
||||
if (addressSpace.get8(strStart) == 'z') {
|
||||
// parse augmentation data length
|
||||
addressSpace.getULEB128(p, cieContentEnd);
|
||||
for (pint_t s = strStart; addressSpace.get8(s) != '\0'; ++s) {
|
||||
switch (addressSpace.get8(s)) {
|
||||
case 'z':
|
||||
cieInfo->fdesHaveAugmentationData = true;
|
||||
break;
|
||||
case 'P':
|
||||
cieInfo->personalityEncoding = addressSpace.get8(p);
|
||||
++p;
|
||||
cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie);
|
||||
cieInfo->personality = addressSpace
|
||||
.getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding);
|
||||
break;
|
||||
case 'L':
|
||||
cieInfo->lsdaEncoding = addressSpace.get8(p);
|
||||
++p;
|
||||
break;
|
||||
case 'R':
|
||||
cieInfo->pointerEncoding = addressSpace.get8(p);
|
||||
++p;
|
||||
break;
|
||||
case 'S':
|
||||
cieInfo->isSignalFrame = true;
|
||||
break;
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
case 'B':
|
||||
cieInfo->addressesSignedWithBKey = true;
|
||||
break;
|
||||
case 'G':
|
||||
cieInfo->mteTaggedFrame = true;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
// ignore unknown letters
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cieInfo->cieLength = cieContentEnd - cieInfo->cieStart;
|
||||
cieInfo->cieInstructions = p;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// "run" the DWARF instructions and create the abstract PrologInfo for an FDE
|
||||
template <typename A>
|
||||
bool CFI_Parser<A>::parseFDEInstructions(A &addressSpace,
|
||||
const FDE_Info &fdeInfo,
|
||||
const CIE_Info &cieInfo, pint_t upToPC,
|
||||
int arch, PrologInfo *results) {
|
||||
// Alloca is used for the allocation of the rememberStack entries. It removes
|
||||
// the dependency on new/malloc but the below for loop can not be refactored
|
||||
// into functions. Entry could be saved during the processing of a CIE and
|
||||
// restored by an FDE.
|
||||
RememberStack rememberStack;
|
||||
|
||||
struct ParseInfo {
|
||||
pint_t instructions;
|
||||
pint_t instructionsEnd;
|
||||
pint_t pcoffset;
|
||||
};
|
||||
|
||||
ParseInfo parseInfoArray[] = {
|
||||
{cieInfo.cieInstructions, cieInfo.cieStart + cieInfo.cieLength,
|
||||
(pint_t)(-1)},
|
||||
{fdeInfo.fdeInstructions, fdeInfo.fdeStart + fdeInfo.fdeLength,
|
||||
upToPC - fdeInfo.pcStart}};
|
||||
|
||||
for (const auto &info : parseInfoArray) {
|
||||
pint_t p = info.instructions;
|
||||
pint_t instructionsEnd = info.instructionsEnd;
|
||||
pint_t pcoffset = info.pcoffset;
|
||||
pint_t codeOffset = 0;
|
||||
|
||||
// initialState initialized as registers in results are modified. Use
|
||||
// PrologInfo accessor functions to avoid reading uninitialized data.
|
||||
PrologInfo initialState(PrologInfo::InitializeTime::kLazy);
|
||||
|
||||
_LIBUNWIND_TRACE_DWARF("parseFDEInstructions(instructions=0x%0" PRIx64
|
||||
")\n",
|
||||
static_cast<uint64_t>(instructionsEnd));
|
||||
|
||||
// see DWARF Spec, section 6.4.2 for details on unwind opcodes
|
||||
while ((p < instructionsEnd) && (codeOffset < pcoffset)) {
|
||||
uint64_t reg;
|
||||
uint64_t reg2;
|
||||
int64_t offset;
|
||||
uint64_t length;
|
||||
uint8_t opcode = addressSpace.get8(p);
|
||||
uint8_t operand;
|
||||
|
||||
++p;
|
||||
switch (opcode) {
|
||||
case DW_CFA_nop:
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_nop\n");
|
||||
break;
|
||||
case DW_CFA_set_loc:
|
||||
codeOffset = addressSpace.getEncodedP(p, instructionsEnd,
|
||||
cieInfo.pointerEncoding);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_set_loc\n");
|
||||
break;
|
||||
case DW_CFA_advance_loc1:
|
||||
codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor);
|
||||
p += 1;
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc1: new offset=%" PRIu64 "\n",
|
||||
static_cast<uint64_t>(codeOffset));
|
||||
break;
|
||||
case DW_CFA_advance_loc2:
|
||||
codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor);
|
||||
p += 2;
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc2: new offset=%" PRIu64 "\n",
|
||||
static_cast<uint64_t>(codeOffset));
|
||||
break;
|
||||
case DW_CFA_advance_loc4:
|
||||
codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor);
|
||||
p += 4;
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc4: new offset=%" PRIu64 "\n",
|
||||
static_cast<uint64_t>(codeOffset));
|
||||
break;
|
||||
case DW_CFA_offset_extended:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) *
|
||||
cieInfo.dataAlignFactor;
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_offset_extended DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
results->setRegister(reg, kRegisterInCFA, offset, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", "
|
||||
"offset=%" PRId64 ")\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_restore_extended:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_restore_extended DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
results->restoreRegisterToInitialState(reg, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n",
|
||||
reg);
|
||||
break;
|
||||
case DW_CFA_undefined:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_undefined DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
results->setRegisterLocation(reg, kRegisterUndefined, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n", reg);
|
||||
break;
|
||||
case DW_CFA_same_value:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_same_value DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
// <rdar://problem/8456377> DW_CFA_same_value unsupported
|
||||
// "same value" means register was stored in frame, but its current
|
||||
// value has not changed, so no need to restore from frame.
|
||||
// We model this as if the register was never saved.
|
||||
results->setRegisterLocation(reg, kRegisterUnused, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n", reg);
|
||||
break;
|
||||
case DW_CFA_register:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
reg2 = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_register DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
if (reg2 > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_register DWARF unwind, reg2 too big");
|
||||
return false;
|
||||
}
|
||||
results->setRegister(reg, kRegisterInRegister, (int64_t)reg2,
|
||||
initialState);
|
||||
_LIBUNWIND_TRACE_DWARF(
|
||||
"DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n", reg, reg2);
|
||||
break;
|
||||
case DW_CFA_remember_state: {
|
||||
// Avoid operator new because that would be an upward dependency.
|
||||
// Avoid malloc because it needs heap allocation.
|
||||
PrologInfoStackEntry *entry =
|
||||
(PrologInfoStackEntry *)_LIBUNWIND_REMEMBER_ALLOC(
|
||||
sizeof(PrologInfoStackEntry));
|
||||
if (entry != NULL) {
|
||||
entry->next = rememberStack.entry;
|
||||
entry->info = *results;
|
||||
rememberStack.entry = entry;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_remember_state\n");
|
||||
break;
|
||||
}
|
||||
case DW_CFA_restore_state:
|
||||
if (rememberStack.entry != NULL) {
|
||||
PrologInfoStackEntry *top = rememberStack.entry;
|
||||
*results = top->info;
|
||||
rememberStack.entry = top->next;
|
||||
_LIBUNWIND_REMEMBER_FREE(top);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n");
|
||||
break;
|
||||
case DW_CFA_def_cfa:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0("malformed DW_CFA_def_cfa DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
results->cfaRegister = (uint32_t)reg;
|
||||
results->cfaRegisterOffset = (int32_t)offset;
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64
|
||||
")\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_def_cfa_register:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_def_cfa_register DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
results->cfaRegister = (uint32_t)reg;
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg);
|
||||
break;
|
||||
case DW_CFA_def_cfa_offset:
|
||||
results->cfaRegisterOffset =
|
||||
(int32_t)addressSpace.getULEB128(p, instructionsEnd);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset(%d)\n",
|
||||
results->cfaRegisterOffset);
|
||||
break;
|
||||
case DW_CFA_def_cfa_expression:
|
||||
results->cfaRegister = 0;
|
||||
results->cfaExpression = (int64_t)p;
|
||||
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||
assert(length < static_cast<pint_t>(~0) && "pointer overflow");
|
||||
p += static_cast<pint_t>(length);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_expression(expression=0x%" PRIx64
|
||||
", length=%" PRIu64 ")\n",
|
||||
results->cfaExpression, length);
|
||||
break;
|
||||
case DW_CFA_expression:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_expression DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
results->setRegister(reg, kRegisterAtExpression, (int64_t)p,
|
||||
initialState);
|
||||
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||
assert(length < static_cast<pint_t>(~0) && "pointer overflow");
|
||||
p += static_cast<pint_t>(length);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_expression(reg=%" PRIu64 ", "
|
||||
"expression=0x%" PRIx64 ", "
|
||||
"length=%" PRIu64 ")\n",
|
||||
reg, results->savedRegisters[reg].value, length);
|
||||
break;
|
||||
case DW_CFA_offset_extended_sf:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_offset_extended_sf DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
offset = addressSpace.getSLEB128(p, instructionsEnd) *
|
||||
cieInfo.dataAlignFactor;
|
||||
results->setRegister(reg, kRegisterInCFA, offset, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", "
|
||||
"offset=%" PRId64 ")\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_def_cfa_sf:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
offset = addressSpace.getSLEB128(p, instructionsEnd) *
|
||||
cieInfo.dataAlignFactor;
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_def_cfa_sf DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
results->cfaRegister = (uint32_t)reg;
|
||||
results->cfaRegisterOffset = (int32_t)offset;
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_sf(reg=%" PRIu64 ", "
|
||||
"offset=%" PRId64 ")\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_def_cfa_offset_sf:
|
||||
results->cfaRegisterOffset =
|
||||
(int32_t)(addressSpace.getSLEB128(p, instructionsEnd) *
|
||||
cieInfo.dataAlignFactor);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset_sf(%d)\n",
|
||||
results->cfaRegisterOffset);
|
||||
break;
|
||||
case DW_CFA_val_offset:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG(
|
||||
"malformed DW_CFA_val_offset DWARF unwind, reg (%" PRIu64
|
||||
") out of range\n",
|
||||
reg);
|
||||
return false;
|
||||
}
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) *
|
||||
cieInfo.dataAlignFactor;
|
||||
results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", "
|
||||
"offset=%" PRId64 "\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_val_offset_sf:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_val_offset_sf DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
offset = addressSpace.getSLEB128(p, instructionsEnd) *
|
||||
cieInfo.dataAlignFactor;
|
||||
results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", "
|
||||
"offset=%" PRId64 "\n",
|
||||
reg, offset);
|
||||
break;
|
||||
case DW_CFA_val_expression:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0(
|
||||
"malformed DW_CFA_val_expression DWARF unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
results->setRegister(reg, kRegisterIsExpression, (int64_t)p,
|
||||
initialState);
|
||||
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||
assert(length < static_cast<pint_t>(~0) && "pointer overflow");
|
||||
p += static_cast<pint_t>(length);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_val_expression(reg=%" PRIu64 ", "
|
||||
"expression=0x%" PRIx64 ", length=%" PRIu64
|
||||
")\n",
|
||||
reg, results->savedRegisters[reg].value, length);
|
||||
break;
|
||||
case DW_CFA_GNU_args_size:
|
||||
length = addressSpace.getULEB128(p, instructionsEnd);
|
||||
results->spExtraArgSize = (uint32_t)length;
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_args_size(%" PRIu64 ")\n", length);
|
||||
break;
|
||||
case DW_CFA_GNU_negative_offset_extended:
|
||||
reg = addressSpace.getULEB128(p, instructionsEnd);
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG0("malformed DW_CFA_GNU_negative_offset_extended DWARF "
|
||||
"unwind, reg too big");
|
||||
return false;
|
||||
}
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) *
|
||||
cieInfo.dataAlignFactor;
|
||||
results->setRegister(reg, kRegisterInCFA, -offset, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF(
|
||||
"DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset);
|
||||
break;
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC) || \
|
||||
defined(_LIBUNWIND_TARGET_SPARC64)
|
||||
// The same constant is used to represent different instructions on
|
||||
// AArch64 (negate_ra_state) and SPARC (window_save).
|
||||
static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save,
|
||||
"uses the same constant");
|
||||
case DW_CFA_AARCH64_negate_ra_state:
|
||||
switch (arch) {
|
||||
#if defined(_LIBUNWIND_TARGET_AARCH64)
|
||||
case REGISTERS_ARM64: {
|
||||
int64_t value =
|
||||
results->savedRegisters[UNW_AARCH64_RA_SIGN_STATE].value ^ 0x1;
|
||||
results->setRegisterValue(UNW_AARCH64_RA_SIGN_STATE, value,
|
||||
initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n");
|
||||
} break;
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_SPARC)
|
||||
// case DW_CFA_GNU_window_save:
|
||||
case REGISTERS_SPARC:
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n");
|
||||
for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) {
|
||||
results->setRegister(reg, kRegisterInRegister,
|
||||
((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0,
|
||||
initialState);
|
||||
}
|
||||
|
||||
for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) {
|
||||
results->setRegister(reg, kRegisterInCFA,
|
||||
((int64_t)reg - UNW_SPARC_L0) * 4,
|
||||
initialState);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_TARGET_SPARC64)
|
||||
// case DW_CFA_GNU_window_save:
|
||||
case REGISTERS_SPARC64:
|
||||
// Don't save %o0-%o7 on sparc64.
|
||||
// https://reviews.llvm.org/D32450#736405
|
||||
|
||||
for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) {
|
||||
if (reg == UNW_SPARC_I7)
|
||||
results->setRegister(
|
||||
reg, kRegisterInCFADecrypt,
|
||||
static_cast<int64_t>((reg - UNW_SPARC_L0) * sizeof(pint_t)),
|
||||
initialState);
|
||||
else
|
||||
results->setRegister(
|
||||
reg, kRegisterInCFA,
|
||||
static_cast<int64_t>((reg - UNW_SPARC_L0) * sizeof(pint_t)),
|
||||
initialState);
|
||||
}
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save\n");
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
#else
|
||||
(void)arch;
|
||||
#endif
|
||||
|
||||
default:
|
||||
operand = opcode & 0x3F;
|
||||
switch (opcode & 0xC0) {
|
||||
case DW_CFA_offset:
|
||||
reg = operand;
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG("malformed DW_CFA_offset DWARF unwind, reg (%" PRIu64
|
||||
") out of range",
|
||||
reg);
|
||||
return false;
|
||||
}
|
||||
offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) *
|
||||
cieInfo.dataAlignFactor;
|
||||
results->setRegister(reg, kRegisterInCFA, offset, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n",
|
||||
operand, offset);
|
||||
break;
|
||||
case DW_CFA_advance_loc:
|
||||
codeOffset += operand * cieInfo.codeAlignFactor;
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc: new offset=%" PRIu64 "\n",
|
||||
static_cast<uint64_t>(codeOffset));
|
||||
break;
|
||||
case DW_CFA_restore:
|
||||
reg = operand;
|
||||
if (reg > kMaxRegisterNumber) {
|
||||
_LIBUNWIND_LOG(
|
||||
"malformed DW_CFA_restore DWARF unwind, reg (%" PRIu64
|
||||
") out of range",
|
||||
reg);
|
||||
return false;
|
||||
}
|
||||
results->restoreRegisterToInitialState(reg, initialState);
|
||||
_LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n",
|
||||
static_cast<uint64_t>(operand));
|
||||
break;
|
||||
default:
|
||||
_LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
#endif // __DWARF_PARSER_HPP__
|
170
third_party/libunwind/EHHeaderParser.hpp
vendored
Normal file
170
third_party/libunwind/EHHeaderParser.hpp
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Parses ELF .eh_frame_hdr sections.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __EHHEADERPARSER_HPP__
|
||||
#define __EHHEADERPARSER_HPP__
|
||||
|
||||
#include "third_party/libunwind/include/libunwind.h"
|
||||
|
||||
#include "third_party/libunwind/DwarfParser.hpp"
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
/// \brief EHHeaderParser does basic parsing of an ELF .eh_frame_hdr section.
|
||||
///
|
||||
/// See DWARF spec for details:
|
||||
/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
|
||||
///
|
||||
template <typename A> class EHHeaderParser {
|
||||
public:
|
||||
typedef typename A::pint_t pint_t;
|
||||
|
||||
/// Information encoded in the EH frame header.
|
||||
struct EHHeaderInfo {
|
||||
pint_t eh_frame_ptr;
|
||||
size_t fde_count;
|
||||
pint_t table;
|
||||
uint8_t table_enc;
|
||||
};
|
||||
|
||||
static bool decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||
EHHeaderInfo &ehHdrInfo);
|
||||
static bool findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
|
||||
uint32_t sectionLength,
|
||||
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo);
|
||||
|
||||
private:
|
||||
static bool decodeTableEntry(A &addressSpace, pint_t &tableEntry,
|
||||
pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||
uint8_t tableEnc,
|
||||
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo);
|
||||
static size_t getTableEntrySize(uint8_t tableEnc);
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
bool EHHeaderParser<A>::decodeEHHdr(A &addressSpace, pint_t ehHdrStart,
|
||||
pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) {
|
||||
pint_t p = ehHdrStart;
|
||||
uint8_t version = addressSpace.get8(p++);
|
||||
if (version != 1) {
|
||||
_LIBUNWIND_LOG("unsupported .eh_frame_hdr version: %" PRIu8 " at %" PRIx64,
|
||||
version, static_cast<uint64_t>(ehHdrStart));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t eh_frame_ptr_enc = addressSpace.get8(p++);
|
||||
uint8_t fde_count_enc = addressSpace.get8(p++);
|
||||
ehHdrInfo.table_enc = addressSpace.get8(p++);
|
||||
|
||||
ehHdrInfo.eh_frame_ptr =
|
||||
addressSpace.getEncodedP(p, ehHdrEnd, eh_frame_ptr_enc, ehHdrStart);
|
||||
ehHdrInfo.fde_count =
|
||||
fde_count_enc == DW_EH_PE_omit
|
||||
? 0
|
||||
: addressSpace.getEncodedP(p, ehHdrEnd, fde_count_enc, ehHdrStart);
|
||||
ehHdrInfo.table = p;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
bool EHHeaderParser<A>::decodeTableEntry(
|
||||
A &addressSpace, pint_t &tableEntry, pint_t ehHdrStart, pint_t ehHdrEnd,
|
||||
uint8_t tableEnc, typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo) {
|
||||
// Have to decode the whole FDE for the PC range anyway, so just throw away
|
||||
// the PC start.
|
||||
addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart);
|
||||
pint_t fde =
|
||||
addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart);
|
||||
const char *message =
|
||||
CFI_Parser<A>::decodeFDE(addressSpace, fde, fdeInfo, cieInfo);
|
||||
if (message != NULL) {
|
||||
_LIBUNWIND_DEBUG_LOG("EHHeaderParser::decodeTableEntry: bad fde: %s",
|
||||
message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
bool EHHeaderParser<A>::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart,
|
||||
uint32_t sectionLength,
|
||||
typename CFI_Parser<A>::FDE_Info *fdeInfo,
|
||||
typename CFI_Parser<A>::CIE_Info *cieInfo) {
|
||||
pint_t ehHdrEnd = ehHdrStart + sectionLength;
|
||||
|
||||
EHHeaderParser<A>::EHHeaderInfo hdrInfo;
|
||||
if (!EHHeaderParser<A>::decodeEHHdr(addressSpace, ehHdrStart, ehHdrEnd,
|
||||
hdrInfo))
|
||||
return false;
|
||||
|
||||
if (hdrInfo.fde_count == 0) return false;
|
||||
|
||||
size_t tableEntrySize = getTableEntrySize(hdrInfo.table_enc);
|
||||
pint_t tableEntry;
|
||||
|
||||
size_t low = 0;
|
||||
for (size_t len = hdrInfo.fde_count; len > 1;) {
|
||||
size_t mid = low + (len / 2);
|
||||
tableEntry = hdrInfo.table + mid * tableEntrySize;
|
||||
pint_t start = addressSpace.getEncodedP(tableEntry, ehHdrEnd,
|
||||
hdrInfo.table_enc, ehHdrStart);
|
||||
|
||||
if (start == pc) {
|
||||
low = mid;
|
||||
break;
|
||||
} else if (start < pc) {
|
||||
low = mid;
|
||||
len -= (len / 2);
|
||||
} else {
|
||||
len /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
tableEntry = hdrInfo.table + low * tableEntrySize;
|
||||
if (decodeTableEntry(addressSpace, tableEntry, ehHdrStart, ehHdrEnd,
|
||||
hdrInfo.table_enc, fdeInfo, cieInfo)) {
|
||||
if (pc >= fdeInfo->pcStart && pc < fdeInfo->pcEnd)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
size_t EHHeaderParser<A>::getTableEntrySize(uint8_t tableEnc) {
|
||||
switch (tableEnc & 0x0f) {
|
||||
case DW_EH_PE_sdata2:
|
||||
case DW_EH_PE_udata2:
|
||||
return 4;
|
||||
case DW_EH_PE_sdata4:
|
||||
case DW_EH_PE_udata4:
|
||||
return 8;
|
||||
case DW_EH_PE_sdata8:
|
||||
case DW_EH_PE_udata8:
|
||||
return 16;
|
||||
case DW_EH_PE_sleb128:
|
||||
case DW_EH_PE_uleb128:
|
||||
_LIBUNWIND_ABORT("Can't binary search on variable length encoded data.");
|
||||
case DW_EH_PE_omit:
|
||||
return 0;
|
||||
default:
|
||||
_LIBUNWIND_ABORT("Unknown DWARF encoding for search table.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
149
third_party/libunwind/FrameHeaderCache.hpp
vendored
Normal file
149
third_party/libunwind/FrameHeaderCache.hpp
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
//===-FrameHeaderCache.hpp ------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// Cache the elf program headers necessary to unwind the stack more efficiently
|
||||
// in the presence of many dsos.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __FRAMEHEADER_CACHE_HPP__
|
||||
#define __FRAMEHEADER_CACHE_HPP__
|
||||
|
||||
#include "third_party/libunwind/config.h"
|
||||
#include "libc/isystem/limits.h"
|
||||
|
||||
#ifdef _LIBUNWIND_DEBUG_FRAMEHEADER_CACHE
|
||||
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x) _LIBUNWIND_LOG0(x)
|
||||
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...) \
|
||||
_LIBUNWIND_LOG(msg, __VA_ARGS__)
|
||||
#else
|
||||
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x)
|
||||
#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...)
|
||||
#endif
|
||||
|
||||
// This cache should only be be used from within a dl_iterate_phdr callback.
|
||||
// dl_iterate_phdr does the necessary synchronization to prevent problems
|
||||
// with concurrent access via the libc load lock. Adding synchronization
|
||||
// for other uses is possible, but not currently done.
|
||||
|
||||
class _LIBUNWIND_HIDDEN FrameHeaderCache {
|
||||
struct CacheEntry {
|
||||
uintptr_t LowPC() { return Info.dso_base; };
|
||||
uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; };
|
||||
UnwindInfoSections Info;
|
||||
CacheEntry *Next;
|
||||
};
|
||||
|
||||
static const size_t kCacheEntryCount = 8;
|
||||
|
||||
// Can't depend on the C++ standard library in libunwind, so use an array to
|
||||
// allocate the entries, and two linked lists for ordering unused and recently
|
||||
// used entries. FIXME: Would the the extra memory for a doubly-linked list
|
||||
// be better than the runtime cost of traversing a very short singly-linked
|
||||
// list on a cache miss? The entries themselves are all small and consecutive,
|
||||
// so unlikely to cause page faults when following the pointers. The memory
|
||||
// spent on additional pointers could also be spent on more entries.
|
||||
|
||||
CacheEntry Entries[kCacheEntryCount];
|
||||
CacheEntry *MostRecentlyUsed;
|
||||
CacheEntry *Unused;
|
||||
|
||||
void resetCache() {
|
||||
_LIBUNWIND_FRAMEHEADERCACHE_TRACE0("FrameHeaderCache reset");
|
||||
MostRecentlyUsed = nullptr;
|
||||
Unused = &Entries[0];
|
||||
for (size_t i = 0; i < kCacheEntryCount - 1; i++) {
|
||||
Entries[i].Next = &Entries[i + 1];
|
||||
}
|
||||
Entries[kCacheEntryCount - 1].Next = nullptr;
|
||||
}
|
||||
|
||||
bool cacheNeedsReset(dl_phdr_info *PInfo) {
|
||||
// C libraries increment dl_phdr_info.adds and dl_phdr_info.subs when
|
||||
// loading and unloading shared libraries. If these values change between
|
||||
// iterations of dl_iterate_phdr, then invalidate the cache.
|
||||
|
||||
// These are static to avoid needing an initializer, and unsigned long long
|
||||
// because that is their type within the extended dl_phdr_info. Initialize
|
||||
// these to something extremely unlikely to be found upon the first call to
|
||||
// dl_iterate_phdr.
|
||||
static unsigned long long LastAdds = ULLONG_MAX;
|
||||
static unsigned long long LastSubs = ULLONG_MAX;
|
||||
if (PInfo->dlpi_adds != LastAdds || PInfo->dlpi_subs != LastSubs) {
|
||||
// Resetting the entire cache is a big hammer, but this path is rare--
|
||||
// usually just on the very first call, when the cache is empty anyway--so
|
||||
// added complexity doesn't buy much.
|
||||
LastAdds = PInfo->dlpi_adds;
|
||||
LastSubs = PInfo->dlpi_subs;
|
||||
resetCache();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
bool find(dl_phdr_info *PInfo, size_t, void *data) {
|
||||
if (cacheNeedsReset(PInfo) || MostRecentlyUsed == nullptr)
|
||||
return false;
|
||||
|
||||
auto *CBData = static_cast<dl_iterate_cb_data *>(data);
|
||||
CacheEntry *Current = MostRecentlyUsed;
|
||||
CacheEntry *Previous = nullptr;
|
||||
while (Current != nullptr) {
|
||||
_LIBUNWIND_FRAMEHEADERCACHE_TRACE(
|
||||
"FrameHeaderCache check %lx in [%lx - %lx)", CBData->targetAddr,
|
||||
Current->LowPC(), Current->HighPC());
|
||||
if (Current->LowPC() <= CBData->targetAddr &&
|
||||
CBData->targetAddr < Current->HighPC()) {
|
||||
_LIBUNWIND_FRAMEHEADERCACHE_TRACE(
|
||||
"FrameHeaderCache hit %lx in [%lx - %lx)", CBData->targetAddr,
|
||||
Current->LowPC(), Current->HighPC());
|
||||
if (Previous) {
|
||||
// If there is no Previous, then Current is already the
|
||||
// MostRecentlyUsed, and no need to move it up.
|
||||
Previous->Next = Current->Next;
|
||||
Current->Next = MostRecentlyUsed;
|
||||
MostRecentlyUsed = Current;
|
||||
}
|
||||
*CBData->sects = Current->Info;
|
||||
return true;
|
||||
}
|
||||
Previous = Current;
|
||||
Current = Current->Next;
|
||||
}
|
||||
_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache miss for address %lx",
|
||||
CBData->targetAddr);
|
||||
return false;
|
||||
}
|
||||
|
||||
void add(const UnwindInfoSections *UIS) {
|
||||
CacheEntry *Current = nullptr;
|
||||
|
||||
if (Unused != nullptr) {
|
||||
Current = Unused;
|
||||
Unused = Unused->Next;
|
||||
} else {
|
||||
Current = MostRecentlyUsed;
|
||||
CacheEntry *Previous = nullptr;
|
||||
while (Current->Next != nullptr) {
|
||||
Previous = Current;
|
||||
Current = Current->Next;
|
||||
}
|
||||
Previous->Next = nullptr;
|
||||
_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache evict [%lx - %lx)",
|
||||
Current->LowPC(), Current->HighPC());
|
||||
}
|
||||
|
||||
Current->Info = *UIS;
|
||||
Current->Next = MostRecentlyUsed;
|
||||
MostRecentlyUsed = Current;
|
||||
_LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache add [%lx - %lx)",
|
||||
MostRecentlyUsed->LowPC(),
|
||||
MostRecentlyUsed->HighPC());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __FRAMEHEADER_CACHE_HPP__
|
311
third_party/libunwind/LICENSE.TXT
vendored
Normal file
311
third_party/libunwind/LICENSE.TXT
vendored
Normal file
|
@ -0,0 +1,311 @@
|
|||
==============================================================================
|
||||
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
|
||||
==============================================================================
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
---- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
|
||||
==============================================================================
|
||||
Software from third parties included in the LLVM Project:
|
||||
==============================================================================
|
||||
The LLVM Project contains third party software which is under different license
|
||||
terms. All such code will be identified clearly using at least one of two
|
||||
mechanisms:
|
||||
1) It will be in a separate directory tree with its own `LICENSE.txt` or
|
||||
`LICENSE` file at the top containing the specific license and restrictions
|
||||
which apply to that software, or
|
||||
2) It will contain specific license and restriction terms at the top of every
|
||||
file.
|
||||
|
||||
==============================================================================
|
||||
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
|
||||
==============================================================================
|
||||
|
||||
The libunwind library is dual licensed under both the University of Illinois
|
||||
"BSD-Like" license and the MIT license. As a user of this code you may choose
|
||||
to use it under either license. As a contributor, you agree to allow your code
|
||||
to be used under both.
|
||||
|
||||
Full text of the relevant licenses is included below.
|
||||
|
||||
==============================================================================
|
||||
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
University of Illinois at Urbana-Champaign
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois at
|
||||
Urbana-Champaign, nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this Software without specific
|
||||
prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
13
third_party/libunwind/README.cosmo
vendored
Normal file
13
third_party/libunwind/README.cosmo
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
DESCRIPTION
|
||||
|
||||
libunwind - LLVM Unwinder
|
||||
|
||||
ORIGIN
|
||||
|
||||
Obtained from the LLVM monorepo, release 17.0.6.
|
||||
|
||||
https://github.com/llvm/llvm-project/tree/llvmorg-17.0.6/libunwind
|
||||
commit 6009708b4367171ccdbf4b5905cb6a803753fe18
|
||||
Author: Tobias Hieta <tobias@hieta.se>
|
||||
Date: Tue, 28 Nov 2023 09:52:28 +0100
|
||||
|
114
third_party/libunwind/RWMutex.hpp
vendored
Normal file
114
third_party/libunwind/RWMutex.hpp
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Abstract interface to shared reader/writer log, hiding platform and
|
||||
// configuration differences.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __RWMUTEX_HPP__
|
||||
#define __RWMUTEX_HPP__
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#elif !defined(_LIBUNWIND_HAS_NO_THREADS)
|
||||
#include "libc/isystem/pthread.h"
|
||||
#if defined(__ELF__) && defined(_LIBUNWIND_LINK_PTHREAD_LIB)
|
||||
#pragma comment(lib, "pthread")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
#if defined(_LIBUNWIND_HAS_NO_THREADS)
|
||||
|
||||
class _LIBUNWIND_HIDDEN RWMutex {
|
||||
public:
|
||||
bool lock_shared() { return true; }
|
||||
bool unlock_shared() { return true; }
|
||||
bool lock() { return true; }
|
||||
bool unlock() { return true; }
|
||||
};
|
||||
|
||||
#elif defined(_WIN32)
|
||||
|
||||
class _LIBUNWIND_HIDDEN RWMutex {
|
||||
public:
|
||||
bool lock_shared() {
|
||||
AcquireSRWLockShared(&_lock);
|
||||
return true;
|
||||
}
|
||||
bool unlock_shared() {
|
||||
ReleaseSRWLockShared(&_lock);
|
||||
return true;
|
||||
}
|
||||
bool lock() {
|
||||
AcquireSRWLockExclusive(&_lock);
|
||||
return true;
|
||||
}
|
||||
bool unlock() {
|
||||
ReleaseSRWLockExclusive(&_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
SRWLOCK _lock = SRWLOCK_INIT;
|
||||
};
|
||||
|
||||
#elif !defined(LIBUNWIND_USE_WEAK_PTHREAD)
|
||||
|
||||
class _LIBUNWIND_HIDDEN RWMutex {
|
||||
public:
|
||||
bool lock_shared() { return pthread_rwlock_rdlock(&_lock) == 0; }
|
||||
bool unlock_shared() { return pthread_rwlock_unlock(&_lock) == 0; }
|
||||
bool lock() { return pthread_rwlock_wrlock(&_lock) == 0; }
|
||||
bool unlock() { return pthread_rwlock_unlock(&_lock) == 0; }
|
||||
|
||||
private:
|
||||
pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
extern "C" int __attribute__((weak))
|
||||
pthread_create(pthread_t *thread, const pthread_attr_t *attr,
|
||||
void *(*start_routine)(void *), void *arg);
|
||||
extern "C" int __attribute__((weak))
|
||||
pthread_rwlock_rdlock(pthread_rwlock_t *lock);
|
||||
extern "C" int __attribute__((weak))
|
||||
pthread_rwlock_wrlock(pthread_rwlock_t *lock);
|
||||
extern "C" int __attribute__((weak))
|
||||
pthread_rwlock_unlock(pthread_rwlock_t *lock);
|
||||
|
||||
// Calls to the locking functions are gated on pthread_create, and not the
|
||||
// functions themselves, because the data structure should only be locked if
|
||||
// another thread has been created. This is what similar libraries do.
|
||||
|
||||
class _LIBUNWIND_HIDDEN RWMutex {
|
||||
public:
|
||||
bool lock_shared() {
|
||||
return !pthread_create || (pthread_rwlock_rdlock(&_lock) == 0);
|
||||
}
|
||||
bool unlock_shared() {
|
||||
return !pthread_create || (pthread_rwlock_unlock(&_lock) == 0);
|
||||
}
|
||||
bool lock() {
|
||||
return !pthread_create || (pthread_rwlock_wrlock(&_lock) == 0);
|
||||
}
|
||||
bool unlock() {
|
||||
return !pthread_create || (pthread_rwlock_unlock(&_lock) == 0);
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
#endif // __RWMUTEX_HPP__
|
5312
third_party/libunwind/Registers.hpp
vendored
Normal file
5312
third_party/libunwind/Registers.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
50
third_party/libunwind/Unwind-EHABI.h
vendored
Normal file
50
third_party/libunwind/Unwind-EHABI.h
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __UNWIND_EHABI_H__
|
||||
#define __UNWIND_EHABI_H__
|
||||
|
||||
#include "third_party/libunwind/include/__libunwind_config.h"
|
||||
|
||||
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "third_party/libunwind/include/unwind.h"
|
||||
|
||||
// Unable to unwind in the ARM index table (section 5 EHABI).
|
||||
#define UNW_EXIDX_CANTUNWIND 0x1
|
||||
|
||||
static inline uint32_t signExtendPrel31(uint32_t data) {
|
||||
return data | ((data & 0x40000000u) << 1);
|
||||
}
|
||||
|
||||
static inline uint32_t readPrel31(const uint32_t *data) {
|
||||
return (((uint32_t)(uintptr_t)data) + signExtendPrel31(*data));
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr0(
|
||||
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
|
||||
|
||||
extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr1(
|
||||
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
|
||||
|
||||
extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr2(
|
||||
_Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // defined(_LIBUNWIND_ARM_EHABI)
|
||||
|
||||
#endif // __UNWIND_EHABI_H__
|
528
third_party/libunwind/Unwind-sjlj.c
vendored
Normal file
528
third_party/libunwind/Unwind-sjlj.c
vendored
Normal file
|
@ -0,0 +1,528 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Implements setjump-longjump based C++ exceptions
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "third_party/libunwind/include/unwind.h"
|
||||
|
||||
#include "libc/isystem/inttypes.h"
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "libc/isystem/stdbool.h"
|
||||
#include "libc/isystem/stdlib.h"
|
||||
|
||||
#include "third_party/libunwind/config.h"
|
||||
|
||||
/// With SJLJ based exceptions, any function that has a catch clause or needs to
|
||||
/// do any clean up when an exception propagates through it, needs to call
|
||||
/// \c _Unwind_SjLj_Register at the start of the function and
|
||||
/// \c _Unwind_SjLj_Unregister at the end. The register function is called with
|
||||
/// the address of a block of memory in the function's stack frame. The runtime
|
||||
/// keeps a linked list (stack) of these blocks - one per thread. The calling
|
||||
/// function also sets the personality and lsda fields of the block.
|
||||
|
||||
#if defined(_LIBUNWIND_BUILD_SJLJ_APIS)
|
||||
|
||||
struct _Unwind_FunctionContext {
|
||||
// next function in stack of handlers
|
||||
struct _Unwind_FunctionContext *prev;
|
||||
|
||||
#if defined(__ve__)
|
||||
// VE requires to store 64 bit pointers in the buffer for SjLj exception.
|
||||
// We expand the size of values defined here. This size must be matched
|
||||
// to the size returned by TargetMachine::getSjLjDataSize().
|
||||
|
||||
// set by calling function before registering to be the landing pad
|
||||
uint64_t resumeLocation;
|
||||
|
||||
// set by personality handler to be parameters passed to landing pad function
|
||||
uint64_t resumeParameters[4];
|
||||
#else
|
||||
// set by calling function before registering to be the landing pad
|
||||
uint32_t resumeLocation;
|
||||
|
||||
// set by personality handler to be parameters passed to landing pad function
|
||||
uint32_t resumeParameters[4];
|
||||
#endif
|
||||
|
||||
// set by calling function before registering
|
||||
_Unwind_Personality_Fn personality; // arm offset=24
|
||||
uintptr_t lsda; // arm offset=28
|
||||
|
||||
// variable length array, contains registers to restore
|
||||
// 0 = r7, 1 = pc, 2 = sp
|
||||
void *jbuf[];
|
||||
};
|
||||
|
||||
#if defined(_LIBUNWIND_HAS_NO_THREADS)
|
||||
# define _LIBUNWIND_THREAD_LOCAL
|
||||
#else
|
||||
# if __STDC_VERSION__ >= 201112L
|
||||
# define _LIBUNWIND_THREAD_LOCAL _Thread_local
|
||||
# elif defined(_MSC_VER)
|
||||
# define _LIBUNWIND_THREAD_LOCAL __declspec(thread)
|
||||
# elif defined(__GNUC__) || defined(__clang__)
|
||||
# define _LIBUNWIND_THREAD_LOCAL __thread
|
||||
# else
|
||||
# error Unable to create thread local storage
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(FOR_DYLD)
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <System/pthread_machdep.h>
|
||||
#else
|
||||
static _LIBUNWIND_THREAD_LOCAL struct _Unwind_FunctionContext *stack = NULL;
|
||||
#endif
|
||||
|
||||
static struct _Unwind_FunctionContext *__Unwind_SjLj_GetTopOfFunctionStack() {
|
||||
#if defined(__APPLE__)
|
||||
return _pthread_getspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key);
|
||||
#else
|
||||
return stack;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc) {
|
||||
#if defined(__APPLE__)
|
||||
_pthread_setspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key, fc);
|
||||
#else
|
||||
stack = fc;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// Called at start of each function that catches exceptions
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_SjLj_Register(struct _Unwind_FunctionContext *fc) {
|
||||
fc->prev = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(fc);
|
||||
}
|
||||
|
||||
|
||||
/// Called at end of each function that catches exceptions
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_SjLj_Unregister(struct _Unwind_FunctionContext *fc) {
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(fc->prev);
|
||||
}
|
||||
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase1(struct _Unwind_Exception *exception_object) {
|
||||
_Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1: initial function-context=%p",
|
||||
(void *)c);
|
||||
|
||||
// walk each frame looking for a place to stop
|
||||
for (bool handlerNotFound = true; handlerNotFound; c = c->prev) {
|
||||
|
||||
// check for no more frames
|
||||
if (c == NULL) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): reached "
|
||||
"bottom => _URC_END_OF_STACK",
|
||||
(void *)exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1: function-context=%p", (void *)c);
|
||||
// if there is a personality routine, ask it if it will want to stop at this
|
||||
// frame
|
||||
if (c->personality != NULL) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): calling "
|
||||
"personality function %p",
|
||||
(void *)exception_object,
|
||||
(void *)c->personality);
|
||||
_Unwind_Reason_Code personalityResult = (*c->personality)(
|
||||
1, _UA_SEARCH_PHASE, exception_object->exception_class,
|
||||
exception_object, (struct _Unwind_Context *)c);
|
||||
switch (personalityResult) {
|
||||
case _URC_HANDLER_FOUND:
|
||||
// found a catch clause or locals that need destructing in this frame
|
||||
// stop search and remember function context
|
||||
handlerNotFound = false;
|
||||
exception_object->private_2 = (uintptr_t) c;
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): "
|
||||
"_URC_HANDLER_FOUND",
|
||||
(void *)exception_object);
|
||||
return _URC_NO_REASON;
|
||||
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): "
|
||||
"_URC_CONTINUE_UNWIND",
|
||||
(void *)exception_object);
|
||||
// continue unwinding
|
||||
break;
|
||||
|
||||
default:
|
||||
// something went wrong
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE1_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase2(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)",
|
||||
(void *)exception_object);
|
||||
|
||||
// walk each frame until we reach where search phase said to stop
|
||||
_Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||
while (true) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2s(ex_ojb=%p): context=%p",
|
||||
(void *)exception_object, (void *)c);
|
||||
|
||||
// check for no more frames
|
||||
if (c == NULL) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_ojb=%p): __unw_step() reached "
|
||||
"bottom => _URC_END_OF_STACK",
|
||||
(void *)exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
// if there is a personality routine, tell it we are unwinding
|
||||
if (c->personality != NULL) {
|
||||
_Unwind_Action action = _UA_CLEANUP_PHASE;
|
||||
if ((uintptr_t) c == exception_object->private_2)
|
||||
action = (_Unwind_Action)(
|
||||
_UA_CLEANUP_PHASE |
|
||||
_UA_HANDLER_FRAME); // tell personality this was the frame it marked
|
||||
// in phase 1
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*c->personality)(1, action, exception_object->exception_class,
|
||||
exception_object, (struct _Unwind_Context *)c);
|
||||
switch (personalityResult) {
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
// continue unwinding
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND",
|
||||
(void *)exception_object);
|
||||
if ((uintptr_t) c == exception_object->private_2) {
|
||||
// phase 1 said we would stop at this frame, but we did not...
|
||||
_LIBUNWIND_ABORT("during phase1 personality function said it would "
|
||||
"stop here, but now if phase2 it did not stop here");
|
||||
}
|
||||
break;
|
||||
case _URC_INSTALL_CONTEXT:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): "
|
||||
"_URC_INSTALL_CONTEXT, will resume at "
|
||||
"landing pad %p",
|
||||
(void *)exception_object, c->jbuf[1]);
|
||||
// personality routine says to transfer control to landing pad
|
||||
// we may get control back if landing pad calls _Unwind_Resume()
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(c);
|
||||
__builtin_longjmp(c->jbuf, 1);
|
||||
// __unw_resume() only returns if there was an error
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
default:
|
||||
// something went wrong
|
||||
_LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d",
|
||||
personalityResult);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
}
|
||||
c = c->prev;
|
||||
}
|
||||
|
||||
// clean up phase did not resume at the frame that the search phase said it
|
||||
// would
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase2_forced(struct _Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||
// walk each frame until we reach where search phase said to stop
|
||||
_Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack();
|
||||
while (true) {
|
||||
|
||||
// get next frame (skip over first which is _Unwind_RaiseException)
|
||||
if (c == NULL) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_ojb=%p): __unw_step() reached "
|
||||
"bottom => _URC_END_OF_STACK",
|
||||
(void *)exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
// call stop function at each frame
|
||||
_Unwind_Action action =
|
||||
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
|
||||
_Unwind_Reason_Code stopResult =
|
||||
(*stop)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)c, stop_parameter);
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"stop function returned %d",
|
||||
(void *)exception_object, stopResult);
|
||||
if (stopResult != _URC_NO_REASON) {
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"stopped by stop function",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
// if there is a personality routine, tell it we are unwinding
|
||||
if (c->personality != NULL) {
|
||||
_Unwind_Personality_Fn p = (_Unwind_Personality_Fn)c->personality;
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"calling personality function %p",
|
||||
(void *)exception_object, (void *)p);
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)c);
|
||||
switch (personalityResult) {
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"personality returned _URC_CONTINUE_UNWIND",
|
||||
(void *)exception_object);
|
||||
// destructors called, continue unwinding
|
||||
break;
|
||||
case _URC_INSTALL_CONTEXT:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"personality returned _URC_INSTALL_CONTEXT",
|
||||
(void *)exception_object);
|
||||
// we may get control back if landing pad calls _Unwind_Resume()
|
||||
__Unwind_SjLj_SetTopOfFunctionStack(c);
|
||||
__builtin_longjmp(c->jbuf, 1);
|
||||
break;
|
||||
default:
|
||||
// something went wrong
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
|
||||
"personality returned %d, "
|
||||
"_URC_FATAL_PHASE2_ERROR",
|
||||
(void *)exception_object, personalityResult);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
}
|
||||
c = c->prev;
|
||||
}
|
||||
|
||||
// call stop function one last time and tell it we've reached the end of the
|
||||
// stack
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "
|
||||
"function with _UA_END_OF_STACK",
|
||||
(void *)exception_object);
|
||||
_Unwind_Action lastAction =
|
||||
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
|
||||
(*stop)(1, lastAction, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)c, stop_parameter);
|
||||
|
||||
// clean up phase did not resume at the frame that the search phase said it
|
||||
// would
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/// Called by __cxa_throw. Only returns if there is a fatal error
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_SjLj_RaiseException(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SjLj_RaiseException(ex_obj=%p)",
|
||||
(void *)exception_object);
|
||||
|
||||
// mark that this is a non-forced unwind, so _Unwind_Resume() can do the right
|
||||
// thing
|
||||
exception_object->private_1 = 0;
|
||||
exception_object->private_2 = 0;
|
||||
|
||||
// phase 1: the search phase
|
||||
_Unwind_Reason_Code phase1 = unwind_phase1(exception_object);
|
||||
if (phase1 != _URC_NO_REASON)
|
||||
return phase1;
|
||||
|
||||
// phase 2: the clean up phase
|
||||
return unwind_phase2(exception_object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// When _Unwind_RaiseException() is in phase2, it hands control
|
||||
/// to the personality function at each frame. The personality
|
||||
/// may force a jump to a landing pad in that function, the landing
|
||||
/// pad code may then call _Unwind_Resume() to continue with the
|
||||
/// unwinding. Note: the call to _Unwind_Resume() is from compiler
|
||||
/// generated user code. All other _Unwind_* routines are called
|
||||
/// by the C++ runtime __cxa_* routines.
|
||||
///
|
||||
/// Re-throwing an exception is implemented by having the code call
|
||||
/// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow()
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_SjLj_Resume(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SjLj_Resume(ex_obj=%p)",
|
||||
(void *)exception_object);
|
||||
|
||||
if (exception_object->private_1 != 0)
|
||||
unwind_phase2_forced(exception_object,
|
||||
(_Unwind_Stop_Fn) exception_object->private_1,
|
||||
(void *)exception_object->private_2);
|
||||
else
|
||||
unwind_phase2(exception_object);
|
||||
|
||||
// clients assume _Unwind_Resume() does not return, so all we can do is abort.
|
||||
_LIBUNWIND_ABORT("_Unwind_SjLj_Resume() can't return");
|
||||
}
|
||||
|
||||
|
||||
/// Called by __cxa_rethrow().
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("__Unwind_SjLj_Resume_or_Rethrow(ex_obj=%p), "
|
||||
"private_1=%" PRIuPTR,
|
||||
(void *)exception_object, exception_object->private_1);
|
||||
// If this is non-forced and a stopping place was found, then this is a
|
||||
// re-throw.
|
||||
// Call _Unwind_RaiseException() as if this was a new exception.
|
||||
if (exception_object->private_1 == 0) {
|
||||
return _Unwind_SjLj_RaiseException(exception_object);
|
||||
// should return if there is no catch clause, so that __cxa_rethrow can call
|
||||
// std::terminate()
|
||||
}
|
||||
|
||||
// Call through to _Unwind_Resume() which distinguishes between forced and
|
||||
// regular exceptions.
|
||||
_Unwind_SjLj_Resume(exception_object);
|
||||
_LIBUNWIND_ABORT("__Unwind_SjLj_Resume_or_Rethrow() called "
|
||||
"_Unwind_SjLj_Resume() which unexpectedly returned");
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get LSDA for current frame.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) "
|
||||
"=> 0x%" PRIuPTR,
|
||||
(void *)context, ufc->lsda);
|
||||
return ufc->lsda;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get register values.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context *context,
|
||||
int index) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d)", (void *)context,
|
||||
index);
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
return ufc->resumeParameters[index];
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to alter register values.
|
||||
_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||
uintptr_t new_value) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%" PRIuPTR
|
||||
")",
|
||||
(void *)context, index, new_value);
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
ufc->resumeParameters[index] = new_value;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIu32,
|
||||
(void *)context, ufc->resumeLocation + 1);
|
||||
return ufc->resumeLocation + 1;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||
/// ipBefore is a boolean that says if IP is already adjusted to be the call
|
||||
/// site address. Normally IP is the return address.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
||||
int *ipBefore) {
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
*ipBefore = 0;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%" PRIu32,
|
||||
(void *)context, (void *)ipBefore,
|
||||
ufc->resumeLocation + 1);
|
||||
return ufc->resumeLocation + 1;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to alter instruction pointer.
|
||||
_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context,
|
||||
uintptr_t new_value) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%" PRIuPTR ")",
|
||||
(void *)context, new_value);
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
ufc->resumeLocation = new_value - 1;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to find the start of the
|
||||
/// function.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
|
||||
// Not supported or needed for sjlj based unwinding
|
||||
(void)context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p)", (void *)context);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 if a foreign exception
|
||||
/// is caught.
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_DeleteException(struct _Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)",
|
||||
(void *)exception_object);
|
||||
if (exception_object->exception_cleanup != NULL)
|
||||
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
|
||||
exception_object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get base address for data
|
||||
/// relative encodings.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetDataRelBase(struct _Unwind_Context *context) {
|
||||
// Not supported or needed for sjlj based unwinding
|
||||
(void)context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context);
|
||||
_LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented");
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get base address for text
|
||||
/// relative encodings.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetTextRelBase(struct _Unwind_Context *context) {
|
||||
// Not supported or needed for sjlj based unwinding
|
||||
(void)context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)", (void *)context);
|
||||
_LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented");
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler to get "Call Frame Area" for current frame.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p)", (void *)context);
|
||||
if (context != NULL) {
|
||||
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
|
||||
// Setjmp/longjmp based exceptions don't have a true CFA.
|
||||
// Instead, the SP in the jmpbuf is the closest approximation.
|
||||
return (uintptr_t) ufc->jbuf[2];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // defined(_LIBUNWIND_BUILD_SJLJ_APIS)
|
2953
third_party/libunwind/UnwindCursor.hpp
vendored
Normal file
2953
third_party/libunwind/UnwindCursor.hpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
351
third_party/libunwind/UnwindLevel1-gcc-ext.c
vendored
Normal file
351
third_party/libunwind/UnwindLevel1-gcc-ext.c
vendored
Normal file
|
@ -0,0 +1,351 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Implements gcc extensions to the C++ ABI Exception Handling Level 1.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "libc/isystem/inttypes.h"
|
||||
#include "libc/isystem/stdbool.h"
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/isystem/stdlib.h"
|
||||
#include "libc/isystem/string.h"
|
||||
|
||||
#include "third_party/libunwind/config.h"
|
||||
#include "third_party/libunwind/libunwind_ext.h"
|
||||
#include "third_party/libunwind/include/libunwind.h"
|
||||
#include "third_party/libunwind/Unwind-EHABI.h"
|
||||
#include "third_party/libunwind/include/unwind.h"
|
||||
|
||||
#if defined(_AIX)
|
||||
#include <sys/debug.h>
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS)
|
||||
|
||||
#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
|
||||
#define PRIVATE_1 private_[0]
|
||||
#elif defined(_LIBUNWIND_ARM_EHABI)
|
||||
#define PRIVATE_1 unwinder_cache.reserved1
|
||||
#else
|
||||
#define PRIVATE_1 private_1
|
||||
#endif
|
||||
|
||||
/// Called by __cxa_rethrow().
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API(
|
||||
"_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%" PRIdPTR,
|
||||
(void *)exception_object, (intptr_t)exception_object->PRIVATE_1);
|
||||
|
||||
// If this is non-forced and a stopping place was found, then this is a
|
||||
// re-throw.
|
||||
// Call _Unwind_RaiseException() as if this was a new exception
|
||||
if (exception_object->PRIVATE_1 == 0) {
|
||||
return _Unwind_RaiseException(exception_object);
|
||||
// Will return if there is no catch clause, so that __cxa_rethrow can call
|
||||
// std::terminate().
|
||||
}
|
||||
|
||||
// Call through to _Unwind_Resume() which distinguishes between forced and
|
||||
// regular exceptions.
|
||||
_Unwind_Resume(exception_object);
|
||||
_LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()"
|
||||
" which unexpectedly returned");
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to get base address for data
|
||||
/// relative encodings.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetDataRelBase(struct _Unwind_Context *context) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context);
|
||||
#if defined(_AIX)
|
||||
return unw_get_data_rel_base((unw_cursor_t *)context);
|
||||
#else
|
||||
(void)context;
|
||||
_LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to get base address for text
|
||||
/// relative encodings.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetTextRelBase(struct _Unwind_Context *context) {
|
||||
(void)context;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)", (void *)context);
|
||||
_LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented");
|
||||
}
|
||||
|
||||
|
||||
/// Scans unwind information to find the function that contains the
|
||||
/// specified code address "pc".
|
||||
_LIBUNWIND_EXPORT void *_Unwind_FindEnclosingFunction(void *pc) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_FindEnclosingFunction(pc=%p)", pc);
|
||||
#if defined(_AIX)
|
||||
if (pc == NULL)
|
||||
return NULL;
|
||||
|
||||
// Get the start address of the enclosing function from the function's
|
||||
// traceback table.
|
||||
uint32_t *p = (uint32_t *)pc;
|
||||
|
||||
// Keep looking forward until a word of 0 is found. The traceback
|
||||
// table starts at the following word.
|
||||
while (*p)
|
||||
++p;
|
||||
struct tbtable *TBTable = (struct tbtable *)(p + 1);
|
||||
|
||||
// Get the address of the traceback table extension.
|
||||
p = (uint32_t *)&TBTable->tb_ext;
|
||||
|
||||
// Skip field parminfo if it exists.
|
||||
if (TBTable->tb.fixedparms || TBTable->tb.floatparms)
|
||||
++p;
|
||||
|
||||
if (TBTable->tb.has_tboff)
|
||||
// *p contains the offset from the function start to traceback table.
|
||||
return (void *)((uintptr_t)TBTable - *p - sizeof(uint32_t));
|
||||
return NULL;
|
||||
#else
|
||||
// This is slow, but works.
|
||||
// We create an unwind cursor then alter the IP to be pc
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t uc;
|
||||
unw_proc_info_t info;
|
||||
__unw_getcontext(&uc);
|
||||
__unw_init_local(&cursor, &uc);
|
||||
__unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc);
|
||||
if (__unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS)
|
||||
return (void *)(intptr_t) info.start_ip;
|
||||
else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Walk every frame and call trace function at each one. If trace function
|
||||
/// returns anything other than _URC_NO_REASON, then walk is terminated.
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) {
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t uc;
|
||||
__unw_getcontext(&uc);
|
||||
__unw_init_local(&cursor, &uc);
|
||||
|
||||
_LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)",
|
||||
(void *)(uintptr_t)callback);
|
||||
|
||||
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||
// Create a mock exception object for force unwinding.
|
||||
_Unwind_Exception ex;
|
||||
memset(&ex, '\0', sizeof(ex));
|
||||
strcpy((char *)&ex.exception_class, "CLNGUNW");
|
||||
#endif
|
||||
|
||||
// walk each frame
|
||||
while (true) {
|
||||
_Unwind_Reason_Code result;
|
||||
|
||||
#if !defined(_LIBUNWIND_ARM_EHABI)
|
||||
// ask libunwind to get next frame (skip over first frame which is
|
||||
// _Unwind_Backtrace())
|
||||
if (__unw_step(&cursor) <= 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because cursor reached "
|
||||
"bottom of stack, returning %d",
|
||||
_URC_END_OF_STACK);
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
#else
|
||||
// Get the information for this frame.
|
||||
unw_proc_info_t frameInfo;
|
||||
if (__unw_get_proc_info(&cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
|
||||
// Update the pr_cache in the mock exception object.
|
||||
uint32_t *unwindInfo = (uint32_t *)frameInfo.unwind_info;
|
||||
ex.pr_cache.fnstart = frameInfo.start_ip;
|
||||
ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo;
|
||||
ex.pr_cache.additional= frameInfo.flags;
|
||||
|
||||
struct _Unwind_Context *context = (struct _Unwind_Context *)&cursor;
|
||||
// Get and call the personality function to unwind the frame.
|
||||
_Unwind_Personality_Fn handler = (_Unwind_Personality_Fn)frameInfo.handler;
|
||||
if (handler == NULL) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
if (handler(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, &ex, context) !=
|
||||
_URC_CONTINUE_UNWIND) {
|
||||
return _URC_END_OF_STACK;
|
||||
}
|
||||
#endif // defined(_LIBUNWIND_ARM_EHABI)
|
||||
|
||||
// debugging
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
char functionName[512];
|
||||
unw_proc_info_t frame;
|
||||
unw_word_t offset;
|
||||
__unw_get_proc_name(&cursor, functionName, 512, &offset);
|
||||
__unw_get_proc_info(&cursor, &frame);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
" _backtrace: start_ip=0x%" PRIxPTR ", func=%s, lsda=0x%" PRIxPTR ", context=%p",
|
||||
frame.start_ip, functionName, frame.lsda,
|
||||
(void *)&cursor);
|
||||
}
|
||||
|
||||
// call trace function with this frame
|
||||
result = (*callback)((struct _Unwind_Context *)(&cursor), ref);
|
||||
if (result != _URC_NO_REASON) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
" _backtrace: ended because callback returned %d", result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Find DWARF unwind info for an address 'pc' in some function.
|
||||
_LIBUNWIND_EXPORT const void *_Unwind_Find_FDE(const void *pc,
|
||||
struct dwarf_eh_bases *bases) {
|
||||
// This is slow, but works.
|
||||
// We create an unwind cursor then alter the IP to be pc
|
||||
unw_cursor_t cursor;
|
||||
unw_context_t uc;
|
||||
unw_proc_info_t info;
|
||||
__unw_getcontext(&uc);
|
||||
__unw_init_local(&cursor, &uc);
|
||||
__unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc);
|
||||
__unw_get_proc_info(&cursor, &info);
|
||||
bases->tbase = (uintptr_t)info.extra;
|
||||
bases->dbase = 0; // dbase not used on Mac OS X
|
||||
bases->func = (uintptr_t)info.start_ip;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_Find_FDE(pc=%p) => %p", pc,
|
||||
(void *)(intptr_t) info.unwind_info);
|
||||
return (void *)(intptr_t) info.unwind_info;
|
||||
}
|
||||
|
||||
/// Returns the CFA (call frame area, or stack pointer at start of function)
|
||||
/// for the current context.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_word_t result;
|
||||
__unw_get_reg(cursor, UNW_REG_SP, &result);
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p) => 0x%" PRIxPTR,
|
||||
(void *)context, result);
|
||||
return (uintptr_t)result;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||
/// ipBefore is a boolean that says if IP is already adjusted to be the call
|
||||
/// site address. Normally IP is the return address.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
||||
int *ipBefore) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p)", (void *)context);
|
||||
int isSignalFrame = __unw_is_signal_frame((unw_cursor_t *)context);
|
||||
// Negative means some kind of error (probably UNW_ENOINFO), but we have no
|
||||
// good way to report that, and this maintains backward compatibility with the
|
||||
// implementation that hard-coded zero in every case, even signal frames.
|
||||
if (isSignalFrame <= 0)
|
||||
*ipBefore = 0;
|
||||
else
|
||||
*ipBefore = 1;
|
||||
return _Unwind_GetIP(context);
|
||||
}
|
||||
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||
|
||||
/// Called by programs with dynamic code generators that want
|
||||
/// to register a dynamically generated FDE.
|
||||
/// This function has existed on Mac OS X since 10.4, but
|
||||
/// was broken until 10.6.
|
||||
_LIBUNWIND_EXPORT void __register_frame(const void *fde) {
|
||||
_LIBUNWIND_TRACE_API("__register_frame(%p)", fde);
|
||||
__unw_add_dynamic_fde((unw_word_t)(uintptr_t)fde);
|
||||
}
|
||||
|
||||
|
||||
/// Called by programs with dynamic code generators that want
|
||||
/// to unregister a dynamically generated FDE.
|
||||
/// This function has existed on Mac OS X since 10.4, but
|
||||
/// was broken until 10.6.
|
||||
_LIBUNWIND_EXPORT void __deregister_frame(const void *fde) {
|
||||
_LIBUNWIND_TRACE_API("__deregister_frame(%p)", fde);
|
||||
__unw_remove_dynamic_fde((unw_word_t)(uintptr_t)fde);
|
||||
}
|
||||
|
||||
|
||||
// The following register/deregister functions are gcc extensions.
|
||||
// They have existed on Mac OS X, but have never worked because Mac OS X
|
||||
// before 10.6 used keymgr to track known FDEs, but these functions
|
||||
// never got updated to use keymgr.
|
||||
// For now, we implement these as do-nothing functions to keep any existing
|
||||
// applications working. We also add the not in 10.6 symbol so that nwe
|
||||
// application won't be able to use them.
|
||||
|
||||
#if defined(_LIBUNWIND_SUPPORT_FRAME_APIS)
|
||||
_LIBUNWIND_EXPORT void __register_frame_info_bases(const void *fde, void *ob,
|
||||
void *tb, void *db) {
|
||||
(void)fde;
|
||||
(void)ob;
|
||||
(void)tb;
|
||||
(void)db;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_info_bases(%p,%p, %p, %p)",
|
||||
fde, ob, tb, db);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void __register_frame_info(const void *fde, void *ob) {
|
||||
(void)fde;
|
||||
(void)ob;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_info(%p, %p)", fde, ob);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void __register_frame_info_table_bases(const void *fde,
|
||||
void *ob, void *tb,
|
||||
void *db) {
|
||||
(void)fde;
|
||||
(void)ob;
|
||||
(void)tb;
|
||||
(void)db;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_info_table_bases"
|
||||
"(%p,%p, %p, %p)", fde, ob, tb, db);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void __register_frame_info_table(const void *fde, void *ob) {
|
||||
(void)fde;
|
||||
(void)ob;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_info_table(%p, %p)", fde, ob);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void __register_frame_table(const void *fde) {
|
||||
(void)fde;
|
||||
_LIBUNWIND_TRACE_API("__register_frame_table(%p)", fde);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void *__deregister_frame_info(const void *fde) {
|
||||
(void)fde;
|
||||
_LIBUNWIND_TRACE_API("__deregister_frame_info(%p)", fde);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT void *__deregister_frame_info_bases(const void *fde) {
|
||||
(void)fde;
|
||||
_LIBUNWIND_TRACE_API("__deregister_frame_info_bases(%p)", fde);
|
||||
// do nothing, this function never worked in Mac OS X
|
||||
return NULL;
|
||||
}
|
||||
#endif // defined(_LIBUNWIND_SUPPORT_FRAME_APIS)
|
||||
|
||||
#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||
|
||||
#endif // defined(_LIBUNWIND_BUILD_ZERO_COST_APIS)
|
587
third_party/libunwind/UnwindLevel1.c
vendored
Normal file
587
third_party/libunwind/UnwindLevel1.c
vendored
Normal file
|
@ -0,0 +1,587 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Implements C++ ABI Exception Handling Level 1 as documented at:
|
||||
// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
|
||||
// using libunwind
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// ARM EHABI does not specify _Unwind_{Get,Set}{GR,IP}(). Thus, we are
|
||||
// defining inline functions to delegate the function calls to
|
||||
// _Unwind_VRS_{Get,Set}(). However, some applications might declare the
|
||||
// function protetype directly (instead of including <unwind.h>), thus we need
|
||||
// to export these functions from libunwind.so as well.
|
||||
#define _LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE 1
|
||||
|
||||
#include "libc/isystem/inttypes.h"
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "libc/isystem/stdbool.h"
|
||||
#include "libc/isystem/stdlib.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/isystem/string.h"
|
||||
|
||||
#include "third_party/libunwind/cet_unwind.h"
|
||||
#include "third_party/libunwind/config.h"
|
||||
#include "third_party/libunwind/include/libunwind.h"
|
||||
#include "third_party/libunwind/libunwind_ext.h"
|
||||
#include "third_party/libunwind/include/unwind.h"
|
||||
|
||||
#if !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||
|
||||
#ifndef _LIBUNWIND_SUPPORT_SEH_UNWIND
|
||||
|
||||
// When CET is enabled, each "call" instruction will push return address to
|
||||
// CET shadow stack, each "ret" instruction will pop current CET shadow stack
|
||||
// top and compare it with target address which program will return.
|
||||
// In exception handing, some stack frames will be skipped before jumping to
|
||||
// landing pad and we must adjust CET shadow stack accordingly.
|
||||
// _LIBUNWIND_POP_CET_SSP is used to adjust CET shadow stack pointer and we
|
||||
// directly jump to __libunwind_Registers_x86/x86_64_jumpto instead of using
|
||||
// a regular function call to avoid pushing to CET shadow stack again.
|
||||
#if !defined(_LIBUNWIND_USE_CET)
|
||||
#define __unw_phase2_resume(cursor, fn) \
|
||||
do { \
|
||||
(void)fn; \
|
||||
__unw_resume((cursor)); \
|
||||
} while (0)
|
||||
#elif defined(_LIBUNWIND_TARGET_I386)
|
||||
#define __cet_ss_step_size 4
|
||||
#define __unw_phase2_resume(cursor, fn) \
|
||||
do { \
|
||||
_LIBUNWIND_POP_CET_SSP((fn)); \
|
||||
void *cetRegContext = __libunwind_cet_get_registers((cursor)); \
|
||||
void *cetJumpAddress = __libunwind_cet_get_jump_target(); \
|
||||
__asm__ volatile("push %%edi\n\t" \
|
||||
"sub $4, %%esp\n\t" \
|
||||
"jmp *%%edx\n\t" :: "D"(cetRegContext), \
|
||||
"d"(cetJumpAddress)); \
|
||||
} while (0)
|
||||
#elif defined(_LIBUNWIND_TARGET_X86_64)
|
||||
#define __cet_ss_step_size 8
|
||||
#define __unw_phase2_resume(cursor, fn) \
|
||||
do { \
|
||||
_LIBUNWIND_POP_CET_SSP((fn)); \
|
||||
void *cetRegContext = __libunwind_cet_get_registers((cursor)); \
|
||||
void *cetJumpAddress = __libunwind_cet_get_jump_target(); \
|
||||
__asm__ volatile("jmpq *%%rdx\n\t" :: "D"(cetRegContext), \
|
||||
"d"(cetJumpAddress)); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) {
|
||||
__unw_init_local(cursor, uc);
|
||||
|
||||
// Walk each frame looking for a place to stop.
|
||||
while (true) {
|
||||
// Ask libunwind to get next frame (skip over first which is
|
||||
// _Unwind_RaiseException).
|
||||
int stepResult = __unw_step(cursor);
|
||||
if (stepResult == 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_obj=%p): __unw_step() reached "
|
||||
"bottom => _URC_END_OF_STACK",
|
||||
(void *)exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
} else if (stepResult < 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_obj=%p): __unw_step failed => "
|
||||
"_URC_FATAL_PHASE1_ERROR",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE1_ERROR;
|
||||
}
|
||||
|
||||
// See if frame has code to run (has personality routine).
|
||||
unw_proc_info_t frameInfo;
|
||||
unw_word_t sp;
|
||||
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_obj=%p): __unw_get_proc_info "
|
||||
"failed => _URC_FATAL_PHASE1_ERROR",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE1_ERROR;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// When tracing, print state information.
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
char functionBuf[512];
|
||||
const char *functionName = functionBuf;
|
||||
unw_word_t offset;
|
||||
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
|
||||
&offset) != UNW_ESUCCESS) ||
|
||||
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||
functionName = ".anonymous.";
|
||||
unw_word_t pc;
|
||||
__unw_get_reg(cursor, UNW_REG_IP, &pc);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_obj=%p): pc=0x%" PRIxPTR ", start_ip=0x%" PRIxPTR
|
||||
", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "",
|
||||
(void *)exception_object, pc, frameInfo.start_ip, functionName,
|
||||
frameInfo.lsda, frameInfo.handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
// If there is a personality routine, ask it if it will want to stop at
|
||||
// this frame.
|
||||
if (frameInfo.handler != 0) {
|
||||
_Unwind_Personality_Fn p =
|
||||
(_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_obj=%p): calling personality function %p",
|
||||
(void *)exception_object, (void *)(uintptr_t)p);
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*p)(1, _UA_SEARCH_PHASE, exception_object->exception_class,
|
||||
exception_object, (struct _Unwind_Context *)(cursor));
|
||||
switch (personalityResult) {
|
||||
case _URC_HANDLER_FOUND:
|
||||
// found a catch clause or locals that need destructing in this frame
|
||||
// stop search and remember stack pointer at the frame
|
||||
__unw_get_reg(cursor, UNW_REG_SP, &sp);
|
||||
exception_object->private_2 = (uintptr_t)sp;
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_obj=%p): _URC_HANDLER_FOUND",
|
||||
(void *)exception_object);
|
||||
return _URC_NO_REASON;
|
||||
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_obj=%p): _URC_CONTINUE_UNWIND",
|
||||
(void *)exception_object);
|
||||
// continue unwinding
|
||||
break;
|
||||
|
||||
default:
|
||||
// something went wrong
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase1(ex_obj=%p): _URC_FATAL_PHASE1_ERROR",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE1_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
extern int __unw_step_stage2(unw_cursor_t *);
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) {
|
||||
__unw_init_local(cursor, uc);
|
||||
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_obj=%p)",
|
||||
(void *)exception_object);
|
||||
|
||||
// uc is initialized by __unw_getcontext in the parent frame. The first stack
|
||||
// frame walked is unwind_phase2.
|
||||
unsigned framesWalked = 1;
|
||||
#ifdef _LIBUNWIND_USE_CET
|
||||
unsigned long shadowStackTop = _get_ssp();
|
||||
#endif
|
||||
// Walk each frame until we reach where search phase said to stop.
|
||||
while (true) {
|
||||
|
||||
// Ask libunwind to get next frame (skip over first which is
|
||||
// _Unwind_RaiseException).
|
||||
int stepResult = __unw_step_stage2(cursor);
|
||||
if (stepResult == 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_obj=%p): __unw_step_stage2() reached "
|
||||
"bottom => _URC_END_OF_STACK",
|
||||
(void *)exception_object);
|
||||
return _URC_END_OF_STACK;
|
||||
} else if (stepResult < 0) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_obj=%p): __unw_step_stage2 failed => "
|
||||
"_URC_FATAL_PHASE1_ERROR",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
// Get info about this frame.
|
||||
unw_word_t sp;
|
||||
unw_proc_info_t frameInfo;
|
||||
__unw_get_reg(cursor, UNW_REG_SP, &sp);
|
||||
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_obj=%p): __unw_get_proc_info "
|
||||
"failed => _URC_FATAL_PHASE1_ERROR",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// When tracing, print state information.
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
char functionBuf[512];
|
||||
const char *functionName = functionBuf;
|
||||
unw_word_t offset;
|
||||
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
|
||||
&offset) != UNW_ESUCCESS) ||
|
||||
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||
functionName = ".anonymous.";
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_obj=%p): start_ip=0x%" PRIxPTR
|
||||
", func=%s, sp=0x%" PRIxPTR ", lsda=0x%" PRIxPTR
|
||||
", personality=0x%" PRIxPTR,
|
||||
(void *)exception_object, frameInfo.start_ip,
|
||||
functionName, sp, frameInfo.lsda,
|
||||
frameInfo.handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
// In CET enabled environment, we check return address stored in normal stack
|
||||
// against return address stored in CET shadow stack, if the 2 addresses don't
|
||||
// match, it means return address in normal stack has been corrupted, we return
|
||||
// _URC_FATAL_PHASE2_ERROR.
|
||||
#ifdef _LIBUNWIND_USE_CET
|
||||
if (shadowStackTop != 0) {
|
||||
unw_word_t retInNormalStack;
|
||||
__unw_get_reg(cursor, UNW_REG_IP, &retInNormalStack);
|
||||
unsigned long retInShadowStack = *(
|
||||
unsigned long *)(shadowStackTop + __cet_ss_step_size * framesWalked);
|
||||
if (retInNormalStack != retInShadowStack)
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
#endif
|
||||
++framesWalked;
|
||||
// If there is a personality routine, tell it we are unwinding.
|
||||
if (frameInfo.handler != 0) {
|
||||
_Unwind_Personality_Fn p =
|
||||
(_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler);
|
||||
_Unwind_Action action = _UA_CLEANUP_PHASE;
|
||||
if (sp == exception_object->private_2) {
|
||||
// Tell personality this was the frame it marked in phase 1.
|
||||
action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME);
|
||||
}
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)(cursor));
|
||||
switch (personalityResult) {
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
// Continue unwinding
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_obj=%p): _URC_CONTINUE_UNWIND",
|
||||
(void *)exception_object);
|
||||
if (sp == exception_object->private_2) {
|
||||
// Phase 1 said we would stop at this frame, but we did not...
|
||||
_LIBUNWIND_ABORT("during phase1 personality function said it would "
|
||||
"stop here, but now in phase2 it did not stop here");
|
||||
}
|
||||
break;
|
||||
case _URC_INSTALL_CONTEXT:
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2(ex_obj=%p): _URC_INSTALL_CONTEXT",
|
||||
(void *)exception_object);
|
||||
// Personality routine says to transfer control to landing pad.
|
||||
// We may get control back if landing pad calls _Unwind_Resume().
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
unw_word_t pc;
|
||||
__unw_get_reg(cursor, UNW_REG_IP, &pc);
|
||||
__unw_get_reg(cursor, UNW_REG_SP, &sp);
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_obj=%p): re-entering "
|
||||
"user code with ip=0x%" PRIxPTR
|
||||
", sp=0x%" PRIxPTR,
|
||||
(void *)exception_object, pc, sp);
|
||||
}
|
||||
|
||||
__unw_phase2_resume(cursor, framesWalked);
|
||||
// __unw_phase2_resume() only returns if there was an error.
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
default:
|
||||
// Personality routine returned an unknown result code.
|
||||
_LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d",
|
||||
personalityResult);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up phase did not resume at the frame that the search phase
|
||||
// said it would...
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
static _Unwind_Reason_Code
|
||||
unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor,
|
||||
_Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||
__unw_init_local(cursor, uc);
|
||||
|
||||
// uc is initialized by __unw_getcontext in the parent frame. The first stack
|
||||
// frame walked is unwind_phase2_forced.
|
||||
unsigned framesWalked = 1;
|
||||
// Walk each frame until we reach where search phase said to stop
|
||||
while (__unw_step_stage2(cursor) > 0) {
|
||||
|
||||
// Update info about this frame.
|
||||
unw_proc_info_t frameInfo;
|
||||
if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2_forced(ex_obj=%p): __unw_get_proc_info "
|
||||
"failed => _URC_END_OF_STACK",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// When tracing, print state information.
|
||||
if (_LIBUNWIND_TRACING_UNWINDING) {
|
||||
char functionBuf[512];
|
||||
const char *functionName = functionBuf;
|
||||
unw_word_t offset;
|
||||
if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf),
|
||||
&offset) != UNW_ESUCCESS) ||
|
||||
(frameInfo.start_ip + offset > frameInfo.end_ip))
|
||||
functionName = ".anonymous.";
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2_forced(ex_obj=%p): start_ip=0x%" PRIxPTR
|
||||
", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR,
|
||||
(void *)exception_object, frameInfo.start_ip, functionName,
|
||||
frameInfo.lsda, frameInfo.handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Call stop function at each frame.
|
||||
_Unwind_Action action =
|
||||
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
|
||||
_Unwind_Reason_Code stopResult =
|
||||
(*stop)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)(cursor), stop_parameter);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2_forced(ex_obj=%p): stop function returned %d",
|
||||
(void *)exception_object, stopResult);
|
||||
if (stopResult != _URC_NO_REASON) {
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2_forced(ex_obj=%p): stopped by stop function",
|
||||
(void *)exception_object);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
++framesWalked;
|
||||
// If there is a personality routine, tell it we are unwinding.
|
||||
if (frameInfo.handler != 0) {
|
||||
_Unwind_Personality_Fn p =
|
||||
(_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler);
|
||||
_LIBUNWIND_TRACE_UNWINDING(
|
||||
"unwind_phase2_forced(ex_obj=%p): calling personality function %p",
|
||||
(void *)exception_object, (void *)(uintptr_t)p);
|
||||
_Unwind_Reason_Code personalityResult =
|
||||
(*p)(1, action, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)(cursor));
|
||||
switch (personalityResult) {
|
||||
case _URC_CONTINUE_UNWIND:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_obj=%p): "
|
||||
"personality returned "
|
||||
"_URC_CONTINUE_UNWIND",
|
||||
(void *)exception_object);
|
||||
// Destructors called, continue unwinding
|
||||
break;
|
||||
case _URC_INSTALL_CONTEXT:
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_obj=%p): "
|
||||
"personality returned "
|
||||
"_URC_INSTALL_CONTEXT",
|
||||
(void *)exception_object);
|
||||
// We may get control back if landing pad calls _Unwind_Resume().
|
||||
__unw_phase2_resume(cursor, framesWalked);
|
||||
break;
|
||||
default:
|
||||
// Personality routine returned an unknown result code.
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_obj=%p): "
|
||||
"personality returned %d, "
|
||||
"_URC_FATAL_PHASE2_ERROR",
|
||||
(void *)exception_object, personalityResult);
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call stop function one last time and tell it we've reached the end
|
||||
// of the stack.
|
||||
_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_obj=%p): calling stop "
|
||||
"function with _UA_END_OF_STACK",
|
||||
(void *)exception_object);
|
||||
_Unwind_Action lastAction =
|
||||
(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
|
||||
(*stop)(1, lastAction, exception_object->exception_class, exception_object,
|
||||
(struct _Unwind_Context *)(cursor), stop_parameter);
|
||||
|
||||
// Clean up phase did not resume at the frame that the search phase said it
|
||||
// would.
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/// Called by __cxa_throw. Only returns if there is a fatal error.
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)",
|
||||
(void *)exception_object);
|
||||
unw_context_t uc;
|
||||
unw_cursor_t cursor;
|
||||
__unw_getcontext(&uc);
|
||||
|
||||
// Mark that this is a non-forced unwind, so _Unwind_Resume()
|
||||
// can do the right thing.
|
||||
exception_object->private_1 = 0;
|
||||
exception_object->private_2 = 0;
|
||||
|
||||
// phase 1: the search phase
|
||||
_Unwind_Reason_Code phase1 = unwind_phase1(&uc, &cursor, exception_object);
|
||||
if (phase1 != _URC_NO_REASON)
|
||||
return phase1;
|
||||
|
||||
// phase 2: the clean up phase
|
||||
return unwind_phase2(&uc, &cursor, exception_object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// When _Unwind_RaiseException() is in phase2, it hands control
|
||||
/// to the personality function at each frame. The personality
|
||||
/// may force a jump to a landing pad in that function, the landing
|
||||
/// pad code may then call _Unwind_Resume() to continue with the
|
||||
/// unwinding. Note: the call to _Unwind_Resume() is from compiler
|
||||
/// generated user code. All other _Unwind_* routines are called
|
||||
/// by the C++ runtime __cxa_* routines.
|
||||
///
|
||||
/// Note: re-throwing an exception (as opposed to continuing the unwind)
|
||||
/// is implemented by having the code call __cxa_rethrow() which
|
||||
/// in turn calls _Unwind_Resume_or_Rethrow().
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_Resume(_Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object);
|
||||
unw_context_t uc;
|
||||
unw_cursor_t cursor;
|
||||
__unw_getcontext(&uc);
|
||||
|
||||
if (exception_object->private_1 != 0)
|
||||
unwind_phase2_forced(&uc, &cursor, exception_object,
|
||||
(_Unwind_Stop_Fn) exception_object->private_1,
|
||||
(void *)exception_object->private_2);
|
||||
else
|
||||
unwind_phase2(&uc, &cursor, exception_object);
|
||||
|
||||
// Clients assume _Unwind_Resume() does not return, so all we can do is abort.
|
||||
_LIBUNWIND_ABORT("_Unwind_Resume() can't return");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Not used by C++.
|
||||
/// Unwinds stack, calling "stop" function at each frame.
|
||||
/// Could be used to implement longjmp().
|
||||
_LIBUNWIND_EXPORT _Unwind_Reason_Code
|
||||
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)",
|
||||
(void *)exception_object, (void *)(uintptr_t)stop);
|
||||
unw_context_t uc;
|
||||
unw_cursor_t cursor;
|
||||
__unw_getcontext(&uc);
|
||||
|
||||
// Mark that this is a forced unwind, so _Unwind_Resume() can do
|
||||
// the right thing.
|
||||
exception_object->private_1 = (uintptr_t) stop;
|
||||
exception_object->private_2 = (uintptr_t) stop_parameter;
|
||||
|
||||
// do it
|
||||
return unwind_phase2_forced(&uc, &cursor, exception_object, stop, stop_parameter);
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to get LSDA for current frame.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_proc_info_t frameInfo;
|
||||
uintptr_t result = 0;
|
||||
if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS)
|
||||
result = (uintptr_t)frameInfo.lsda;
|
||||
_LIBUNWIND_TRACE_API(
|
||||
"_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR,
|
||||
(void *)context, result);
|
||||
#if !defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
|
||||
if (result != 0) {
|
||||
if (*((uint8_t *)result) != 0xFF)
|
||||
_LIBUNWIND_DEBUG_LOG("lsda at 0x%" PRIxPTR " does not start with 0xFF",
|
||||
result);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// Called by personality handler during phase 2 to find the start of the
|
||||
/// function.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_proc_info_t frameInfo;
|
||||
uintptr_t result = 0;
|
||||
if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS)
|
||||
result = (uintptr_t)frameInfo.start_ip;
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR,
|
||||
(void *)context, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // !_LIBUNWIND_SUPPORT_SEH_UNWIND
|
||||
|
||||
/// Called by personality handler during phase 2 if a foreign exception
|
||||
// is caught.
|
||||
_LIBUNWIND_EXPORT void
|
||||
_Unwind_DeleteException(_Unwind_Exception *exception_object) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)",
|
||||
(void *)exception_object);
|
||||
if (exception_object->exception_cleanup != NULL)
|
||||
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT,
|
||||
exception_object);
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to get register values.
|
||||
_LIBUNWIND_EXPORT uintptr_t
|
||||
_Unwind_GetGR(struct _Unwind_Context *context, int index) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_word_t result;
|
||||
__unw_get_reg(cursor, index, &result);
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%" PRIxPTR,
|
||||
(void *)context, index, result);
|
||||
return (uintptr_t)result;
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to alter register values.
|
||||
_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||
uintptr_t value) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0" PRIxPTR
|
||||
")",
|
||||
(void *)context, index, value);
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
__unw_set_reg(cursor, index, value);
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to get instruction pointer.
|
||||
_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
unw_word_t result;
|
||||
__unw_get_reg(cursor, UNW_REG_IP, &result);
|
||||
_LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIxPTR,
|
||||
(void *)context, result);
|
||||
return (uintptr_t)result;
|
||||
}
|
||||
|
||||
/// Called by personality handler during phase 2 to alter instruction pointer,
|
||||
/// such as setting where the landing pad is, so _Unwind_Resume() will
|
||||
/// start executing in the landing pad.
|
||||
_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context,
|
||||
uintptr_t value) {
|
||||
_LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%0" PRIxPTR ")",
|
||||
(void *)context, value);
|
||||
unw_cursor_t *cursor = (unw_cursor_t *)context;
|
||||
__unw_set_reg(cursor, UNW_REG_IP, value);
|
||||
}
|
||||
|
||||
#endif // !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__)
|
41
third_party/libunwind/cet_unwind.h
vendored
Normal file
41
third_party/libunwind/cet_unwind.h
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LIBUNWIND_CET_UNWIND_H
|
||||
#define LIBUNWIND_CET_UNWIND_H
|
||||
|
||||
#include "third_party/libunwind/include/libunwind.h"
|
||||
|
||||
// Currently, CET is implemented on Linux x86 platforms.
|
||||
#if defined(_LIBUNWIND_TARGET_LINUX) && defined(__CET__) && defined(__SHSTK__)
|
||||
#define _LIBUNWIND_USE_CET 1
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_USE_CET)
|
||||
#include <cet.h>
|
||||
#include <immintrin.h>
|
||||
|
||||
#define _LIBUNWIND_POP_CET_SSP(x) \
|
||||
do { \
|
||||
unsigned long ssp = _get_ssp(); \
|
||||
if (ssp != 0) { \
|
||||
unsigned int tmp = (x); \
|
||||
while (tmp > 255) { \
|
||||
_inc_ssp(255); \
|
||||
tmp -= 255; \
|
||||
} \
|
||||
_inc_ssp(tmp); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
extern void *__libunwind_cet_get_registers(unw_cursor_t *);
|
||||
extern void *__libunwind_cet_get_jump_target(void);
|
||||
|
||||
#endif
|
245
third_party/libunwind/config.h
vendored
Normal file
245
third_party/libunwind/config.h
vendored
Normal file
|
@ -0,0 +1,245 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Defines macros used within libunwind project.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#ifndef LIBUNWIND_CONFIG_H
|
||||
#define LIBUNWIND_CONFIG_H
|
||||
|
||||
#include "libc/isystem/assert.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "libc/isystem/stdlib.h"
|
||||
|
||||
#include "third_party/libunwind/include/__libunwind_config.h"
|
||||
|
||||
// Platform specific configuration defines.
|
||||
#ifdef __APPLE__
|
||||
#if defined(FOR_DYLD)
|
||||
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1
|
||||
#else
|
||||
#define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
|
||||
#endif
|
||||
#elif defined(_WIN32)
|
||||
#ifdef __SEH__
|
||||
#define _LIBUNWIND_SUPPORT_SEH_UNWIND 1
|
||||
#else
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
|
||||
#endif
|
||||
#elif defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
#if !defined(_LIBUNWIND_ARM_EHABI)
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_INDEX 1
|
||||
#endif
|
||||
#elif defined(__BIONIC__) && defined(_LIBUNWIND_ARM_EHABI)
|
||||
// For ARM EHABI, Bionic didn't implement dl_iterate_phdr until API 21. After
|
||||
// API 21, dl_iterate_phdr exists, but dl_unwind_find_exidx is much faster.
|
||||
#define _LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX 1
|
||||
#elif defined(_AIX)
|
||||
// The traceback table at the end of each function is used for unwinding.
|
||||
#define _LIBUNWIND_SUPPORT_TBTAB_UNWIND 1
|
||||
#else
|
||||
// Assume an ELF system with a dl_iterate_phdr function.
|
||||
#define _LIBUNWIND_USE_DL_ITERATE_PHDR 1
|
||||
#if !defined(_LIBUNWIND_ARM_EHABI)
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
|
||||
#define _LIBUNWIND_SUPPORT_DWARF_INDEX 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_HIDE_SYMBOLS)
|
||||
// The CMake file passes -fvisibility=hidden to control ELF/Mach-O visibility.
|
||||
#define _LIBUNWIND_EXPORT
|
||||
#define _LIBUNWIND_HIDDEN
|
||||
#else
|
||||
#if !defined(__ELF__) && !defined(__MACH__) && !defined(_AIX)
|
||||
#define _LIBUNWIND_EXPORT __declspec(dllexport)
|
||||
#define _LIBUNWIND_HIDDEN
|
||||
#else
|
||||
#define _LIBUNWIND_EXPORT __attribute__((visibility("default")))
|
||||
#define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden")))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define STR(a) #a
|
||||
#define XSTR(a) STR(a)
|
||||
#define SYMBOL_NAME(name) XSTR(__USER_LABEL_PREFIX__) #name
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(_LIBUNWIND_HIDE_SYMBOLS)
|
||||
#define _LIBUNWIND_ALIAS_VISIBILITY(name) __asm__(".private_extern " name);
|
||||
#else
|
||||
#define _LIBUNWIND_ALIAS_VISIBILITY(name)
|
||||
#endif
|
||||
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
|
||||
__asm__(".globl " SYMBOL_NAME(aliasname)); \
|
||||
__asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name)); \
|
||||
_LIBUNWIND_ALIAS_VISIBILITY(SYMBOL_NAME(aliasname))
|
||||
#elif defined(__ELF__) || defined(_AIX)
|
||||
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
|
||||
extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \
|
||||
__attribute__((weak, alias(#name)));
|
||||
#elif defined(_WIN32)
|
||||
#if defined(__MINGW32__)
|
||||
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
|
||||
extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \
|
||||
__attribute__((alias(#name)));
|
||||
#else
|
||||
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
|
||||
__pragma(comment(linker, "/alternatename:" SYMBOL_NAME(aliasname) "=" \
|
||||
SYMBOL_NAME(name))) \
|
||||
extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname;
|
||||
#endif
|
||||
#else
|
||||
#error Unsupported target
|
||||
#endif
|
||||
|
||||
// Apple/armv7k defaults to DWARF/Compact unwinding, but its libunwind also
|
||||
// needs to include the SJLJ APIs.
|
||||
#if (defined(__APPLE__) && defined(__arm__)) || defined(__USING_SJLJ_EXCEPTIONS__)
|
||||
#define _LIBUNWIND_BUILD_SJLJ_APIS
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__)
|
||||
#define _LIBUNWIND_SUPPORT_FRAME_APIS
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || \
|
||||
(!defined(__APPLE__) && defined(__arm__)) || defined(__aarch64__) || \
|
||||
defined(__mips__) || defined(__riscv) || defined(__hexagon__) || \
|
||||
defined(__sparc__) || defined(__s390x__) || defined(__loongarch__)
|
||||
#if !defined(_LIBUNWIND_BUILD_SJLJ_APIS)
|
||||
#define _LIBUNWIND_BUILD_ZERO_COST_APIS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _LIBUNWIND_REMEMBER_HEAP_ALLOC
|
||||
#if defined(_LIBUNWIND_REMEMBER_STACK_ALLOC) || defined(__APPLE__) || \
|
||||
defined(__linux__) || defined(__ANDROID__) || defined(__MINGW32__) || \
|
||||
defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
#define _LIBUNWIND_REMEMBER_ALLOC(_size) alloca(_size)
|
||||
#define _LIBUNWIND_REMEMBER_FREE(_ptr) \
|
||||
do { \
|
||||
} while (0)
|
||||
#elif defined(_WIN32)
|
||||
#define _LIBUNWIND_REMEMBER_ALLOC(_size) _malloca(_size)
|
||||
#define _LIBUNWIND_REMEMBER_FREE(_ptr) _freea(_ptr)
|
||||
#define _LIBUNWIND_REMEMBER_CLEANUP_NEEDED
|
||||
#else
|
||||
#define _LIBUNWIND_REMEMBER_ALLOC(_size) malloc(_size)
|
||||
#define _LIBUNWIND_REMEMBER_FREE(_ptr) free(_ptr)
|
||||
#define _LIBUNWIND_REMEMBER_CLEANUP_NEEDED
|
||||
#endif
|
||||
#else /* _LIBUNWIND_REMEMBER_HEAP_ALLOC */
|
||||
#define _LIBUNWIND_REMEMBER_ALLOC(_size) malloc(_size)
|
||||
#define _LIBUNWIND_REMEMBER_FREE(_ptr) free(_ptr)
|
||||
#define _LIBUNWIND_REMEMBER_CLEANUP_NEEDED
|
||||
#endif
|
||||
|
||||
#if defined(NDEBUG) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
#define _LIBUNWIND_ABORT(msg) \
|
||||
do { \
|
||||
abort(); \
|
||||
} while (0)
|
||||
#else
|
||||
#define _LIBUNWIND_ABORT(msg) \
|
||||
do { \
|
||||
fprintf(stderr, "libunwind: %s - %s\n", __func__, msg); \
|
||||
fflush(stderr); \
|
||||
abort(); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if defined(NDEBUG) && defined(_LIBUNWIND_IS_BAREMETAL)
|
||||
#define _LIBUNWIND_LOG0(msg)
|
||||
#define _LIBUNWIND_LOG(msg, ...)
|
||||
#else
|
||||
#define _LIBUNWIND_LOG0(msg) do { \
|
||||
fprintf(stderr, "libunwind: " msg "\n"); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
#define _LIBUNWIND_LOG(msg, ...) do { \
|
||||
fprintf(stderr, "libunwind: " msg "\n", __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if defined(NDEBUG)
|
||||
#define _LIBUNWIND_LOG_IF_FALSE(x) x
|
||||
#else
|
||||
#define _LIBUNWIND_LOG_IF_FALSE(x) \
|
||||
do { \
|
||||
bool _ret = x; \
|
||||
if (!_ret) \
|
||||
_LIBUNWIND_LOG("" #x " failed in %s", __FUNCTION__); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// Macros that define away in non-Debug builds
|
||||
#ifdef NDEBUG
|
||||
#define _LIBUNWIND_DEBUG_LOG(msg, ...)
|
||||
#define _LIBUNWIND_TRACE_API(msg, ...)
|
||||
#define _LIBUNWIND_TRACING_UNWINDING (0)
|
||||
#define _LIBUNWIND_TRACING_DWARF (0)
|
||||
#define _LIBUNWIND_TRACE_UNWINDING(msg, ...)
|
||||
#define _LIBUNWIND_TRACE_DWARF(...)
|
||||
#else
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern bool logAPIs(void);
|
||||
extern bool logUnwinding(void);
|
||||
extern bool logDWARF(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#define _LIBUNWIND_DEBUG_LOG(msg, ...) _LIBUNWIND_LOG(msg, __VA_ARGS__)
|
||||
#define _LIBUNWIND_TRACE_API(msg, ...) \
|
||||
do { \
|
||||
if (logAPIs()) \
|
||||
_LIBUNWIND_LOG(msg, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define _LIBUNWIND_TRACING_UNWINDING logUnwinding()
|
||||
#define _LIBUNWIND_TRACING_DWARF logDWARF()
|
||||
#define _LIBUNWIND_TRACE_UNWINDING(msg, ...) \
|
||||
do { \
|
||||
if (logUnwinding()) \
|
||||
_LIBUNWIND_LOG(msg, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define _LIBUNWIND_TRACE_DWARF(...) \
|
||||
do { \
|
||||
if (logDWARF()) \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
// Used to fit UnwindCursor and Registers_xxx types against unw_context_t /
|
||||
// unw_cursor_t sized memory blocks.
|
||||
#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||
# define COMP_OP ==
|
||||
#else
|
||||
# define COMP_OP <=
|
||||
#endif
|
||||
template <typename _Type, typename _Mem>
|
||||
struct check_fit {
|
||||
template <typename T>
|
||||
struct blk_count {
|
||||
static const size_t count =
|
||||
(sizeof(T) + sizeof(uint64_t) - 1) / sizeof(uint64_t);
|
||||
};
|
||||
static const bool does_fit =
|
||||
(blk_count<_Type>::count COMP_OP blk_count<_Mem>::count);
|
||||
};
|
||||
#undef COMP_OP
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // LIBUNWIND_CONFIG_H
|
239
third_party/libunwind/dwarf2.h
vendored
Normal file
239
third_party/libunwind/dwarf2.h
vendored
Normal file
|
@ -0,0 +1,239 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
/*
|
||||
These constants were taken from version 3 of the DWARF standard,
|
||||
which is Copyright (c) 2005 Free Standards Group, and
|
||||
Copyright (c) 1992, 1993 UNIX International, Inc.
|
||||
*/
|
||||
|
||||
#ifndef __DWARF2__
|
||||
#define __DWARF2__
|
||||
|
||||
// DWARF unwind instructions
|
||||
enum {
|
||||
DW_CFA_nop = 0x0,
|
||||
DW_CFA_set_loc = 0x1,
|
||||
DW_CFA_advance_loc1 = 0x2,
|
||||
DW_CFA_advance_loc2 = 0x3,
|
||||
DW_CFA_advance_loc4 = 0x4,
|
||||
DW_CFA_offset_extended = 0x5,
|
||||
DW_CFA_restore_extended = 0x6,
|
||||
DW_CFA_undefined = 0x7,
|
||||
DW_CFA_same_value = 0x8,
|
||||
DW_CFA_register = 0x9,
|
||||
DW_CFA_remember_state = 0xA,
|
||||
DW_CFA_restore_state = 0xB,
|
||||
DW_CFA_def_cfa = 0xC,
|
||||
DW_CFA_def_cfa_register = 0xD,
|
||||
DW_CFA_def_cfa_offset = 0xE,
|
||||
DW_CFA_def_cfa_expression = 0xF,
|
||||
DW_CFA_expression = 0x10,
|
||||
DW_CFA_offset_extended_sf = 0x11,
|
||||
DW_CFA_def_cfa_sf = 0x12,
|
||||
DW_CFA_def_cfa_offset_sf = 0x13,
|
||||
DW_CFA_val_offset = 0x14,
|
||||
DW_CFA_val_offset_sf = 0x15,
|
||||
DW_CFA_val_expression = 0x16,
|
||||
DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta
|
||||
DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register
|
||||
DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register
|
||||
|
||||
// GNU extensions
|
||||
DW_CFA_GNU_window_save = 0x2D,
|
||||
DW_CFA_GNU_args_size = 0x2E,
|
||||
DW_CFA_GNU_negative_offset_extended = 0x2F,
|
||||
|
||||
// AARCH64 extensions
|
||||
DW_CFA_AARCH64_negate_ra_state = 0x2D
|
||||
};
|
||||
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
|
||||
// DWARF expressions
|
||||
enum {
|
||||
DW_OP_addr = 0x03, // constant address (size target specific)
|
||||
DW_OP_deref = 0x06,
|
||||
DW_OP_const1u = 0x08, // 1-byte constant
|
||||
DW_OP_const1s = 0x09, // 1-byte constant
|
||||
DW_OP_const2u = 0x0A, // 2-byte constant
|
||||
DW_OP_const2s = 0x0B, // 2-byte constant
|
||||
DW_OP_const4u = 0x0C, // 4-byte constant
|
||||
DW_OP_const4s = 0x0D, // 4-byte constant
|
||||
DW_OP_const8u = 0x0E, // 8-byte constant
|
||||
DW_OP_const8s = 0x0F, // 8-byte constant
|
||||
DW_OP_constu = 0x10, // ULEB128 constant
|
||||
DW_OP_consts = 0x11, // SLEB128 constant
|
||||
DW_OP_dup = 0x12,
|
||||
DW_OP_drop = 0x13,
|
||||
DW_OP_over = 0x14,
|
||||
DW_OP_pick = 0x15, // 1-byte stack index
|
||||
DW_OP_swap = 0x16,
|
||||
DW_OP_rot = 0x17,
|
||||
DW_OP_xderef = 0x18,
|
||||
DW_OP_abs = 0x19,
|
||||
DW_OP_and = 0x1A,
|
||||
DW_OP_div = 0x1B,
|
||||
DW_OP_minus = 0x1C,
|
||||
DW_OP_mod = 0x1D,
|
||||
DW_OP_mul = 0x1E,
|
||||
DW_OP_neg = 0x1F,
|
||||
DW_OP_not = 0x20,
|
||||
DW_OP_or = 0x21,
|
||||
DW_OP_plus = 0x22,
|
||||
DW_OP_plus_uconst = 0x23, // ULEB128 addend
|
||||
DW_OP_shl = 0x24,
|
||||
DW_OP_shr = 0x25,
|
||||
DW_OP_shra = 0x26,
|
||||
DW_OP_xor = 0x27,
|
||||
DW_OP_skip = 0x2F, // signed 2-byte constant
|
||||
DW_OP_bra = 0x28, // signed 2-byte constant
|
||||
DW_OP_eq = 0x29,
|
||||
DW_OP_ge = 0x2A,
|
||||
DW_OP_gt = 0x2B,
|
||||
DW_OP_le = 0x2C,
|
||||
DW_OP_lt = 0x2D,
|
||||
DW_OP_ne = 0x2E,
|
||||
DW_OP_lit0 = 0x30, // Literal 0
|
||||
DW_OP_lit1 = 0x31, // Literal 1
|
||||
DW_OP_lit2 = 0x32, // Literal 2
|
||||
DW_OP_lit3 = 0x33, // Literal 3
|
||||
DW_OP_lit4 = 0x34, // Literal 4
|
||||
DW_OP_lit5 = 0x35, // Literal 5
|
||||
DW_OP_lit6 = 0x36, // Literal 6
|
||||
DW_OP_lit7 = 0x37, // Literal 7
|
||||
DW_OP_lit8 = 0x38, // Literal 8
|
||||
DW_OP_lit9 = 0x39, // Literal 9
|
||||
DW_OP_lit10 = 0x3A, // Literal 10
|
||||
DW_OP_lit11 = 0x3B, // Literal 11
|
||||
DW_OP_lit12 = 0x3C, // Literal 12
|
||||
DW_OP_lit13 = 0x3D, // Literal 13
|
||||
DW_OP_lit14 = 0x3E, // Literal 14
|
||||
DW_OP_lit15 = 0x3F, // Literal 15
|
||||
DW_OP_lit16 = 0x40, // Literal 16
|
||||
DW_OP_lit17 = 0x41, // Literal 17
|
||||
DW_OP_lit18 = 0x42, // Literal 18
|
||||
DW_OP_lit19 = 0x43, // Literal 19
|
||||
DW_OP_lit20 = 0x44, // Literal 20
|
||||
DW_OP_lit21 = 0x45, // Literal 21
|
||||
DW_OP_lit22 = 0x46, // Literal 22
|
||||
DW_OP_lit23 = 0x47, // Literal 23
|
||||
DW_OP_lit24 = 0x48, // Literal 24
|
||||
DW_OP_lit25 = 0x49, // Literal 25
|
||||
DW_OP_lit26 = 0x4A, // Literal 26
|
||||
DW_OP_lit27 = 0x4B, // Literal 27
|
||||
DW_OP_lit28 = 0x4C, // Literal 28
|
||||
DW_OP_lit29 = 0x4D, // Literal 29
|
||||
DW_OP_lit30 = 0x4E, // Literal 30
|
||||
DW_OP_lit31 = 0x4F, // Literal 31
|
||||
DW_OP_reg0 = 0x50, // Contents of reg0
|
||||
DW_OP_reg1 = 0x51, // Contents of reg1
|
||||
DW_OP_reg2 = 0x52, // Contents of reg2
|
||||
DW_OP_reg3 = 0x53, // Contents of reg3
|
||||
DW_OP_reg4 = 0x54, // Contents of reg4
|
||||
DW_OP_reg5 = 0x55, // Contents of reg5
|
||||
DW_OP_reg6 = 0x56, // Contents of reg6
|
||||
DW_OP_reg7 = 0x57, // Contents of reg7
|
||||
DW_OP_reg8 = 0x58, // Contents of reg8
|
||||
DW_OP_reg9 = 0x59, // Contents of reg9
|
||||
DW_OP_reg10 = 0x5A, // Contents of reg10
|
||||
DW_OP_reg11 = 0x5B, // Contents of reg11
|
||||
DW_OP_reg12 = 0x5C, // Contents of reg12
|
||||
DW_OP_reg13 = 0x5D, // Contents of reg13
|
||||
DW_OP_reg14 = 0x5E, // Contents of reg14
|
||||
DW_OP_reg15 = 0x5F, // Contents of reg15
|
||||
DW_OP_reg16 = 0x60, // Contents of reg16
|
||||
DW_OP_reg17 = 0x61, // Contents of reg17
|
||||
DW_OP_reg18 = 0x62, // Contents of reg18
|
||||
DW_OP_reg19 = 0x63, // Contents of reg19
|
||||
DW_OP_reg20 = 0x64, // Contents of reg20
|
||||
DW_OP_reg21 = 0x65, // Contents of reg21
|
||||
DW_OP_reg22 = 0x66, // Contents of reg22
|
||||
DW_OP_reg23 = 0x67, // Contents of reg23
|
||||
DW_OP_reg24 = 0x68, // Contents of reg24
|
||||
DW_OP_reg25 = 0x69, // Contents of reg25
|
||||
DW_OP_reg26 = 0x6A, // Contents of reg26
|
||||
DW_OP_reg27 = 0x6B, // Contents of reg27
|
||||
DW_OP_reg28 = 0x6C, // Contents of reg28
|
||||
DW_OP_reg29 = 0x6D, // Contents of reg29
|
||||
DW_OP_reg30 = 0x6E, // Contents of reg30
|
||||
DW_OP_reg31 = 0x6F, // Contents of reg31
|
||||
DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset
|
||||
DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset
|
||||
DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset
|
||||
DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset
|
||||
DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset
|
||||
DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset
|
||||
DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset
|
||||
DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset
|
||||
DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset
|
||||
DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset
|
||||
DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset
|
||||
DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset
|
||||
DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset
|
||||
DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset
|
||||
DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset
|
||||
DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset
|
||||
DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset
|
||||
DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset
|
||||
DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset
|
||||
DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset
|
||||
DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset
|
||||
DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset
|
||||
DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset
|
||||
DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset
|
||||
DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset
|
||||
DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset
|
||||
DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset
|
||||
DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset
|
||||
DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset
|
||||
DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset
|
||||
DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset
|
||||
DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset
|
||||
DW_OP_regx = 0x90, // ULEB128 register
|
||||
DW_OP_fbreg = 0x91, // SLEB128 offset
|
||||
DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset
|
||||
DW_OP_piece = 0x93, // ULEB128 size of piece addressed
|
||||
DW_OP_deref_size = 0x94, // 1-byte size of data retrieved
|
||||
DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved
|
||||
DW_OP_nop = 0x96,
|
||||
DW_OP_push_object_addres = 0x97,
|
||||
DW_OP_call2 = 0x98, // 2-byte offset of DIE
|
||||
DW_OP_call4 = 0x99, // 4-byte offset of DIE
|
||||
DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE
|
||||
DW_OP_lo_user = 0xE0,
|
||||
DW_OP_APPLE_uninit = 0xF0,
|
||||
DW_OP_hi_user = 0xFF
|
||||
};
|
||||
|
||||
|
||||
#endif
|
205
third_party/libunwind/include/__libunwind_config.h
vendored
Normal file
205
third_party/libunwind/include/__libunwind_config.h
vendored
Normal file
|
@ -0,0 +1,205 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ____LIBUNWIND_CONFIG_H__
|
||||
#define ____LIBUNWIND_CONFIG_H__
|
||||
|
||||
#define _LIBUNWIND_VERSION 15000
|
||||
|
||||
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \
|
||||
!defined(__ARM_DWARF_EH__) && !defined(__SEH__)
|
||||
#define _LIBUNWIND_ARM_EHABI
|
||||
#endif
|
||||
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86 8
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64 32
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC 112
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64 116
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64 95
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM 287
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K 32
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS 65
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC 31
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64 31
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON 34
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV 64
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE 143
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_S390X 83
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_LOONGARCH 64
|
||||
|
||||
#if defined(_LIBUNWIND_IS_NATIVE_ONLY)
|
||||
# if defined(__linux__)
|
||||
# define _LIBUNWIND_TARGET_LINUX 1
|
||||
# endif
|
||||
# if defined(__i386__)
|
||||
# define _LIBUNWIND_TARGET_I386
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 8
|
||||
# define _LIBUNWIND_CURSOR_SIZE 15
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86
|
||||
# elif defined(__x86_64__)
|
||||
# define _LIBUNWIND_TARGET_X86_64 1
|
||||
# if defined(_WIN64)
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 54
|
||||
# ifdef __SEH__
|
||||
# define _LIBUNWIND_CURSOR_SIZE 204
|
||||
# else
|
||||
# define _LIBUNWIND_CURSOR_SIZE 66
|
||||
# endif
|
||||
# else
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 21
|
||||
# define _LIBUNWIND_CURSOR_SIZE 33
|
||||
# endif
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64
|
||||
# elif defined(__powerpc64__)
|
||||
# define _LIBUNWIND_TARGET_PPC64 1
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 167
|
||||
# define _LIBUNWIND_CURSOR_SIZE 179
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64
|
||||
# elif defined(__powerpc__)
|
||||
# define _LIBUNWIND_TARGET_PPC 1
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 117
|
||||
# define _LIBUNWIND_CURSOR_SIZE 124
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC
|
||||
# elif defined(__aarch64__)
|
||||
# define _LIBUNWIND_TARGET_AARCH64 1
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 66
|
||||
# if defined(__SEH__)
|
||||
# define _LIBUNWIND_CURSOR_SIZE 164
|
||||
# else
|
||||
# define _LIBUNWIND_CURSOR_SIZE 78
|
||||
# endif
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64
|
||||
# elif defined(__arm__)
|
||||
# define _LIBUNWIND_TARGET_ARM 1
|
||||
# if defined(__SEH__)
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 42
|
||||
# define _LIBUNWIND_CURSOR_SIZE 80
|
||||
# elif defined(__ARM_WMMX)
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 61
|
||||
# define _LIBUNWIND_CURSOR_SIZE 68
|
||||
# else
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 42
|
||||
# define _LIBUNWIND_CURSOR_SIZE 49
|
||||
# endif
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM
|
||||
# elif defined(__or1k__)
|
||||
# define _LIBUNWIND_TARGET_OR1K 1
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 16
|
||||
# define _LIBUNWIND_CURSOR_SIZE 24
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K
|
||||
# elif defined(__hexagon__)
|
||||
# define _LIBUNWIND_TARGET_HEXAGON 1
|
||||
// Values here change when : Registers.hpp - hexagon_thread_state_t change
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 18
|
||||
# define _LIBUNWIND_CURSOR_SIZE 24
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON
|
||||
# elif defined(__mips__)
|
||||
# if defined(_ABIO32) && _MIPS_SIM == _ABIO32
|
||||
# define _LIBUNWIND_TARGET_MIPS_O32 1
|
||||
# if defined(__mips_hard_float)
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 50
|
||||
# define _LIBUNWIND_CURSOR_SIZE 57
|
||||
# else
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 18
|
||||
# define _LIBUNWIND_CURSOR_SIZE 24
|
||||
# endif
|
||||
# elif defined(_ABIN32) && _MIPS_SIM == _ABIN32
|
||||
# define _LIBUNWIND_TARGET_MIPS_NEWABI 1
|
||||
# if defined(__mips_hard_float)
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 67
|
||||
# define _LIBUNWIND_CURSOR_SIZE 74
|
||||
# else
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 35
|
||||
# define _LIBUNWIND_CURSOR_SIZE 42
|
||||
# endif
|
||||
# elif defined(_ABI64) && _MIPS_SIM == _ABI64
|
||||
# define _LIBUNWIND_TARGET_MIPS_NEWABI 1
|
||||
# if defined(__mips_hard_float)
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 67
|
||||
# define _LIBUNWIND_CURSOR_SIZE 79
|
||||
# else
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 35
|
||||
# define _LIBUNWIND_CURSOR_SIZE 47
|
||||
# endif
|
||||
# else
|
||||
# error "Unsupported MIPS ABI and/or environment"
|
||||
# endif
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS
|
||||
#elif defined(__sparc__) && defined(__arch64__)
|
||||
#define _LIBUNWIND_TARGET_SPARC64 1
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER \
|
||||
_LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64
|
||||
#define _LIBUNWIND_CONTEXT_SIZE 33
|
||||
#define _LIBUNWIND_CURSOR_SIZE 45
|
||||
# elif defined(__sparc__)
|
||||
#define _LIBUNWIND_TARGET_SPARC 1
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC
|
||||
#define _LIBUNWIND_CONTEXT_SIZE 16
|
||||
#define _LIBUNWIND_CURSOR_SIZE 23
|
||||
# elif defined(__riscv)
|
||||
# define _LIBUNWIND_TARGET_RISCV 1
|
||||
# if defined(__riscv_flen)
|
||||
# define RISCV_FLEN __riscv_flen
|
||||
# else
|
||||
# define RISCV_FLEN 0
|
||||
# endif
|
||||
# define _LIBUNWIND_CONTEXT_SIZE (32 * (__riscv_xlen + RISCV_FLEN) / 64)
|
||||
# if __riscv_xlen == 32
|
||||
# define _LIBUNWIND_CURSOR_SIZE (_LIBUNWIND_CONTEXT_SIZE + 7)
|
||||
# elif __riscv_xlen == 64
|
||||
# define _LIBUNWIND_CURSOR_SIZE (_LIBUNWIND_CONTEXT_SIZE + 12)
|
||||
# else
|
||||
# error "Unsupported RISC-V ABI"
|
||||
# endif
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV
|
||||
# elif defined(__ve__)
|
||||
# define _LIBUNWIND_TARGET_VE 1
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 67
|
||||
# define _LIBUNWIND_CURSOR_SIZE 79
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE
|
||||
# elif defined(__s390x__)
|
||||
# define _LIBUNWIND_TARGET_S390X 1
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 34
|
||||
# define _LIBUNWIND_CURSOR_SIZE 46
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_S390X
|
||||
#elif defined(__loongarch__)
|
||||
#define _LIBUNWIND_TARGET_LOONGARCH 1
|
||||
#if __loongarch_grlen == 64
|
||||
#define _LIBUNWIND_CONTEXT_SIZE 65
|
||||
#define _LIBUNWIND_CURSOR_SIZE 77
|
||||
#else
|
||||
#error "Unsupported LoongArch ABI"
|
||||
#endif
|
||||
#define _LIBUNWIND_HIGHEST_DWARF_REGISTER \
|
||||
_LIBUNWIND_HIGHEST_DWARF_REGISTER_LOONGARCH
|
||||
# else
|
||||
# error "Unsupported architecture."
|
||||
# endif
|
||||
#else // !_LIBUNWIND_IS_NATIVE_ONLY
|
||||
# define _LIBUNWIND_TARGET_I386
|
||||
# define _LIBUNWIND_TARGET_X86_64 1
|
||||
# define _LIBUNWIND_TARGET_PPC 1
|
||||
# define _LIBUNWIND_TARGET_PPC64 1
|
||||
# define _LIBUNWIND_TARGET_AARCH64 1
|
||||
# define _LIBUNWIND_TARGET_ARM 1
|
||||
# define _LIBUNWIND_TARGET_OR1K 1
|
||||
# define _LIBUNWIND_TARGET_MIPS_O32 1
|
||||
# define _LIBUNWIND_TARGET_MIPS_NEWABI 1
|
||||
# define _LIBUNWIND_TARGET_SPARC 1
|
||||
# define _LIBUNWIND_TARGET_SPARC64 1
|
||||
# define _LIBUNWIND_TARGET_HEXAGON 1
|
||||
# define _LIBUNWIND_TARGET_RISCV 1
|
||||
# define _LIBUNWIND_TARGET_VE 1
|
||||
# define _LIBUNWIND_TARGET_S390X 1
|
||||
#define _LIBUNWIND_TARGET_LOONGARCH 1
|
||||
# define _LIBUNWIND_CONTEXT_SIZE 167
|
||||
# define _LIBUNWIND_CURSOR_SIZE 204
|
||||
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287
|
||||
#endif // _LIBUNWIND_IS_NATIVE_ONLY
|
||||
|
||||
#endif // ____LIBUNWIND_CONFIG_H__
|
1300
third_party/libunwind/include/libunwind.h
vendored
Normal file
1300
third_party/libunwind/include/libunwind.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
477
third_party/libunwind/include/mach-o/compact_unwind_encoding.h
vendored
Normal file
477
third_party/libunwind/include/mach-o/compact_unwind_encoding.h
vendored
Normal file
|
@ -0,0 +1,477 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Darwin's alternative to DWARF based unwind encodings.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#ifndef __COMPACT_UNWIND_ENCODING__
|
||||
#define __COMPACT_UNWIND_ENCODING__
|
||||
|
||||
#include "libc/isystem/stdint.h"
|
||||
|
||||
//
|
||||
// Compilers can emit standard DWARF FDEs in the __TEXT,__eh_frame section
|
||||
// of object files. Or compilers can emit compact unwind information in
|
||||
// the __LD,__compact_unwind section.
|
||||
//
|
||||
// When the linker creates a final linked image, it will create a
|
||||
// __TEXT,__unwind_info section. This section is a small and fast way for the
|
||||
// runtime to access unwind info for any given function. If the compiler
|
||||
// emitted compact unwind info for the function, that compact unwind info will
|
||||
// be encoded in the __TEXT,__unwind_info section. If the compiler emitted
|
||||
// DWARF unwind info, the __TEXT,__unwind_info section will contain the offset
|
||||
// of the FDE in the __TEXT,__eh_frame section in the final linked image.
|
||||
//
|
||||
// Note: Previously, the linker would transform some DWARF unwind infos into
|
||||
// compact unwind info. But that is fragile and no longer done.
|
||||
|
||||
|
||||
//
|
||||
// The compact unwind encoding is a 32-bit value which encoded in an
|
||||
// architecture specific way, which registers to restore from where, and how
|
||||
// to unwind out of the function.
|
||||
//
|
||||
typedef uint32_t compact_unwind_encoding_t;
|
||||
|
||||
|
||||
// architecture independent bits
|
||||
enum {
|
||||
UNWIND_IS_NOT_FUNCTION_START = 0x80000000,
|
||||
UNWIND_HAS_LSDA = 0x40000000,
|
||||
UNWIND_PERSONALITY_MASK = 0x30000000,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// x86
|
||||
//
|
||||
// 1-bit: start
|
||||
// 1-bit: has lsda
|
||||
// 2-bit: personality index
|
||||
//
|
||||
// 4-bits: 0=old, 1=ebp based, 2=stack-imm, 3=stack-ind, 4=DWARF
|
||||
// ebp based:
|
||||
// 15-bits (5*3-bits per reg) register permutation
|
||||
// 8-bits for stack offset
|
||||
// frameless:
|
||||
// 8-bits stack size
|
||||
// 3-bits stack adjust
|
||||
// 3-bits register count
|
||||
// 10-bits register permutation
|
||||
//
|
||||
enum {
|
||||
UNWIND_X86_MODE_MASK = 0x0F000000,
|
||||
UNWIND_X86_MODE_EBP_FRAME = 0x01000000,
|
||||
UNWIND_X86_MODE_STACK_IMMD = 0x02000000,
|
||||
UNWIND_X86_MODE_STACK_IND = 0x03000000,
|
||||
UNWIND_X86_MODE_DWARF = 0x04000000,
|
||||
|
||||
UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF,
|
||||
UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000,
|
||||
|
||||
UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000,
|
||||
UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000,
|
||||
UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
|
||||
UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
|
||||
|
||||
UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||
};
|
||||
|
||||
enum {
|
||||
UNWIND_X86_REG_NONE = 0,
|
||||
UNWIND_X86_REG_EBX = 1,
|
||||
UNWIND_X86_REG_ECX = 2,
|
||||
UNWIND_X86_REG_EDX = 3,
|
||||
UNWIND_X86_REG_EDI = 4,
|
||||
UNWIND_X86_REG_ESI = 5,
|
||||
UNWIND_X86_REG_EBP = 6,
|
||||
};
|
||||
|
||||
//
|
||||
// For x86 there are four modes for the compact unwind encoding:
|
||||
// UNWIND_X86_MODE_EBP_FRAME:
|
||||
// EBP based frame where EBP is push on stack immediately after return address,
|
||||
// then ESP is moved to EBP. Thus, to unwind ESP is restored with the current
|
||||
// EPB value, then EBP is restored by popping off the stack, and the return
|
||||
// is done by popping the stack once more into the pc.
|
||||
// All non-volatile registers that need to be restored must have been saved
|
||||
// in a small range in the stack that starts EBP-4 to EBP-1020. The offset/4
|
||||
// is encoded in the UNWIND_X86_EBP_FRAME_OFFSET bits. The registers saved
|
||||
// are encoded in the UNWIND_X86_EBP_FRAME_REGISTERS bits as five 3-bit entries.
|
||||
// Each entry contains which register to restore.
|
||||
// UNWIND_X86_MODE_STACK_IMMD:
|
||||
// A "frameless" (EBP not used as frame pointer) function with a small
|
||||
// constant stack size. To return, a constant (encoded in the compact
|
||||
// unwind encoding) is added to the ESP. Then the return is done by
|
||||
// popping the stack into the pc.
|
||||
// All non-volatile registers that need to be restored must have been saved
|
||||
// on the stack immediately after the return address. The stack_size/4 is
|
||||
// encoded in the UNWIND_X86_FRAMELESS_STACK_SIZE (max stack size is 1024).
|
||||
// The number of registers saved is encoded in UNWIND_X86_FRAMELESS_STACK_REG_COUNT.
|
||||
// UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION contains which registers were
|
||||
// saved and their order.
|
||||
// UNWIND_X86_MODE_STACK_IND:
|
||||
// A "frameless" (EBP not used as frame pointer) function large constant
|
||||
// stack size. This case is like the previous, except the stack size is too
|
||||
// large to encode in the compact unwind encoding. Instead it requires that
|
||||
// the function contains "subl $nnnnnnnn,ESP" in its prolog. The compact
|
||||
// encoding contains the offset to the nnnnnnnn value in the function in
|
||||
// UNWIND_X86_FRAMELESS_STACK_SIZE.
|
||||
// UNWIND_X86_MODE_DWARF:
|
||||
// No compact unwind encoding is available. Instead the low 24-bits of the
|
||||
// compact encoding is the offset of the DWARF FDE in the __eh_frame section.
|
||||
// This mode is never used in object files. It is only generated by the
|
||||
// linker in final linked images which have only DWARF unwind info for a
|
||||
// function.
|
||||
//
|
||||
// The permutation encoding is a Lehmer code sequence encoded into a
|
||||
// single variable-base number so we can encode the ordering of up to
|
||||
// six registers in a 10-bit space.
|
||||
//
|
||||
// The following is the algorithm used to create the permutation encoding used
|
||||
// with frameless stacks. It is passed the number of registers to be saved and
|
||||
// an array of the register numbers saved.
|
||||
//
|
||||
//uint32_t permute_encode(uint32_t registerCount, const uint32_t registers[6])
|
||||
//{
|
||||
// uint32_t renumregs[6];
|
||||
// for (int i=6-registerCount; i < 6; ++i) {
|
||||
// int countless = 0;
|
||||
// for (int j=6-registerCount; j < i; ++j) {
|
||||
// if ( registers[j] < registers[i] )
|
||||
// ++countless;
|
||||
// }
|
||||
// renumregs[i] = registers[i] - countless -1;
|
||||
// }
|
||||
// uint32_t permutationEncoding = 0;
|
||||
// switch ( registerCount ) {
|
||||
// case 6:
|
||||
// permutationEncoding |= (120*renumregs[0] + 24*renumregs[1]
|
||||
// + 6*renumregs[2] + 2*renumregs[3]
|
||||
// + renumregs[4]);
|
||||
// break;
|
||||
// case 5:
|
||||
// permutationEncoding |= (120*renumregs[1] + 24*renumregs[2]
|
||||
// + 6*renumregs[3] + 2*renumregs[4]
|
||||
// + renumregs[5]);
|
||||
// break;
|
||||
// case 4:
|
||||
// permutationEncoding |= (60*renumregs[2] + 12*renumregs[3]
|
||||
// + 3*renumregs[4] + renumregs[5]);
|
||||
// break;
|
||||
// case 3:
|
||||
// permutationEncoding |= (20*renumregs[3] + 4*renumregs[4]
|
||||
// + renumregs[5]);
|
||||
// break;
|
||||
// case 2:
|
||||
// permutationEncoding |= (5*renumregs[4] + renumregs[5]);
|
||||
// break;
|
||||
// case 1:
|
||||
// permutationEncoding |= (renumregs[5]);
|
||||
// break;
|
||||
// }
|
||||
// return permutationEncoding;
|
||||
//}
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// x86_64
|
||||
//
|
||||
// 1-bit: start
|
||||
// 1-bit: has lsda
|
||||
// 2-bit: personality index
|
||||
//
|
||||
// 4-bits: 0=old, 1=rbp based, 2=stack-imm, 3=stack-ind, 4=DWARF
|
||||
// rbp based:
|
||||
// 15-bits (5*3-bits per reg) register permutation
|
||||
// 8-bits for stack offset
|
||||
// frameless:
|
||||
// 8-bits stack size
|
||||
// 3-bits stack adjust
|
||||
// 3-bits register count
|
||||
// 10-bits register permutation
|
||||
//
|
||||
enum {
|
||||
UNWIND_X86_64_MODE_MASK = 0x0F000000,
|
||||
UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000,
|
||||
UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000,
|
||||
UNWIND_X86_64_MODE_STACK_IND = 0x03000000,
|
||||
UNWIND_X86_64_MODE_DWARF = 0x04000000,
|
||||
|
||||
UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF,
|
||||
UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000,
|
||||
|
||||
UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000,
|
||||
UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000,
|
||||
UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00,
|
||||
UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF,
|
||||
|
||||
UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||
};
|
||||
|
||||
enum {
|
||||
UNWIND_X86_64_REG_NONE = 0,
|
||||
UNWIND_X86_64_REG_RBX = 1,
|
||||
UNWIND_X86_64_REG_R12 = 2,
|
||||
UNWIND_X86_64_REG_R13 = 3,
|
||||
UNWIND_X86_64_REG_R14 = 4,
|
||||
UNWIND_X86_64_REG_R15 = 5,
|
||||
UNWIND_X86_64_REG_RBP = 6,
|
||||
};
|
||||
//
|
||||
// For x86_64 there are four modes for the compact unwind encoding:
|
||||
// UNWIND_X86_64_MODE_RBP_FRAME:
|
||||
// RBP based frame where RBP is push on stack immediately after return address,
|
||||
// then RSP is moved to RBP. Thus, to unwind RSP is restored with the current
|
||||
// EPB value, then RBP is restored by popping off the stack, and the return
|
||||
// is done by popping the stack once more into the pc.
|
||||
// All non-volatile registers that need to be restored must have been saved
|
||||
// in a small range in the stack that starts RBP-8 to RBP-2040. The offset/8
|
||||
// is encoded in the UNWIND_X86_64_RBP_FRAME_OFFSET bits. The registers saved
|
||||
// are encoded in the UNWIND_X86_64_RBP_FRAME_REGISTERS bits as five 3-bit entries.
|
||||
// Each entry contains which register to restore.
|
||||
// UNWIND_X86_64_MODE_STACK_IMMD:
|
||||
// A "frameless" (RBP not used as frame pointer) function with a small
|
||||
// constant stack size. To return, a constant (encoded in the compact
|
||||
// unwind encoding) is added to the RSP. Then the return is done by
|
||||
// popping the stack into the pc.
|
||||
// All non-volatile registers that need to be restored must have been saved
|
||||
// on the stack immediately after the return address. The stack_size/8 is
|
||||
// encoded in the UNWIND_X86_64_FRAMELESS_STACK_SIZE (max stack size is 2048).
|
||||
// The number of registers saved is encoded in UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT.
|
||||
// UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION contains which registers were
|
||||
// saved and their order.
|
||||
// UNWIND_X86_64_MODE_STACK_IND:
|
||||
// A "frameless" (RBP not used as frame pointer) function large constant
|
||||
// stack size. This case is like the previous, except the stack size is too
|
||||
// large to encode in the compact unwind encoding. Instead it requires that
|
||||
// the function contains "subq $nnnnnnnn,RSP" in its prolog. The compact
|
||||
// encoding contains the offset to the nnnnnnnn value in the function in
|
||||
// UNWIND_X86_64_FRAMELESS_STACK_SIZE.
|
||||
// UNWIND_X86_64_MODE_DWARF:
|
||||
// No compact unwind encoding is available. Instead the low 24-bits of the
|
||||
// compact encoding is the offset of the DWARF FDE in the __eh_frame section.
|
||||
// This mode is never used in object files. It is only generated by the
|
||||
// linker in final linked images which have only DWARF unwind info for a
|
||||
// function.
|
||||
//
|
||||
|
||||
|
||||
// ARM64
|
||||
//
|
||||
// 1-bit: start
|
||||
// 1-bit: has lsda
|
||||
// 2-bit: personality index
|
||||
//
|
||||
// 4-bits: 4=frame-based, 3=DWARF, 2=frameless
|
||||
// frameless:
|
||||
// 12-bits of stack size
|
||||
// frame-based:
|
||||
// 4-bits D reg pairs saved
|
||||
// 5-bits X reg pairs saved
|
||||
// DWARF:
|
||||
// 24-bits offset of DWARF FDE in __eh_frame section
|
||||
//
|
||||
enum {
|
||||
UNWIND_ARM64_MODE_MASK = 0x0F000000,
|
||||
UNWIND_ARM64_MODE_FRAMELESS = 0x02000000,
|
||||
UNWIND_ARM64_MODE_DWARF = 0x03000000,
|
||||
UNWIND_ARM64_MODE_FRAME = 0x04000000,
|
||||
|
||||
UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001,
|
||||
UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002,
|
||||
UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
|
||||
UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
|
||||
UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
|
||||
UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
|
||||
UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
|
||||
UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
|
||||
UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
|
||||
|
||||
UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
|
||||
UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||
};
|
||||
// For arm64 there are three modes for the compact unwind encoding:
|
||||
// UNWIND_ARM64_MODE_FRAME:
|
||||
// This is a standard arm64 prolog where FP/LR are immediately pushed on the
|
||||
// stack, then SP is copied to FP. If there are any non-volatile registers
|
||||
// saved, then are copied into the stack frame in pairs in a contiguous
|
||||
// range right below the saved FP/LR pair. Any subset of the five X pairs
|
||||
// and four D pairs can be saved, but the memory layout must be in register
|
||||
// number order.
|
||||
// UNWIND_ARM64_MODE_FRAMELESS:
|
||||
// A "frameless" leaf function, where FP/LR are not saved. The return address
|
||||
// remains in LR throughout the function. If any non-volatile registers
|
||||
// are saved, they must be pushed onto the stack before any stack space is
|
||||
// allocated for local variables. The stack sized (including any saved
|
||||
// non-volatile registers) divided by 16 is encoded in the bits
|
||||
// UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK.
|
||||
// UNWIND_ARM64_MODE_DWARF:
|
||||
// No compact unwind encoding is available. Instead the low 24-bits of the
|
||||
// compact encoding is the offset of the DWARF FDE in the __eh_frame section.
|
||||
// This mode is never used in object files. It is only generated by the
|
||||
// linker in final linked images which have only DWARF unwind info for a
|
||||
// function.
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Relocatable Object Files: __LD,__compact_unwind
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// A compiler can generated compact unwind information for a function by adding
|
||||
// a "row" to the __LD,__compact_unwind section. This section has the
|
||||
// S_ATTR_DEBUG bit set, so the section will be ignored by older linkers.
|
||||
// It is removed by the new linker, so never ends up in final executables.
|
||||
// This section is a table, initially with one row per function (that needs
|
||||
// unwind info). The table columns and some conceptual entries are:
|
||||
//
|
||||
// range-start pointer to start of function/range
|
||||
// range-length
|
||||
// compact-unwind-encoding 32-bit encoding
|
||||
// personality-function or zero if no personality function
|
||||
// lsda or zero if no LSDA data
|
||||
//
|
||||
// The length and encoding fields are 32-bits. The other are all pointer sized.
|
||||
//
|
||||
// In x86_64 assembly, these entry would look like:
|
||||
//
|
||||
// .section __LD,__compact_unwind,regular,debug
|
||||
//
|
||||
// #compact unwind for _foo
|
||||
// .quad _foo
|
||||
// .set L1,LfooEnd-_foo
|
||||
// .long L1
|
||||
// .long 0x01010001
|
||||
// .quad 0
|
||||
// .quad 0
|
||||
//
|
||||
// #compact unwind for _bar
|
||||
// .quad _bar
|
||||
// .set L2,LbarEnd-_bar
|
||||
// .long L2
|
||||
// .long 0x01020011
|
||||
// .quad __gxx_personality
|
||||
// .quad except_tab1
|
||||
//
|
||||
//
|
||||
// Notes: There is no need for any labels in the the __compact_unwind section.
|
||||
// The use of the .set directive is to force the evaluation of the
|
||||
// range-length at assembly time, instead of generating relocations.
|
||||
//
|
||||
// To support future compiler optimizations where which non-volatile registers
|
||||
// are saved changes within a function (e.g. delay saving non-volatiles until
|
||||
// necessary), there can by multiple lines in the __compact_unwind table for one
|
||||
// function, each with a different (non-overlapping) range and each with
|
||||
// different compact unwind encodings that correspond to the non-volatiles
|
||||
// saved at that range of the function.
|
||||
//
|
||||
// If a particular function is so wacky that there is no compact unwind way
|
||||
// to encode it, then the compiler can emit traditional DWARF unwind info.
|
||||
// The runtime will use which ever is available.
|
||||
//
|
||||
// Runtime support for compact unwind encodings are only available on 10.6
|
||||
// and later. So, the compiler should not generate it when targeting pre-10.6.
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Final Linked Images: __TEXT,__unwind_info
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// The __TEXT,__unwind_info section is laid out for an efficient two level lookup.
|
||||
// The header of the section contains a coarse index that maps function address
|
||||
// to the page (4096 byte block) containing the unwind info for that function.
|
||||
//
|
||||
|
||||
#define UNWIND_SECTION_VERSION 1
|
||||
struct unwind_info_section_header
|
||||
{
|
||||
uint32_t version; // UNWIND_SECTION_VERSION
|
||||
uint32_t commonEncodingsArraySectionOffset;
|
||||
uint32_t commonEncodingsArrayCount;
|
||||
uint32_t personalityArraySectionOffset;
|
||||
uint32_t personalityArrayCount;
|
||||
uint32_t indexSectionOffset;
|
||||
uint32_t indexCount;
|
||||
// compact_unwind_encoding_t[]
|
||||
// uint32_t personalities[]
|
||||
// unwind_info_section_header_index_entry[]
|
||||
// unwind_info_section_header_lsda_index_entry[]
|
||||
};
|
||||
|
||||
struct unwind_info_section_header_index_entry
|
||||
{
|
||||
uint32_t functionOffset;
|
||||
uint32_t secondLevelPagesSectionOffset; // section offset to start of regular or compress page
|
||||
uint32_t lsdaIndexArraySectionOffset; // section offset to start of lsda_index array for this range
|
||||
};
|
||||
|
||||
struct unwind_info_section_header_lsda_index_entry
|
||||
{
|
||||
uint32_t functionOffset;
|
||||
uint32_t lsdaOffset;
|
||||
};
|
||||
|
||||
//
|
||||
// There are two kinds of second level index pages: regular and compressed.
|
||||
// A compressed page can hold up to 1021 entries, but it cannot be used
|
||||
// if too many different encoding types are used. The regular page holds
|
||||
// 511 entries.
|
||||
//
|
||||
|
||||
struct unwind_info_regular_second_level_entry
|
||||
{
|
||||
uint32_t functionOffset;
|
||||
compact_unwind_encoding_t encoding;
|
||||
};
|
||||
|
||||
#define UNWIND_SECOND_LEVEL_REGULAR 2
|
||||
struct unwind_info_regular_second_level_page_header
|
||||
{
|
||||
uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR
|
||||
uint16_t entryPageOffset;
|
||||
uint16_t entryCount;
|
||||
// entry array
|
||||
};
|
||||
|
||||
#define UNWIND_SECOND_LEVEL_COMPRESSED 3
|
||||
struct unwind_info_compressed_second_level_page_header
|
||||
{
|
||||
uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED
|
||||
uint16_t entryPageOffset;
|
||||
uint16_t entryCount;
|
||||
uint16_t encodingsPageOffset;
|
||||
uint16_t encodingsCount;
|
||||
// 32-bit entry array
|
||||
// encodings array
|
||||
};
|
||||
|
||||
#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF)
|
||||
#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) ((entry >> 24) & 0xFF)
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
207
third_party/libunwind/include/unwind.h
vendored
Normal file
207
third_party/libunwind/include/unwind.h
vendored
Normal file
|
@ -0,0 +1,207 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// C++ ABI Level 1 ABI documented at:
|
||||
// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __UNWIND_H__
|
||||
#define __UNWIND_H__
|
||||
|
||||
#include "third_party/libunwind/include/__libunwind_config.h"
|
||||
|
||||
#include "libc/isystem/stdint.h"
|
||||
#include "libc/isystem/stddef.h"
|
||||
|
||||
#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) && defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <ntverp.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define LIBUNWIND_UNAVAIL __attribute__ (( unavailable ))
|
||||
#else
|
||||
#define LIBUNWIND_UNAVAIL
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
_URC_NO_REASON = 0,
|
||||
_URC_OK = 0,
|
||||
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
|
||||
_URC_FATAL_PHASE2_ERROR = 2,
|
||||
_URC_FATAL_PHASE1_ERROR = 3,
|
||||
_URC_NORMAL_STOP = 4,
|
||||
_URC_END_OF_STACK = 5,
|
||||
_URC_HANDLER_FOUND = 6,
|
||||
_URC_INSTALL_CONTEXT = 7,
|
||||
_URC_CONTINUE_UNWIND = 8,
|
||||
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||
_URC_FAILURE = 9
|
||||
#endif
|
||||
} _Unwind_Reason_Code;
|
||||
|
||||
typedef enum {
|
||||
_UA_SEARCH_PHASE = 1,
|
||||
_UA_CLEANUP_PHASE = 2,
|
||||
_UA_HANDLER_FRAME = 4,
|
||||
_UA_FORCE_UNWIND = 8,
|
||||
_UA_END_OF_STACK = 16 // gcc extension to C++ ABI
|
||||
} _Unwind_Action;
|
||||
|
||||
typedef struct _Unwind_Context _Unwind_Context; // opaque
|
||||
|
||||
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||
#include "third_party/libunwind/include/unwind_arm_ehabi.h"
|
||||
#else
|
||||
#include "third_party/libunwind/include/unwind_itanium.h"
|
||||
#endif
|
||||
|
||||
typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
|
||||
(int version,
|
||||
_Unwind_Action actions,
|
||||
_Unwind_Exception_Class exceptionClass,
|
||||
_Unwind_Exception* exceptionObject,
|
||||
struct _Unwind_Context* context,
|
||||
void* stop_parameter);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *context);
|
||||
extern uintptr_t
|
||||
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context);
|
||||
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_SjLj_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter);
|
||||
#else
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
|
||||
_Unwind_Stop_Fn stop, void *stop_parameter);
|
||||
#endif
|
||||
|
||||
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||
typedef struct _Unwind_FunctionContext *_Unwind_FunctionContext_t;
|
||||
extern void _Unwind_SjLj_Register(_Unwind_FunctionContext_t fc);
|
||||
extern void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t fc);
|
||||
#endif
|
||||
|
||||
//
|
||||
// The following are semi-supported extensions to the C++ ABI
|
||||
//
|
||||
|
||||
//
|
||||
// called by __cxa_rethrow().
|
||||
//
|
||||
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_SjLj_Resume_or_Rethrow(_Unwind_Exception *exception_object);
|
||||
#else
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object);
|
||||
#endif
|
||||
|
||||
// _Unwind_Backtrace() is a gcc extension that walks the stack and calls the
|
||||
// _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack
|
||||
// or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON.
|
||||
typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *,
|
||||
void *);
|
||||
extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
|
||||
|
||||
// _Unwind_GetCFA is a gcc extension that can be called from within a
|
||||
// personality handler to get the CFA (stack pointer before call) of
|
||||
// current frame.
|
||||
extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context *);
|
||||
|
||||
|
||||
// _Unwind_GetIPInfo is a gcc extension that can be called from within a
|
||||
// personality handler. Similar to _Unwind_GetIP() but also returns in
|
||||
// *ipBefore a non-zero value if the instruction pointer is at or before the
|
||||
// instruction causing the unwind. Normally, in a function call, the IP returned
|
||||
// is the return address which is after the call instruction and may be past the
|
||||
// end of the function containing the call instruction.
|
||||
extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
|
||||
int *ipBefore);
|
||||
|
||||
|
||||
// __register_frame() is used with dynamically generated code to register the
|
||||
// FDE for a generated (JIT) code. The FDE must use pc-rel addressing to point
|
||||
// to its function and optional LSDA.
|
||||
// __register_frame() has existed in all versions of Mac OS X, but in 10.4 and
|
||||
// 10.5 it was buggy and did not actually register the FDE with the unwinder.
|
||||
// In 10.6 and later it does register properly.
|
||||
extern void __register_frame(const void *fde);
|
||||
extern void __deregister_frame(const void *fde);
|
||||
|
||||
// _Unwind_Find_FDE() will locate the FDE if the pc is in some function that has
|
||||
// an associated FDE. Note, Mac OS X 10.6 and later, introduces "compact unwind
|
||||
// info" which the runtime uses in preference to DWARF unwind info. This
|
||||
// function will only work if the target function has an FDE but no compact
|
||||
// unwind info.
|
||||
struct dwarf_eh_bases {
|
||||
uintptr_t tbase;
|
||||
uintptr_t dbase;
|
||||
uintptr_t func;
|
||||
};
|
||||
extern const void *_Unwind_Find_FDE(const void *pc, struct dwarf_eh_bases *);
|
||||
|
||||
|
||||
// This function attempts to find the start (address of first instruction) of
|
||||
// a function given an address inside the function. It only works if the
|
||||
// function has an FDE (DWARF unwind info).
|
||||
// This function is unimplemented on Mac OS X 10.6 and later. Instead, use
|
||||
// _Unwind_Find_FDE() and look at the dwarf_eh_bases.func result.
|
||||
extern void *_Unwind_FindEnclosingFunction(void *pc);
|
||||
|
||||
// Mac OS X does not support text-rel and data-rel addressing so these functions
|
||||
// are unimplemented.
|
||||
extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *context)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
|
||||
// Mac OS X 10.4 and 10.5 had implementations of these functions in
|
||||
// libgcc_s.dylib, but they never worked.
|
||||
/// These functions are no longer available on Mac OS X.
|
||||
extern void __register_frame_info_bases(const void *fde, void *ob, void *tb,
|
||||
void *db) LIBUNWIND_UNAVAIL;
|
||||
extern void __register_frame_info(const void *fde, void *ob)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void __register_frame_info_table_bases(const void *fde, void *ob,
|
||||
void *tb, void *db)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void __register_frame_info_table(const void *fde, void *ob)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void __register_frame_table(const void *fde)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void *__deregister_frame_info(const void *fde)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
extern void *__deregister_frame_info_bases(const void *fde)
|
||||
LIBUNWIND_UNAVAIL;
|
||||
|
||||
#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||
#ifndef _WIN32
|
||||
typedef struct _EXCEPTION_RECORD EXCEPTION_RECORD;
|
||||
typedef struct _CONTEXT CONTEXT;
|
||||
typedef struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT;
|
||||
#elif !defined(__MINGW32__) && VER_PRODUCTBUILD < 8000
|
||||
typedef struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT;
|
||||
#endif
|
||||
// This is the common wrapper for GCC-style personality functions with SEH.
|
||||
extern EXCEPTION_DISPOSITION _GCC_specific_handler(EXCEPTION_RECORD *exc,
|
||||
void *frame, CONTEXT *ctx,
|
||||
DISPATCHER_CONTEXT *disp,
|
||||
_Unwind_Personality_Fn pers);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __UNWIND_H__
|
170
third_party/libunwind/include/unwind_arm_ehabi.h
vendored
Normal file
170
third_party/libunwind/include/unwind_arm_ehabi.h
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// C++ ABI Level 1 ABI documented at:
|
||||
// https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __ARM_EHABI_UNWIND_H__
|
||||
#define __ARM_EHABI_UNWIND_H__
|
||||
|
||||
typedef uint32_t _Unwind_State;
|
||||
|
||||
static const _Unwind_State _US_VIRTUAL_UNWIND_FRAME = 0;
|
||||
static const _Unwind_State _US_UNWIND_FRAME_STARTING = 1;
|
||||
static const _Unwind_State _US_UNWIND_FRAME_RESUME = 2;
|
||||
static const _Unwind_State _US_ACTION_MASK = 3;
|
||||
/* Undocumented flag for force unwinding. */
|
||||
static const _Unwind_State _US_FORCE_UNWIND = 8;
|
||||
|
||||
typedef uint32_t _Unwind_EHT_Header;
|
||||
|
||||
struct _Unwind_Control_Block;
|
||||
typedef struct _Unwind_Control_Block _Unwind_Control_Block;
|
||||
#define _Unwind_Exception _Unwind_Control_Block /* Alias */
|
||||
typedef uint8_t _Unwind_Exception_Class[8];
|
||||
|
||||
struct _Unwind_Control_Block {
|
||||
_Unwind_Exception_Class exception_class;
|
||||
void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block*);
|
||||
|
||||
/* Unwinder cache, private fields for the unwinder's use */
|
||||
struct {
|
||||
uint32_t reserved1; /* init reserved1 to 0, then don't touch */
|
||||
uint32_t reserved2;
|
||||
uint32_t reserved3;
|
||||
uint32_t reserved4;
|
||||
uint32_t reserved5;
|
||||
} unwinder_cache;
|
||||
|
||||
/* Propagation barrier cache (valid after phase 1): */
|
||||
struct {
|
||||
uint32_t sp;
|
||||
uint32_t bitpattern[5];
|
||||
} barrier_cache;
|
||||
|
||||
/* Cleanup cache (preserved over cleanup): */
|
||||
struct {
|
||||
uint32_t bitpattern[4];
|
||||
} cleanup_cache;
|
||||
|
||||
/* Pr cache (for pr's benefit): */
|
||||
struct {
|
||||
uint32_t fnstart; /* function start address */
|
||||
_Unwind_EHT_Header* ehtp; /* pointer to EHT entry header word */
|
||||
uint32_t additional;
|
||||
uint32_t reserved1;
|
||||
} pr_cache;
|
||||
|
||||
long long int :0; /* Enforce the 8-byte alignment */
|
||||
} __attribute__((__aligned__(8)));
|
||||
|
||||
typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)(
|
||||
_Unwind_State state, _Unwind_Exception *exceptionObject,
|
||||
struct _Unwind_Context *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//
|
||||
// The following are the base functions documented by the C++ ABI
|
||||
//
|
||||
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_SjLj_RaiseException(_Unwind_Exception *exception_object);
|
||||
extern void _Unwind_SjLj_Resume(_Unwind_Exception *exception_object);
|
||||
#else
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_RaiseException(_Unwind_Exception *exception_object);
|
||||
extern void _Unwind_Resume(_Unwind_Exception *exception_object);
|
||||
#endif
|
||||
extern void _Unwind_DeleteException(_Unwind_Exception *exception_object);
|
||||
|
||||
typedef enum {
|
||||
_UVRSC_CORE = 0, /* integer register */
|
||||
_UVRSC_VFP = 1, /* vfp */
|
||||
_UVRSC_WMMXD = 3, /* Intel WMMX data register */
|
||||
_UVRSC_WMMXC = 4, /* Intel WMMX control register */
|
||||
_UVRSC_PSEUDO = 5 /* Special purpose pseudo register */
|
||||
} _Unwind_VRS_RegClass;
|
||||
|
||||
typedef enum {
|
||||
_UVRSD_UINT32 = 0,
|
||||
_UVRSD_VFPX = 1,
|
||||
_UVRSD_UINT64 = 3,
|
||||
_UVRSD_FLOAT = 4,
|
||||
_UVRSD_DOUBLE = 5
|
||||
} _Unwind_VRS_DataRepresentation;
|
||||
|
||||
typedef enum {
|
||||
_UVRSR_OK = 0,
|
||||
_UVRSR_NOT_IMPLEMENTED = 1,
|
||||
_UVRSR_FAILED = 2
|
||||
} _Unwind_VRS_Result;
|
||||
|
||||
extern void _Unwind_Complete(_Unwind_Exception* exception_object);
|
||||
|
||||
extern _Unwind_VRS_Result
|
||||
_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||
uint32_t regno, _Unwind_VRS_DataRepresentation representation,
|
||||
void *valuep);
|
||||
|
||||
extern _Unwind_VRS_Result
|
||||
_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||
uint32_t regno, _Unwind_VRS_DataRepresentation representation,
|
||||
void *valuep);
|
||||
|
||||
extern _Unwind_VRS_Result
|
||||
_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass,
|
||||
uint32_t discriminator,
|
||||
_Unwind_VRS_DataRepresentation representation);
|
||||
|
||||
#if defined(_LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE)
|
||||
#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 extern
|
||||
#else
|
||||
#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 static __inline__
|
||||
#endif
|
||||
|
||||
// These are de facto helper functions for ARM, which delegate the function
|
||||
// calls to _Unwind_VRS_Get/Set(). These are not a part of ARM EHABI
|
||||
// specification, thus these function MUST be inlined. Please don't replace
|
||||
// these with the "extern" function declaration; otherwise, the program
|
||||
// including this <unwind.h> header won't be ABI compatible and will result in
|
||||
// link error when we are linking the program with libgcc.
|
||||
|
||||
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||
uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index) {
|
||||
uintptr_t value = 0;
|
||||
_Unwind_VRS_Get(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||
void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||
uintptr_t value) {
|
||||
_Unwind_VRS_Set(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value);
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||
uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
|
||||
// remove the thumb-bit before returning
|
||||
return _Unwind_GetGR(context, 15) & (~(uintptr_t)0x1);
|
||||
}
|
||||
|
||||
_LIBUNWIND_EXPORT_UNWIND_LEVEL1
|
||||
void _Unwind_SetIP(struct _Unwind_Context *context, uintptr_t value) {
|
||||
uintptr_t thumb_bit = _Unwind_GetGR(context, 15) & ((uintptr_t)0x1);
|
||||
_Unwind_SetGR(context, 15, value | thumb_bit);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __ARM_EHABI_UNWIND_H__
|
76
third_party/libunwind/include/unwind_itanium.h
vendored
Normal file
76
third_party/libunwind/include/unwind_itanium.h
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// C++ ABI Level 1 ABI documented at:
|
||||
// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __ITANIUM_UNWIND_H__
|
||||
#define __ITANIUM_UNWIND_H__
|
||||
|
||||
struct _Unwind_Context; // opaque
|
||||
struct _Unwind_Exception; // forward declaration
|
||||
typedef struct _Unwind_Exception _Unwind_Exception;
|
||||
typedef uint64_t _Unwind_Exception_Class;
|
||||
|
||||
struct _Unwind_Exception {
|
||||
_Unwind_Exception_Class exception_class;
|
||||
void (*exception_cleanup)(_Unwind_Reason_Code reason,
|
||||
_Unwind_Exception *exc);
|
||||
#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||
uintptr_t private_[6];
|
||||
#else
|
||||
uintptr_t private_1; // non-zero means forced unwind
|
||||
uintptr_t private_2; // holds sp that phase1 found for phase2 to use
|
||||
#endif
|
||||
#if __SIZEOF_POINTER__ == 4
|
||||
// The implementation of _Unwind_Exception uses an attribute mode on the
|
||||
// above fields which has the side effect of causing this whole struct to
|
||||
// round up to 32 bytes in size (48 with SEH). To be more explicit, we add
|
||||
// pad fields added for binary compatibility.
|
||||
uint32_t reserved[3];
|
||||
#endif
|
||||
// The Itanium ABI requires that _Unwind_Exception objects are "double-word
|
||||
// aligned". GCC has interpreted this to mean "use the maximum useful
|
||||
// alignment for the target"; so do we.
|
||||
} __attribute__((__aligned__));
|
||||
|
||||
typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)(
|
||||
int version, _Unwind_Action actions, uint64_t exceptionClass,
|
||||
_Unwind_Exception *exceptionObject, struct _Unwind_Context *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//
|
||||
// The following are the base functions documented by the C++ ABI
|
||||
//
|
||||
#ifdef __USING_SJLJ_EXCEPTIONS__
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_SjLj_RaiseException(_Unwind_Exception *exception_object);
|
||||
extern void _Unwind_SjLj_Resume(_Unwind_Exception *exception_object);
|
||||
#else
|
||||
extern _Unwind_Reason_Code
|
||||
_Unwind_RaiseException(_Unwind_Exception *exception_object);
|
||||
extern void _Unwind_Resume(_Unwind_Exception *exception_object);
|
||||
#endif
|
||||
extern void _Unwind_DeleteException(_Unwind_Exception *exception_object);
|
||||
|
||||
|
||||
extern uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index);
|
||||
extern void _Unwind_SetGR(struct _Unwind_Context *context, int index,
|
||||
uintptr_t new_value);
|
||||
extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *context);
|
||||
extern void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t new_value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __ITANIUM_UNWIND_H__
|
475
third_party/libunwind/libunwind.cc
vendored
Normal file
475
third_party/libunwind/libunwind.cc
vendored
Normal file
|
@ -0,0 +1,475 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Implements unw_* functions from <libunwind.h>
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "third_party/libunwind/include/libunwind.h"
|
||||
|
||||
#include "third_party/libunwind/config.h"
|
||||
#include "third_party/libunwind/libunwind_ext.h"
|
||||
|
||||
#include "libc/isystem/stdlib.h"
|
||||
|
||||
// Define the __has_feature extension for compilers that do not support it so
|
||||
// that we can later check for the presence of ASan in a compiler-neutral way.
|
||||
#if !defined(__has_feature)
|
||||
#define __has_feature(feature) 0
|
||||
#endif
|
||||
|
||||
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
#if !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||
#include "third_party/libunwind/AddressSpace.hpp"
|
||||
#include "third_party/libunwind/UnwindCursor.hpp"
|
||||
|
||||
using namespace libunwind;
|
||||
|
||||
/// internal object to represent this processes address space
|
||||
LocalAddressSpace LocalAddressSpace::sThisAddressSpace;
|
||||
|
||||
_LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space =
|
||||
(unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace;
|
||||
|
||||
/// Create a cursor of a thread in this process given 'context' recorded by
|
||||
/// __unw_getcontext().
|
||||
_LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor,
|
||||
unw_context_t *context) {
|
||||
_LIBUNWIND_TRACE_API("__unw_init_local(cursor=%p, context=%p)",
|
||||
static_cast<void *>(cursor),
|
||||
static_cast<void *>(context));
|
||||
#if defined(__i386__)
|
||||
# define REGISTER_KIND Registers_x86
|
||||
#elif defined(__x86_64__)
|
||||
# define REGISTER_KIND Registers_x86_64
|
||||
#elif defined(__powerpc64__)
|
||||
# define REGISTER_KIND Registers_ppc64
|
||||
#elif defined(__powerpc__)
|
||||
# define REGISTER_KIND Registers_ppc
|
||||
#elif defined(__aarch64__)
|
||||
# define REGISTER_KIND Registers_arm64
|
||||
#elif defined(__arm__)
|
||||
# define REGISTER_KIND Registers_arm
|
||||
#elif defined(__or1k__)
|
||||
# define REGISTER_KIND Registers_or1k
|
||||
#elif defined(__hexagon__)
|
||||
# define REGISTER_KIND Registers_hexagon
|
||||
#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32
|
||||
# define REGISTER_KIND Registers_mips_o32
|
||||
#elif defined(__mips64)
|
||||
# define REGISTER_KIND Registers_mips_newabi
|
||||
#elif defined(__mips__)
|
||||
# warning The MIPS architecture is not supported with this ABI and environment!
|
||||
#elif defined(__sparc__) && defined(__arch64__)
|
||||
#define REGISTER_KIND Registers_sparc64
|
||||
#elif defined(__sparc__)
|
||||
# define REGISTER_KIND Registers_sparc
|
||||
#elif defined(__riscv)
|
||||
# define REGISTER_KIND Registers_riscv
|
||||
#elif defined(__ve__)
|
||||
# define REGISTER_KIND Registers_ve
|
||||
#elif defined(__s390x__)
|
||||
# define REGISTER_KIND Registers_s390x
|
||||
#elif defined(__loongarch__) && __loongarch_grlen == 64
|
||||
#define REGISTER_KIND Registers_loongarch
|
||||
#else
|
||||
# error Architecture not supported
|
||||
#endif
|
||||
// Use "placement new" to allocate UnwindCursor in the cursor buffer.
|
||||
new (reinterpret_cast<UnwindCursor<LocalAddressSpace, REGISTER_KIND> *>(cursor))
|
||||
UnwindCursor<LocalAddressSpace, REGISTER_KIND>(
|
||||
context, LocalAddressSpace::sThisAddressSpace);
|
||||
#undef REGISTER_KIND
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
co->setInfoBasedOnIPRegister();
|
||||
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_init_local, unw_init_local)
|
||||
|
||||
/// Get value of specified register at cursor position in stack frame.
|
||||
_LIBUNWIND_HIDDEN int __unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||
unw_word_t *value) {
|
||||
_LIBUNWIND_TRACE_API("__unw_get_reg(cursor=%p, regNum=%d, &value=%p)",
|
||||
static_cast<void *>(cursor), regNum,
|
||||
static_cast<void *>(value));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->validReg(regNum)) {
|
||||
*value = co->getReg(regNum);
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
return UNW_EBADREG;
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_get_reg, unw_get_reg)
|
||||
|
||||
/// Set value of specified register at cursor position in stack frame.
|
||||
_LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||
unw_word_t value) {
|
||||
_LIBUNWIND_TRACE_API("__unw_set_reg(cursor=%p, regNum=%d, value=0x%" PRIxPTR
|
||||
")",
|
||||
static_cast<void *>(cursor), regNum, value);
|
||||
typedef LocalAddressSpace::pint_t pint_t;
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->validReg(regNum)) {
|
||||
co->setReg(regNum, (pint_t)value);
|
||||
// special case altering IP to re-find info (being called by personality
|
||||
// function)
|
||||
if (regNum == UNW_REG_IP) {
|
||||
unw_proc_info_t info;
|
||||
// First, get the FDE for the old location and then update it.
|
||||
co->getInfo(&info);
|
||||
co->setInfoBasedOnIPRegister(false);
|
||||
// If the original call expects stack adjustment, perform this now.
|
||||
// Normal frame unwinding would have included the offset already in the
|
||||
// CFA computation.
|
||||
// Note: for PA-RISC and other platforms where the stack grows up,
|
||||
// this should actually be - info.gp. LLVM doesn't currently support
|
||||
// any such platforms and Clang doesn't export a macro for them.
|
||||
if (info.gp)
|
||||
co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + info.gp);
|
||||
}
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
return UNW_EBADREG;
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_set_reg, unw_set_reg)
|
||||
|
||||
/// Get value of specified float register at cursor position in stack frame.
|
||||
_LIBUNWIND_HIDDEN int __unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||
unw_fpreg_t *value) {
|
||||
_LIBUNWIND_TRACE_API("__unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)",
|
||||
static_cast<void *>(cursor), regNum,
|
||||
static_cast<void *>(value));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->validFloatReg(regNum)) {
|
||||
*value = co->getFloatReg(regNum);
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
return UNW_EBADREG;
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_get_fpreg, unw_get_fpreg)
|
||||
|
||||
/// Set value of specified float register at cursor position in stack frame.
|
||||
_LIBUNWIND_HIDDEN int __unw_set_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum,
|
||||
unw_fpreg_t value) {
|
||||
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||
_LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%llX)",
|
||||
static_cast<void *>(cursor), regNum, value);
|
||||
#else
|
||||
_LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%g)",
|
||||
static_cast<void *>(cursor), regNum, value);
|
||||
#endif
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->validFloatReg(regNum)) {
|
||||
co->setFloatReg(regNum, value);
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
return UNW_EBADREG;
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_set_fpreg, unw_set_fpreg)
|
||||
|
||||
/// Move cursor to next frame.
|
||||
_LIBUNWIND_HIDDEN int __unw_step(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("__unw_step(cursor=%p)", static_cast<void *>(cursor));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->step();
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_step, unw_step)
|
||||
|
||||
// Move cursor to next frame and for stage2 of unwinding.
|
||||
// This resets MTE tags of tagged frames to zero.
|
||||
extern "C" _LIBUNWIND_HIDDEN int __unw_step_stage2(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("__unw_step_stage2(cursor=%p)",
|
||||
static_cast<void *>(cursor));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->step(true);
|
||||
}
|
||||
|
||||
/// Get unwind info at cursor position in stack frame.
|
||||
_LIBUNWIND_HIDDEN int __unw_get_proc_info(unw_cursor_t *cursor,
|
||||
unw_proc_info_t *info) {
|
||||
_LIBUNWIND_TRACE_API("__unw_get_proc_info(cursor=%p, &info=%p)",
|
||||
static_cast<void *>(cursor), static_cast<void *>(info));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
co->getInfo(info);
|
||||
if (info->end_ip == 0)
|
||||
return UNW_ENOINFO;
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_info, unw_get_proc_info)
|
||||
|
||||
/// Resume execution at cursor position (aka longjump).
|
||||
_LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("__unw_resume(cursor=%p)", static_cast<void *>(cursor));
|
||||
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
// Inform the ASan runtime that now might be a good time to clean stuff up.
|
||||
__asan_handle_no_return();
|
||||
#endif
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
co->jumpto();
|
||||
return UNW_EUNSPEC;
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_resume, unw_resume)
|
||||
|
||||
/// Get name of function at cursor position in stack frame.
|
||||
_LIBUNWIND_HIDDEN int __unw_get_proc_name(unw_cursor_t *cursor, char *buf,
|
||||
size_t bufLen, unw_word_t *offset) {
|
||||
_LIBUNWIND_TRACE_API("__unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%lu)",
|
||||
static_cast<void *>(cursor), static_cast<void *>(buf),
|
||||
static_cast<unsigned long>(bufLen));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
if (co->getFunctionName(buf, bufLen, offset))
|
||||
return UNW_ESUCCESS;
|
||||
return UNW_EUNSPEC;
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_name, unw_get_proc_name)
|
||||
|
||||
/// Checks if a register is a floating-point register.
|
||||
_LIBUNWIND_HIDDEN int __unw_is_fpreg(unw_cursor_t *cursor,
|
||||
unw_regnum_t regNum) {
|
||||
_LIBUNWIND_TRACE_API("__unw_is_fpreg(cursor=%p, regNum=%d)",
|
||||
static_cast<void *>(cursor), regNum);
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->validFloatReg(regNum);
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_is_fpreg, unw_is_fpreg)
|
||||
|
||||
/// Checks if a register is a floating-point register.
|
||||
_LIBUNWIND_HIDDEN const char *__unw_regname(unw_cursor_t *cursor,
|
||||
unw_regnum_t regNum) {
|
||||
_LIBUNWIND_TRACE_API("__unw_regname(cursor=%p, regNum=%d)",
|
||||
static_cast<void *>(cursor), regNum);
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->getRegisterName(regNum);
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_regname, unw_regname)
|
||||
|
||||
/// Checks if current frame is signal trampoline.
|
||||
_LIBUNWIND_HIDDEN int __unw_is_signal_frame(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("__unw_is_signal_frame(cursor=%p)",
|
||||
static_cast<void *>(cursor));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->isSignalFrame();
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_is_signal_frame, unw_is_signal_frame)
|
||||
|
||||
#ifdef _AIX
|
||||
_LIBUNWIND_EXPORT uintptr_t __unw_get_data_rel_base(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("unw_get_data_rel_base(cursor=%p)",
|
||||
static_cast<void *>(cursor));
|
||||
AbstractUnwindCursor *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);
|
||||
return co->getDataRelBase();
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_get_data_rel_base, unw_get_data_rel_base)
|
||||
#endif
|
||||
|
||||
#ifdef __arm__
|
||||
// Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD
|
||||
_LIBUNWIND_HIDDEN void __unw_save_vfp_as_X(unw_cursor_t *cursor) {
|
||||
_LIBUNWIND_TRACE_API("__unw_get_fpreg_save_vfp_as_X(cursor=%p)",
|
||||
static_cast<void *>(cursor));
|
||||
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
|
||||
return co->saveVFPAsX();
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_save_vfp_as_X, unw_save_vfp_as_X)
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||
/// SPI: walks cached DWARF entries
|
||||
_LIBUNWIND_HIDDEN void __unw_iterate_dwarf_unwind_cache(void (*func)(
|
||||
unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) {
|
||||
_LIBUNWIND_TRACE_API("__unw_iterate_dwarf_unwind_cache(func=%p)",
|
||||
reinterpret_cast<void *>(func));
|
||||
DwarfFDECache<LocalAddressSpace>::iterateCacheEntries(func);
|
||||
}
|
||||
_LIBUNWIND_WEAK_ALIAS(__unw_iterate_dwarf_unwind_cache,
|
||||
unw_iterate_dwarf_unwind_cache)
|
||||
|
||||
/// IPI: for __register_frame()
|
||||
void __unw_add_dynamic_fde(unw_word_t fde) {
|
||||
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
|
||||
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
|
||||
const char *message = CFI_Parser<LocalAddressSpace>::decodeFDE(
|
||||
LocalAddressSpace::sThisAddressSpace,
|
||||
(LocalAddressSpace::pint_t) fde, &fdeInfo, &cieInfo);
|
||||
if (message == NULL) {
|
||||
// dynamically registered FDEs don't have a mach_header group they are in.
|
||||
// Use fde as mh_group
|
||||
unw_word_t mh_group = fdeInfo.fdeStart;
|
||||
DwarfFDECache<LocalAddressSpace>::add((LocalAddressSpace::pint_t)mh_group,
|
||||
fdeInfo.pcStart, fdeInfo.pcEnd,
|
||||
fdeInfo.fdeStart);
|
||||
} else {
|
||||
_LIBUNWIND_DEBUG_LOG("__unw_add_dynamic_fde: bad fde: %s", message);
|
||||
}
|
||||
}
|
||||
|
||||
/// IPI: for __deregister_frame()
|
||||
void __unw_remove_dynamic_fde(unw_word_t fde) {
|
||||
// fde is own mh_group
|
||||
DwarfFDECache<LocalAddressSpace>::removeAllIn((LocalAddressSpace::pint_t)fde);
|
||||
}
|
||||
|
||||
void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start) {
|
||||
// The eh_frame section start serves as the mh_group
|
||||
unw_word_t mh_group = eh_frame_start;
|
||||
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
|
||||
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
|
||||
auto p = (LocalAddressSpace::pint_t)eh_frame_start;
|
||||
while (true) {
|
||||
if (CFI_Parser<LocalAddressSpace>::decodeFDE(
|
||||
LocalAddressSpace::sThisAddressSpace, p, &fdeInfo, &cieInfo,
|
||||
true) == NULL) {
|
||||
DwarfFDECache<LocalAddressSpace>::add((LocalAddressSpace::pint_t)mh_group,
|
||||
fdeInfo.pcStart, fdeInfo.pcEnd,
|
||||
fdeInfo.fdeStart);
|
||||
p += fdeInfo.fdeLength;
|
||||
} else if (CFI_Parser<LocalAddressSpace>::parseCIE(
|
||||
LocalAddressSpace::sThisAddressSpace, p, &cieInfo) == NULL) {
|
||||
p += cieInfo.cieLength;
|
||||
} else
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void __unw_remove_dynamic_eh_frame_section(unw_word_t eh_frame_start) {
|
||||
// The eh_frame section start serves as the mh_group
|
||||
DwarfFDECache<LocalAddressSpace>::removeAllIn(
|
||||
(LocalAddressSpace::pint_t)eh_frame_start);
|
||||
}
|
||||
|
||||
#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
|
||||
#endif // !defined(__USING_SJLJ_EXCEPTIONS__)
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
namespace libunwind {
|
||||
|
||||
static constexpr size_t MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS = 8;
|
||||
|
||||
static RWMutex findDynamicUnwindSectionsLock;
|
||||
static size_t numDynamicUnwindSectionsFinders = 0;
|
||||
static unw_find_dynamic_unwind_sections
|
||||
dynamicUnwindSectionsFinders[MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS] = {0};
|
||||
|
||||
bool findDynamicUnwindSections(void *addr, unw_dynamic_unwind_sections *info) {
|
||||
bool found = false;
|
||||
findDynamicUnwindSectionsLock.lock_shared();
|
||||
for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) {
|
||||
if (dynamicUnwindSectionsFinders[i]((unw_word_t)addr, info)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
findDynamicUnwindSectionsLock.unlock_shared();
|
||||
return found;
|
||||
}
|
||||
|
||||
} // namespace libunwind
|
||||
|
||||
int __unw_add_find_dynamic_unwind_sections(
|
||||
unw_find_dynamic_unwind_sections find_dynamic_unwind_sections) {
|
||||
findDynamicUnwindSectionsLock.lock();
|
||||
|
||||
// Check that we have enough space...
|
||||
if (numDynamicUnwindSectionsFinders == MAX_DYNAMIC_UNWIND_SECTIONS_FINDERS) {
|
||||
findDynamicUnwindSectionsLock.unlock();
|
||||
return UNW_ENOMEM;
|
||||
}
|
||||
|
||||
// Check for value already present...
|
||||
for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) {
|
||||
if (dynamicUnwindSectionsFinders[i] == find_dynamic_unwind_sections) {
|
||||
findDynamicUnwindSectionsLock.unlock();
|
||||
return UNW_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
// Success -- add callback entry.
|
||||
dynamicUnwindSectionsFinders[numDynamicUnwindSectionsFinders++] =
|
||||
find_dynamic_unwind_sections;
|
||||
findDynamicUnwindSectionsLock.unlock();
|
||||
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
|
||||
int __unw_remove_find_dynamic_unwind_sections(
|
||||
unw_find_dynamic_unwind_sections find_dynamic_unwind_sections) {
|
||||
findDynamicUnwindSectionsLock.lock();
|
||||
|
||||
// Find index to remove.
|
||||
size_t finderIdx = numDynamicUnwindSectionsFinders;
|
||||
for (size_t i = 0; i != numDynamicUnwindSectionsFinders; ++i) {
|
||||
if (dynamicUnwindSectionsFinders[i] == find_dynamic_unwind_sections) {
|
||||
finderIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no such registration is present then error out.
|
||||
if (finderIdx == numDynamicUnwindSectionsFinders) {
|
||||
findDynamicUnwindSectionsLock.unlock();
|
||||
return UNW_EINVAL;
|
||||
}
|
||||
|
||||
// Remove entry.
|
||||
for (size_t i = finderIdx; i != numDynamicUnwindSectionsFinders - 1; ++i)
|
||||
dynamicUnwindSectionsFinders[i] = dynamicUnwindSectionsFinders[i + 1];
|
||||
dynamicUnwindSectionsFinders[--numDynamicUnwindSectionsFinders] = nullptr;
|
||||
|
||||
findDynamicUnwindSectionsLock.unlock();
|
||||
return UNW_ESUCCESS;
|
||||
}
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
// Add logging hooks in Debug builds only
|
||||
#ifndef NDEBUG
|
||||
#include "libc/isystem/stdlib.h"
|
||||
|
||||
_LIBUNWIND_HIDDEN
|
||||
bool logAPIs() {
|
||||
// do manual lock to avoid use of _cxa_guard_acquire or initializers
|
||||
static bool checked = false;
|
||||
static bool log = false;
|
||||
if (!checked) {
|
||||
log = (getenv("LIBUNWIND_PRINT_APIS") != NULL);
|
||||
checked = true;
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
_LIBUNWIND_HIDDEN
|
||||
bool logUnwinding() {
|
||||
// do manual lock to avoid use of _cxa_guard_acquire or initializers
|
||||
static bool checked = false;
|
||||
static bool log = false;
|
||||
if (!checked) {
|
||||
log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL);
|
||||
checked = true;
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
_LIBUNWIND_HIDDEN
|
||||
bool logDWARF() {
|
||||
// do manual lock to avoid use of _cxa_guard_acquire or initializers
|
||||
static bool checked = false;
|
||||
static bool log = false;
|
||||
if (!checked) {
|
||||
log = (getenv("LIBUNWIND_PRINT_DWARF") != NULL);
|
||||
checked = true;
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
#endif // NDEBUG
|
||||
|
137
third_party/libunwind/libunwind_ext.h
vendored
Normal file
137
third_party/libunwind/libunwind_ext.h
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//
|
||||
// Extensions to libunwind API.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef __LIBUNWIND_EXT__
|
||||
#define __LIBUNWIND_EXT__
|
||||
|
||||
#include "third_party/libunwind/config.h"
|
||||
#include "third_party/libunwind/include/libunwind.h"
|
||||
#include "third_party/libunwind/include/unwind.h"
|
||||
|
||||
#define UNW_STEP_SUCCESS 1
|
||||
#define UNW_STEP_END 0
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int __unw_getcontext(unw_context_t *);
|
||||
extern int __unw_init_local(unw_cursor_t *, unw_context_t *);
|
||||
extern int __unw_step(unw_cursor_t *);
|
||||
extern int __unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *);
|
||||
extern int __unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *);
|
||||
extern int __unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t);
|
||||
extern int __unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t);
|
||||
extern int __unw_resume(unw_cursor_t *);
|
||||
|
||||
#ifdef __arm__
|
||||
/* Save VFP registers in FSTMX format (instead of FSTMD). */
|
||||
extern void __unw_save_vfp_as_X(unw_cursor_t *);
|
||||
#endif
|
||||
|
||||
extern const char *__unw_regname(unw_cursor_t *, unw_regnum_t);
|
||||
extern int __unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *);
|
||||
extern int __unw_is_fpreg(unw_cursor_t *, unw_regnum_t);
|
||||
extern int __unw_is_signal_frame(unw_cursor_t *);
|
||||
extern int __unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *);
|
||||
|
||||
#if defined(_AIX)
|
||||
extern uintptr_t __unw_get_data_rel_base(unw_cursor_t *);
|
||||
#endif
|
||||
|
||||
// SPI
|
||||
extern void __unw_iterate_dwarf_unwind_cache(void (*func)(
|
||||
unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh));
|
||||
|
||||
// IPI
|
||||
extern void __unw_add_dynamic_fde(unw_word_t fde);
|
||||
extern void __unw_remove_dynamic_fde(unw_word_t fde);
|
||||
|
||||
extern void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start);
|
||||
extern void __unw_remove_dynamic_eh_frame_section(unw_word_t eh_frame_start);
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
// Holds a description of the object-format-header (if any) and unwind info
|
||||
// sections for a given address:
|
||||
//
|
||||
// * dso_base should point to a header for the JIT'd object containing the
|
||||
// given address. The header's type should match the format type that
|
||||
// libunwind was compiled for (so a mach_header or mach_header_64 on Darwin).
|
||||
// A value of zero indicates that no such header exists.
|
||||
//
|
||||
// * dwarf_section and dwarf_section_length hold the address range of a DWARF
|
||||
// eh-frame section associated with the given address, if any. If the
|
||||
// dwarf_section_length field is zero it indicates that no such section
|
||||
// exists (and in this case dwarf_section should also be set to zero).
|
||||
//
|
||||
// * compact_unwind_section and compact_unwind_section_length hold the address
|
||||
// range of a compact-unwind info section associated with the given address,
|
||||
// if any. If the compact_unwind_section_length field is zero it indicates
|
||||
// that no such section exists (and in this case compact_unwind_section
|
||||
// should also be set to zero).
|
||||
//
|
||||
// See the unw_find_dynamic_unwind_sections type below for more details.
|
||||
struct unw_dynamic_unwind_sections {
|
||||
unw_word_t dso_base;
|
||||
unw_word_t dwarf_section;
|
||||
size_t dwarf_section_length;
|
||||
unw_word_t compact_unwind_section;
|
||||
size_t compact_unwind_section_length;
|
||||
};
|
||||
|
||||
// Typedef for unwind-info lookup callbacks. Functions of this type can be
|
||||
// registered and deregistered using __unw_add_find_dynamic_unwind_sections
|
||||
// and __unw_remove_find_dynamic_unwind_sections respectively.
|
||||
//
|
||||
// An unwind-info lookup callback should return 1 to indicate that it found
|
||||
// unwind-info for the given address, or 0 to indicate that it did not find
|
||||
// unwind-info for the given address. If found, the callback should populate
|
||||
// some or all of the fields of the info argument (which is guaranteed to be
|
||||
// non-null with all fields zero-initialized):
|
||||
typedef int (*unw_find_dynamic_unwind_sections)(
|
||||
unw_word_t addr, struct unw_dynamic_unwind_sections *info);
|
||||
|
||||
// Register a dynamic unwind-info lookup callback. If libunwind does not find
|
||||
// unwind info for a given frame in the executable program or normal dynamic
|
||||
// shared objects then it will call all registered dynamic lookup functions
|
||||
// in registration order until either one of them returns true, or the end
|
||||
// of the list is reached. This lookup will happen before libunwind searches
|
||||
// any eh-frames registered via __register_frame or
|
||||
// __unw_add_dynamic_eh_frame_section.
|
||||
//
|
||||
// Returns UNW_ESUCCESS for successful registrations. If the given callback
|
||||
// has already been registered then UNW_EINVAL will be returned. If all
|
||||
// available callback entries are in use then UNW_ENOMEM will be returned.
|
||||
extern int __unw_add_find_dynamic_unwind_sections(
|
||||
unw_find_dynamic_unwind_sections find_dynamic_unwind_sections);
|
||||
|
||||
// Deregister a dynacim unwind-info lookup callback.
|
||||
//
|
||||
// Returns UNW_ESUCCESS for successful deregistrations. If the given callback
|
||||
// has already been registered then UNW_EINVAL will be returned.
|
||||
extern int __unw_remove_find_dynamic_unwind_sections(
|
||||
unw_find_dynamic_unwind_sections find_dynamic_unwind_sections);
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_LIBUNWIND_ARM_EHABI)
|
||||
extern const uint32_t* decode_eht_entry(const uint32_t*, size_t*, size_t*);
|
||||
extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context,
|
||||
const uint32_t *data,
|
||||
size_t offset, size_t len);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __LIBUNWIND_EXT__
|
Loading…
Reference in a new issue