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:
Trung Nguyen 2024-01-06 15:04:30 +07:00 committed by GitHub
parent 91de6f1f5d
commit b09096691a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 17372 additions and 0 deletions

View file

@ -328,6 +328,7 @@ include third_party/python/BUILD.mk
include tool/build/BUILD.mk include tool/build/BUILD.mk
include tool/curl/BUILD.mk include tool/curl/BUILD.mk
include third_party/qemu/BUILD.mk include third_party/qemu/BUILD.mk
include third_party/libunwind/BUILD.mk
include examples/BUILD.mk include examples/BUILD.mk
include examples/pyapp/BUILD.mk include examples/pyapp/BUILD.mk
include examples/pylife/BUILD.mk include examples/pylife/BUILD.mk

View file

@ -17,6 +17,7 @@ o/$(MODE)/third_party: \
o/$(MODE)/third_party/hiredis \ o/$(MODE)/third_party/hiredis \
o/$(MODE)/third_party/less \ o/$(MODE)/third_party/less \
o/$(MODE)/third_party/libcxx \ o/$(MODE)/third_party/libcxx \
o/$(MODE)/third_party/libunwind \
o/$(MODE)/third_party/linenoise \ o/$(MODE)/third_party/linenoise \
o/$(MODE)/third_party/lua \ o/$(MODE)/third_party/lua \
o/$(MODE)/third_party/lz4cli \ o/$(MODE)/third_party/lz4cli \

697
third_party/libunwind/AddressSpace.hpp vendored Normal file
View 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
View 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)

View 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 &registers);
private:
typename A::pint_t pint_t;
static void frameUnwind(A &addressSpace, Registers_x86 &registers);
static void framelessUnwind(A &addressSpace,
typename A::pint_t returnAddressLocation,
Registers_x86 &registers);
static int
stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding,
uint32_t functionStart, A &addressSpace,
Registers_x86 &registers);
static int stepWithCompactEncodingFrameless(
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
A &addressSpace, Registers_x86 &registers, bool indirectStackSize);
};
template <typename A>
int CompactUnwinder_x86<A>::stepWithCompactEncoding(
compact_unwind_encoding_t compactEncoding, uint32_t functionStart,
A &addressSpace, Registers_x86 &registers) {
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 &registers) {
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 &registers, 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 &registers) {
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 &registers) {
// 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 &registers);
private:
typename A::pint_t pint_t;
static void frameUnwind(A &addressSpace, Registers_x86_64 &registers);
static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation,
Registers_x86_64 &registers);
static int
stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding,
uint64_t functionStart, A &addressSpace,
Registers_x86_64 &registers);
static int stepWithCompactEncodingFrameless(
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
A &addressSpace, Registers_x86_64 &registers, bool indirectStackSize);
};
template <typename A>
int CompactUnwinder_x86_64<A>::stepWithCompactEncoding(
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
A &addressSpace, Registers_x86_64 &registers) {
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 &registers) {
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 &registers, 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 &registers) {
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 &registers) {
// 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 &registers);
private:
typename A::pint_t pint_t;
static int
stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding,
uint64_t functionStart, A &addressSpace,
Registers_arm64 &registers);
static int stepWithCompactEncodingFrameless(
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
A &addressSpace, Registers_arm64 &registers);
};
template <typename A>
int CompactUnwinder_arm64<A>::stepWithCompactEncoding(
compact_unwind_encoding_t compactEncoding, uint64_t functionStart,
A &addressSpace, Registers_arm64 &registers) {
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 &registers) {
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 &registers) {
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__

View 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 &registers, 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 &registers,
pint_t initialStackValue);
static pint_t getSavedRegister(A &addressSpace, const R &registers,
pint_t cfa, const RegisterLocation &savedReg);
static double getSavedFloatRegister(A &addressSpace, const R &registers,
pint_t cfa, const RegisterLocation &savedReg);
static v128 getSavedVectorRegister(A &addressSpace, const R &registers,
pint_t cfa, const RegisterLocation &savedReg);
static pint_t getCFA(A &addressSpace, const PrologInfo &prolog,
const R &registers) {
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 &registers, 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 &registers, 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 &registers, 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 &registers,
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 &registers,
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
View 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
View 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

View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

50
third_party/libunwind/Unwind-EHABI.h vendored Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

View 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
View 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
View 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
View 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
View 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

View 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

File diff suppressed because it is too large Load diff

View 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
View 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__

View 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__

View 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
View 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
View 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__