diff --git a/Makefile b/Makefile index ae1acb034..d60e01db6 100644 --- a/Makefile +++ b/Makefile @@ -328,6 +328,7 @@ include third_party/python/BUILD.mk include tool/build/BUILD.mk include tool/curl/BUILD.mk include third_party/qemu/BUILD.mk +include third_party/libunwind/BUILD.mk include examples/BUILD.mk include examples/pyapp/BUILD.mk include examples/pylife/BUILD.mk diff --git a/third_party/BUILD.mk b/third_party/BUILD.mk index aad7c82e7..680e0d42b 100644 --- a/third_party/BUILD.mk +++ b/third_party/BUILD.mk @@ -17,6 +17,7 @@ o/$(MODE)/third_party: \ o/$(MODE)/third_party/hiredis \ o/$(MODE)/third_party/less \ o/$(MODE)/third_party/libcxx \ + o/$(MODE)/third_party/libunwind \ o/$(MODE)/third_party/linenoise \ o/$(MODE)/third_party/lua \ o/$(MODE)/third_party/lz4cli \ diff --git a/third_party/libunwind/AddressSpace.hpp b/third_party/libunwind/AddressSpace.hpp new file mode 100644 index 000000000..f7c627141 --- /dev/null +++ b/third_party/libunwind/AddressSpace.hpp @@ -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 +#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 +#include + +#elif defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) || \ + defined(_LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX) + +#include + +#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 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::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::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(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( + 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(findResult.dlfo_map_start); + info.text_segment_length = static_cast( + (char *)findResult.dlfo_map_end - (char *)findResult.dlfo_map_start); + + // Record the start of PT_GNU_EH_FRAME. + info.dwarf_index_section = + reinterpret_cast(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::EHHeaderInfo hdrInfo; + if (!EHHeaderParser::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(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__ diff --git a/third_party/libunwind/BUILD.mk b/third_party/libunwind/BUILD.mk new file mode 100644 index 000000000..abd0ac86c --- /dev/null +++ b/third_party/libunwind/BUILD.mk @@ -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) diff --git a/third_party/libunwind/CompactUnwinder.hpp b/third_party/libunwind/CompactUnwinder.hpp new file mode 100644 index 000000000..21596d61e --- /dev/null +++ b/third_party/libunwind/CompactUnwinder.hpp @@ -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 +class CompactUnwinder_x86 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t info, + uint32_t functionStart, A &addressSpace, + Registers_x86 ®isters); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A &addressSpace, Registers_x86 ®isters); + static void framelessUnwind(A &addressSpace, + typename A::pint_t returnAddressLocation, + Registers_x86 ®isters); + static int + stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, + uint32_t functionStart, A &addressSpace, + Registers_x86 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters, bool indirectStackSize); +}; + +template +int CompactUnwinder_x86::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters) { + switch (compactEncoding & UNWIND_X86_MODE_MASK) { + case UNWIND_X86_MODE_EBP_FRAME: + return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_X86_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, false); + case UNWIND_X86_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, true); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template +int CompactUnwinder_x86::stepWithCompactEncodingEBPFrame( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters) { + uint32_t savedRegistersOffset = + EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = + EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS); + + uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset; + for (int i = 0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + default: + (void)functionStart; + _LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for " + "function starting at 0x%X", + compactEncoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_x86::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters, bool indirectStackSize) { + uint32_t stackSizeEncoded = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + uint32_t regCount = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded * 4; + if (indirectStackSize) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); + stackSize = subl + 4 * stackAdjust; + } + // decompress permutation + uint32_t permunreg[6]; + switch (regCount) { + case 6: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < regCount; ++i) { + uint32_t renum = 0; + for (int u = 1; u < 7; ++u) { + if (!used[u]) { + if (renum == permunreg[i]) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount; + for (uint32_t i = 0; i < regCount; ++i) { + switch (registersSaved[i]) { + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EBP: + registers.setEBP(addressSpace.get32(savedRegisters)); + break; + default: + _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " + "function starting at 0x%X", + encoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +template +void CompactUnwinder_x86::frameUnwind(A &addressSpace, + Registers_x86 ®isters) { + typename A::pint_t bp = registers.getEBP(); + // ebp points to old ebp + registers.setEBP(addressSpace.get32(bp)); + // old esp is ebp less saved ebp and return address + registers.setSP((uint32_t)bp + 8); + // pop return address into eip + registers.setIP(addressSpace.get32(bp + 4)); +} + +template +void CompactUnwinder_x86::framelessUnwind( + A &addressSpace, typename A::pint_t returnAddressLocation, + Registers_x86 ®isters) { + // return address is on stack after last saved register + registers.setIP(addressSpace.get32(returnAddressLocation)); + // old esp is before return address + registers.setSP((uint32_t)returnAddressLocation + 4); +} +#endif // _LIBUNWIND_TARGET_I386 + + +#if defined(_LIBUNWIND_TARGET_X86_64) +/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_x86_64 register set +template +class CompactUnwinder_x86_64 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A &addressSpace, Registers_x86_64 ®isters); + static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation, + Registers_x86_64 ®isters); + static int + stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters, bool indirectStackSize); +}; + +template +int CompactUnwinder_x86_64::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters) { + switch (compactEncoding & UNWIND_X86_64_MODE_MASK) { + case UNWIND_X86_64_MODE_RBP_FRAME: + return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_X86_64_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, false); + case UNWIND_X86_64_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, true); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template +int CompactUnwinder_x86_64::stepWithCompactEncodingRBPFrame( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters) { + uint32_t savedRegistersOffset = + EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = + EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + + uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset; + for (int i = 0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_64_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters)); + break; + default: + (void)functionStart; + _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for " + "function starting at 0x%llX", + compactEncoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_x86_64::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters, bool indirectStackSize) { + uint32_t stackSizeEncoded = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + uint32_t regCount = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded * 8; + if (indirectStackSize) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); + stackSize = subl + 8 * stackAdjust; + } + // decompress permutation + uint32_t permunreg[6]; + switch (regCount) { + case 6: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < regCount; ++i) { + uint32_t renum = 0; + for (int u = 1; u < 7; ++u) { + if (!used[u]) { + if (renum == permunreg[i]) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount; + for (uint32_t i = 0; i < regCount; ++i) { + switch (registersSaved[i]) { + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_RBP: + registers.setRBP(addressSpace.get64(savedRegisters)); + break; + default: + _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " + "function starting at 0x%llX", + encoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +template +void CompactUnwinder_x86_64::frameUnwind(A &addressSpace, + Registers_x86_64 ®isters) { + uint64_t rbp = registers.getRBP(); + // ebp points to old ebp + registers.setRBP(addressSpace.get64(rbp)); + // old esp is ebp less saved ebp and return address + registers.setSP(rbp + 16); + // pop return address into eip + registers.setIP(addressSpace.get64(rbp + 8)); +} + +template +void CompactUnwinder_x86_64::framelessUnwind(A &addressSpace, + uint64_t returnAddressLocation, + Registers_x86_64 ®isters) { + // return address is on stack after last saved register + registers.setIP(addressSpace.get64(returnAddressLocation)); + // old esp is before return address + registers.setSP(returnAddressLocation + 8); +} +#endif // _LIBUNWIND_TARGET_X86_64 + + + +#if defined(_LIBUNWIND_TARGET_AARCH64) +/// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_arm64 register set +template +class CompactUnwinder_arm64 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_arm64 ®isters); + +private: + typename A::pint_t pint_t; + + static int + stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_arm64 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_arm64 ®isters); +}; + +template +int CompactUnwinder_arm64::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_arm64 ®isters) { + switch (compactEncoding & UNWIND_ARM64_MODE_MASK) { + case UNWIND_ARM64_MODE_FRAME: + return stepWithCompactEncodingFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_ARM64_MODE_FRAMELESS: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template +int CompactUnwinder_arm64::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, + Registers_arm64 ®isters) { + uint32_t stackSize = + 16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK); + + uint64_t savedRegisterLoc = registers.getSP() + stackSize; + + if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V8, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V9, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V10, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V11, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V12, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V13, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V14, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V15, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + // subtract stack size off of sp + registers.setSP(savedRegisterLoc); + + // set pc to be value in lr + registers.setIP(registers.getRegister(UNW_AARCH64_LR)); + + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_arm64::stepWithCompactEncodingFrame( + compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, + Registers_arm64 ®isters) { + uint64_t savedRegisterLoc = registers.getFP() - 8; + + if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V8, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V9, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V10, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V11, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V12, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V13, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V14, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V15, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + uint64_t fp = registers.getFP(); + // fp points to old fp + registers.setFP(addressSpace.get64(fp)); + // old sp is fp less saved fp and lr + registers.setSP(fp + 16); + // pop return address into pc + registers.setIP(addressSpace.get64(fp + 8)); + + return UNW_STEP_SUCCESS; +} +#endif // _LIBUNWIND_TARGET_AARCH64 + + +} // namespace libunwind + +#endif // __COMPACT_UNWINDER_HPP__ diff --git a/third_party/libunwind/DwarfInstructions.hpp b/third_party/libunwind/DwarfInstructions.hpp new file mode 100644 index 000000000..920a575ab --- /dev/null +++ b/third_party/libunwind/DwarfInstructions.hpp @@ -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 +class DwarfInstructions { +public: + typedef typename A::pint_t pint_t; + typedef typename A::sint_t sint_t; + + static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, + R ®isters, bool &isSignalFrame, bool stage2); + +private: + + enum { + DW_X86_64_RET_ADDR = 16 + }; + + enum { + DW_X86_RET_ADDR = 8 + }; + + typedef typename CFI_Parser::RegisterLocation RegisterLocation; + typedef typename CFI_Parser::PrologInfo PrologInfo; + typedef typename CFI_Parser::FDE_Info FDE_Info; + typedef typename CFI_Parser::CIE_Info CIE_Info; + + static pint_t evaluateExpression(pint_t expression, A &addressSpace, + const R ®isters, + pint_t initialStackValue); + static pint_t getSavedRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + static double getSavedFloatRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + static v128 getSavedVectorRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + + static pint_t getCFA(A &addressSpace, const PrologInfo &prolog, + const R ®isters) { + if (prolog.cfaRegister != 0) + return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + + prolog.cfaRegisterOffset); + if (prolog.cfaExpression != 0) + return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, + registers, 0); + assert(0 && "getCFA(): unknown location"); + __builtin_unreachable(); + } +#if defined(_LIBUNWIND_TARGET_AARCH64) + static bool getRA_SIGN_STATE(A &addressSpace, R registers, pint_t cfa, + PrologInfo &prolog); +#endif +}; + +template +auto getSparcWCookie(const R &r, int) -> decltype(r.getWCookie()) { + return r.getWCookie(); +} +template uint64_t getSparcWCookie(const R &, long) { + return 0; +} + +template +typename A::pint_t DwarfInstructions::getSavedRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return (pint_t)addressSpace.getRegister(cfa + (pint_t)savedReg.value); + + case CFI_Parser::kRegisterInCFADecrypt: // sparc64 specific + return (pint_t)(addressSpace.getP(cfa + (pint_t)savedReg.value) ^ + getSparcWCookie(registers, 0)); + + case CFI_Parser::kRegisterAtExpression: + return (pint_t)addressSpace.getRegister(evaluateExpression( + (pint_t)savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + return evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa); + + case CFI_Parser::kRegisterInRegister: + return registers.getRegister((int)savedReg.value); + case CFI_Parser::kRegisterUndefined: + return 0; + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for register"); +} + +template +double DwarfInstructions::getSavedFloatRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getDouble(cfa + (pint_t)savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getDouble( + evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa)); + case CFI_Parser::kRegisterUndefined: + return 0.0; + case CFI_Parser::kRegisterInRegister: +#ifndef _LIBUNWIND_TARGET_ARM + return registers.getFloatRegister((int)savedReg.value); +#endif + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInCFADecrypt: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for float register"); +} + +template +v128 DwarfInstructions::getSavedVectorRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getVector(cfa + (pint_t)savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getVector( + evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterUndefined: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + case CFI_Parser::kRegisterInCFADecrypt: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for vector register"); +} +#if defined(_LIBUNWIND_TARGET_AARCH64) +template +bool DwarfInstructions::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::kRegisterUnused) + raSignState = static_cast(regloc.value); + else + raSignState = getSavedRegister(addressSpace, registers, cfa, regloc); + + // Only bit[0] is meaningful. + return raSignState & 0x01; +} +#endif + +template +int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, + pint_t fdeStart, R ®isters, + bool &isSignalFrame, bool stage2) { + FDE_Info fdeInfo; + CIE_Info cieInfo; + if (CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, + &cieInfo) == NULL) { + PrologInfo prolog; + if (CFI_Parser::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(CFI_Parser::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::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::pint_t +DwarfInstructions::evaluateExpression(pint_t expression, A &addressSpace, + const R ®isters, + pint_t initialStackValue) { + const bool log = false; + pint_t p = expression; + pint_t expressionEnd = expression + 20; // temp, until len read + pint_t length = (pint_t)addressSpace.getULEB128(p, expressionEnd); + expressionEnd = p + length; + if (log) + fprintf(stderr, "evaluateExpression(): length=%" PRIu64 "\n", + (uint64_t)length); + pint_t stack[100]; + pint_t *sp = stack; + *(++sp) = initialStackValue; + + while (p < expressionEnd) { + if (log) { + for (pint_t *t = sp; t > stack; --t) { + fprintf(stderr, "sp[] = 0x%" PRIx64 "\n", (uint64_t)(*t)); + } + } + uint8_t opcode = addressSpace.get8(p++); + sint_t svalue, svalue2; + pint_t value; + uint32_t reg; + switch (opcode) { + case DW_OP_addr: + // push immediate address sized value + value = addressSpace.getP(p); + p += sizeof(pint_t); + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_deref: + // pop stack, dereference, push result + value = *sp--; + *(++sp) = addressSpace.getP(value); + if (log) + fprintf(stderr, "dereference 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const1u: + // push immediate 1 byte value + value = addressSpace.get8(p); + p += 1; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const1s: + // push immediate 1 byte signed value + svalue = (int8_t) addressSpace.get8(p); + p += 1; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_const2u: + // push immediate 2 byte value + value = addressSpace.get16(p); + p += 2; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const2s: + // push immediate 2 byte signed value + svalue = (int16_t) addressSpace.get16(p); + p += 2; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_const4u: + // push immediate 4 byte value + value = addressSpace.get32(p); + p += 4; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const4s: + // push immediate 4 byte signed value + svalue = (int32_t)addressSpace.get32(p); + p += 4; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_const8u: + // push immediate 8 byte value + value = (pint_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const8s: + // push immediate 8 byte signed value + value = (pint_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_constu: + // push immediate ULEB128 value + value = (pint_t)addressSpace.getULEB128(p, expressionEnd); + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_consts: + // push immediate SLEB128 value + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_dup: + // push top of stack + value = *sp; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate top of stack\n"); + break; + + case DW_OP_drop: + // pop + --sp; + if (log) + fprintf(stderr, "pop top of stack\n"); + break; + + case DW_OP_over: + // dup second + value = sp[-1]; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate second in stack\n"); + break; + + case DW_OP_pick: + // pick from + reg = addressSpace.get8(p); + p += 1; + value = sp[-(int)reg]; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate %d in stack\n", reg); + break; + + case DW_OP_swap: + // swap top two + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = value; + if (log) + fprintf(stderr, "swap top of stack\n"); + break; + + case DW_OP_rot: + // rotate top three + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = value; + if (log) + fprintf(stderr, "rotate top three of stack\n"); + break; + + case DW_OP_xderef: + // pop stack, dereference, push result + value = *sp--; + *sp = *((pint_t*)value); + if (log) + fprintf(stderr, "x-dereference 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_abs: + svalue = (sint_t)*sp; + if (svalue < 0) + *sp = (pint_t)(-svalue); + if (log) + fprintf(stderr, "abs\n"); + break; + + case DW_OP_and: + value = *sp--; + *sp &= value; + if (log) + fprintf(stderr, "and\n"); + break; + + case DW_OP_div: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 / svalue); + if (log) + fprintf(stderr, "div\n"); + break; + + case DW_OP_minus: + value = *sp--; + *sp = *sp - value; + if (log) + fprintf(stderr, "minus\n"); + break; + + case DW_OP_mod: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 % svalue); + if (log) + fprintf(stderr, "module\n"); + break; + + case DW_OP_mul: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 * svalue); + if (log) + fprintf(stderr, "mul\n"); + break; + + case DW_OP_neg: + *sp = 0 - *sp; + if (log) + fprintf(stderr, "neg\n"); + break; + + case DW_OP_not: + svalue = (sint_t)(*sp); + *sp = (pint_t)(~svalue); + if (log) + fprintf(stderr, "not\n"); + break; + + case DW_OP_or: + value = *sp--; + *sp |= value; + if (log) + fprintf(stderr, "or\n"); + break; + + case DW_OP_plus: + value = *sp--; + *sp += value; + if (log) + fprintf(stderr, "plus\n"); + break; + + case DW_OP_plus_uconst: + // pop stack, add uelb128 constant, push result + *sp += static_cast(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(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(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(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(opcode - DW_OP_breg0); + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + svalue += static_cast(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(addressSpace.getULEB128(p, expressionEnd)); + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + svalue += static_cast(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__ diff --git a/third_party/libunwind/DwarfParser.hpp b/third_party/libunwind/DwarfParser.hpp new file mode 100644 index 000000000..90fba0b5f --- /dev/null +++ b/third_party/libunwind/DwarfParser.hpp @@ -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 +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 +const char *CFI_Parser::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 +bool CFI_Parser::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(-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 +const char *CFI_Parser::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 +bool CFI_Parser::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(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(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(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(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; + } + // 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(~0) && "pointer overflow"); + p += static_cast(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(~0) && "pointer overflow"); + p += static_cast(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(~0) && "pointer overflow"); + p += static_cast(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((reg - UNW_SPARC_L0) * sizeof(pint_t)), + initialState); + else + results->setRegister( + reg, kRegisterInCFA, + static_cast((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(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(operand)); + break; + default: + _LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode); + return false; + } + } + } + } + return true; +} + +} // namespace libunwind + +#endif // __DWARF_PARSER_HPP__ diff --git a/third_party/libunwind/EHHeaderParser.hpp b/third_party/libunwind/EHHeaderParser.hpp new file mode 100644 index 000000000..885e8eeeb --- /dev/null +++ b/third_party/libunwind/EHHeaderParser.hpp @@ -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 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::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo); + +private: + static bool decodeTableEntry(A &addressSpace, pint_t &tableEntry, + pint_t ehHdrStart, pint_t ehHdrEnd, + uint8_t tableEnc, + typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo); + static size_t getTableEntrySize(uint8_t tableEnc); +}; + +template +bool EHHeaderParser::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(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 +bool EHHeaderParser::decodeTableEntry( + A &addressSpace, pint_t &tableEntry, pint_t ehHdrStart, pint_t ehHdrEnd, + uint8_t tableEnc, typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::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::decodeFDE(addressSpace, fde, fdeInfo, cieInfo); + if (message != NULL) { + _LIBUNWIND_DEBUG_LOG("EHHeaderParser::decodeTableEntry: bad fde: %s", + message); + return false; + } + + return true; +} + +template +bool EHHeaderParser::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart, + uint32_t sectionLength, + typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo) { + pint_t ehHdrEnd = ehHdrStart + sectionLength; + + EHHeaderParser::EHHeaderInfo hdrInfo; + if (!EHHeaderParser::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 +size_t EHHeaderParser::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 diff --git a/third_party/libunwind/FrameHeaderCache.hpp b/third_party/libunwind/FrameHeaderCache.hpp new file mode 100644 index 000000000..a24a7449d --- /dev/null +++ b/third_party/libunwind/FrameHeaderCache.hpp @@ -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(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__ diff --git a/third_party/libunwind/LICENSE.TXT b/third_party/libunwind/LICENSE.TXT new file mode 100644 index 000000000..1e3120621 --- /dev/null +++ b/third_party/libunwind/LICENSE.TXT @@ -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. diff --git a/third_party/libunwind/README.cosmo b/third_party/libunwind/README.cosmo new file mode 100644 index 000000000..3524d2a39 --- /dev/null +++ b/third_party/libunwind/README.cosmo @@ -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 + Date: Tue, 28 Nov 2023 09:52:28 +0100 + diff --git a/third_party/libunwind/RWMutex.hpp b/third_party/libunwind/RWMutex.hpp new file mode 100644 index 000000000..85505b0fe --- /dev/null +++ b/third_party/libunwind/RWMutex.hpp @@ -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 +#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__ diff --git a/third_party/libunwind/Registers.hpp b/third_party/libunwind/Registers.hpp new file mode 100644 index 000000000..60f51f9fa --- /dev/null +++ b/third_party/libunwind/Registers.hpp @@ -0,0 +1,5312 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +// +// Models register sets for supported processors. +// +//===----------------------------------------------------------------------===// + +#ifndef __REGISTERS_HPP__ +#define __REGISTERS_HPP__ + +#include "libc/isystem/stdint.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" + +namespace libunwind { + +// For emulating 128-bit registers +struct v128 { uint32_t vec[4]; }; + +enum { + REGISTERS_X86, + REGISTERS_X86_64, + REGISTERS_PPC, + REGISTERS_PPC64, + REGISTERS_ARM64, + REGISTERS_ARM, + REGISTERS_OR1K, + REGISTERS_MIPS_O32, + REGISTERS_MIPS_NEWABI, + REGISTERS_SPARC, + REGISTERS_SPARC64, + REGISTERS_HEXAGON, + REGISTERS_RISCV, + REGISTERS_VE, + REGISTERS_S390X, + REGISTERS_LOONGARCH, +}; + +#if defined(_LIBUNWIND_TARGET_I386) +class _LIBUNWIND_HIDDEN Registers_x86; +extern "C" void __libunwind_Registers_x86_jumpto(Registers_x86 *); + +#if defined(_LIBUNWIND_USE_CET) +extern "C" void *__libunwind_cet_get_jump_target() { + return reinterpret_cast(&__libunwind_Registers_x86_jumpto); +} +#endif + +/// Registers_x86 holds the register state of a thread in a 32-bit intel +/// process. +class _LIBUNWIND_HIDDEN Registers_x86 { +public: + Registers_x86(); + Registers_x86(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto() { __libunwind_Registers_x86_jumpto(this); } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86; + } + static int getArch() { return REGISTERS_X86; } + + uint32_t getSP() const { return _registers.__esp; } + void setSP(uint32_t value) { _registers.__esp = value; } + uint32_t getIP() const { return _registers.__eip; } + void setIP(uint32_t value) { _registers.__eip = value; } + uint32_t getEBP() const { return _registers.__ebp; } + void setEBP(uint32_t value) { _registers.__ebp = value; } + uint32_t getEBX() const { return _registers.__ebx; } + void setEBX(uint32_t value) { _registers.__ebx = value; } + uint32_t getECX() const { return _registers.__ecx; } + void setECX(uint32_t value) { _registers.__ecx = value; } + uint32_t getEDX() const { return _registers.__edx; } + void setEDX(uint32_t value) { _registers.__edx = value; } + uint32_t getESI() const { return _registers.__esi; } + void setESI(uint32_t value) { _registers.__esi = value; } + uint32_t getEDI() const { return _registers.__edi; } + void setEDI(uint32_t value) { _registers.__edi = value; } + +private: + struct GPRs { + unsigned int __eax; + unsigned int __ebx; + unsigned int __ecx; + unsigned int __edx; + unsigned int __edi; + unsigned int __esi; + unsigned int __ebp; + unsigned int __esp; + unsigned int __ss; + unsigned int __eflags; + unsigned int __eip; + unsigned int __cs; + unsigned int __ds; + unsigned int __es; + unsigned int __fs; + unsigned int __gs; + }; + + GPRs _registers; +}; + +inline Registers_x86::Registers_x86(const void *registers) { + static_assert((check_fit::does_fit), + "x86 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); +} + +inline Registers_x86::Registers_x86() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_x86::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 7) + return false; + return true; +} + +inline uint32_t Registers_x86::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__eip; + case UNW_REG_SP: + return _registers.__esp; + case UNW_X86_EAX: + return _registers.__eax; + case UNW_X86_ECX: + return _registers.__ecx; + case UNW_X86_EDX: + return _registers.__edx; + case UNW_X86_EBX: + return _registers.__ebx; +#if !defined(__APPLE__) + case UNW_X86_ESP: +#else + case UNW_X86_EBP: +#endif + return _registers.__ebp; +#if !defined(__APPLE__) + case UNW_X86_EBP: +#else + case UNW_X86_ESP: +#endif + return _registers.__esp; + case UNW_X86_ESI: + return _registers.__esi; + case UNW_X86_EDI: + return _registers.__edi; + } + _LIBUNWIND_ABORT("unsupported x86 register"); +} + +inline void Registers_x86::setRegister(int regNum, uint32_t value) { + switch (regNum) { + case UNW_REG_IP: + _registers.__eip = value; + return; + case UNW_REG_SP: + _registers.__esp = value; + return; + case UNW_X86_EAX: + _registers.__eax = value; + return; + case UNW_X86_ECX: + _registers.__ecx = value; + return; + case UNW_X86_EDX: + _registers.__edx = value; + return; + case UNW_X86_EBX: + _registers.__ebx = value; + return; +#if !defined(__APPLE__) + case UNW_X86_ESP: +#else + case UNW_X86_EBP: +#endif + _registers.__ebp = value; + return; +#if !defined(__APPLE__) + case UNW_X86_EBP: +#else + case UNW_X86_ESP: +#endif + _registers.__esp = value; + return; + case UNW_X86_ESI: + _registers.__esi = value; + return; + case UNW_X86_EDI: + _registers.__edi = value; + return; + } + _LIBUNWIND_ABORT("unsupported x86 register"); +} + +inline const char *Registers_x86::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "esp"; + case UNW_X86_EAX: + return "eax"; + case UNW_X86_ECX: + return "ecx"; + case UNW_X86_EDX: + return "edx"; + case UNW_X86_EBX: + return "ebx"; + case UNW_X86_EBP: + return "ebp"; + case UNW_X86_ESP: + return "esp"; + case UNW_X86_ESI: + return "esi"; + case UNW_X86_EDI: + return "edi"; + default: + return "unknown register"; + } +} + +inline double Registers_x86::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no x86 float registers"); +} + +inline void Registers_x86::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no x86 float registers"); +} + +inline v128 Registers_x86::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no x86 vector registers"); +} + +inline void Registers_x86::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no x86 vector registers"); +} +#endif // _LIBUNWIND_TARGET_I386 + + +#if defined(_LIBUNWIND_TARGET_X86_64) +/// Registers_x86_64 holds the register state of a thread in a 64-bit intel +/// process. +class _LIBUNWIND_HIDDEN Registers_x86_64; +extern "C" void __libunwind_Registers_x86_64_jumpto(Registers_x86_64 *); + +#if defined(_LIBUNWIND_USE_CET) +extern "C" void *__libunwind_cet_get_jump_target() { + return reinterpret_cast(&__libunwind_Registers_x86_64_jumpto); +} +#endif + +class _LIBUNWIND_HIDDEN Registers_x86_64 { +public: + Registers_x86_64(); + Registers_x86_64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto() { __libunwind_Registers_x86_64_jumpto(this); } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64; + } + static int getArch() { return REGISTERS_X86_64; } + + uint64_t getSP() const { return _registers.__rsp; } + void setSP(uint64_t value) { _registers.__rsp = value; } + uint64_t getIP() const { return _registers.__rip; } + void setIP(uint64_t value) { _registers.__rip = value; } + uint64_t getRBP() const { return _registers.__rbp; } + void setRBP(uint64_t value) { _registers.__rbp = value; } + uint64_t getRBX() const { return _registers.__rbx; } + void setRBX(uint64_t value) { _registers.__rbx = value; } + uint64_t getR12() const { return _registers.__r12; } + void setR12(uint64_t value) { _registers.__r12 = value; } + uint64_t getR13() const { return _registers.__r13; } + void setR13(uint64_t value) { _registers.__r13 = value; } + uint64_t getR14() const { return _registers.__r14; } + void setR14(uint64_t value) { _registers.__r14 = value; } + uint64_t getR15() const { return _registers.__r15; } + void setR15(uint64_t value) { _registers.__r15 = value; } + +private: + struct GPRs { + uint64_t __rax; + uint64_t __rbx; + uint64_t __rcx; + uint64_t __rdx; + uint64_t __rdi; + uint64_t __rsi; + uint64_t __rbp; + uint64_t __rsp; + uint64_t __r8; + uint64_t __r9; + uint64_t __r10; + uint64_t __r11; + uint64_t __r12; + uint64_t __r13; + uint64_t __r14; + uint64_t __r15; + uint64_t __rip; + uint64_t __rflags; + uint64_t __cs; + uint64_t __fs; + uint64_t __gs; +#if defined(_WIN64) + uint64_t __padding; // 16-byte align +#endif + }; + GPRs _registers; +#if defined(_WIN64) + v128 _xmm[16]; +#endif +}; + +inline Registers_x86_64::Registers_x86_64(const void *registers) { + static_assert((check_fit::does_fit), + "x86_64 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); +} + +inline Registers_x86_64::Registers_x86_64() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_x86_64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 16) + return false; + return true; +} + +inline uint64_t Registers_x86_64::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + case UNW_X86_64_RIP: + return _registers.__rip; + case UNW_REG_SP: + return _registers.__rsp; + case UNW_X86_64_RAX: + return _registers.__rax; + case UNW_X86_64_RDX: + return _registers.__rdx; + case UNW_X86_64_RCX: + return _registers.__rcx; + case UNW_X86_64_RBX: + return _registers.__rbx; + case UNW_X86_64_RSI: + return _registers.__rsi; + case UNW_X86_64_RDI: + return _registers.__rdi; + case UNW_X86_64_RBP: + return _registers.__rbp; + case UNW_X86_64_RSP: + return _registers.__rsp; + case UNW_X86_64_R8: + return _registers.__r8; + case UNW_X86_64_R9: + return _registers.__r9; + case UNW_X86_64_R10: + return _registers.__r10; + case UNW_X86_64_R11: + return _registers.__r11; + case UNW_X86_64_R12: + return _registers.__r12; + case UNW_X86_64_R13: + return _registers.__r13; + case UNW_X86_64_R14: + return _registers.__r14; + case UNW_X86_64_R15: + return _registers.__r15; + } + _LIBUNWIND_ABORT("unsupported x86_64 register"); +} + +inline void Registers_x86_64::setRegister(int regNum, uint64_t value) { + switch (regNum) { + case UNW_REG_IP: + case UNW_X86_64_RIP: + _registers.__rip = value; + return; + case UNW_REG_SP: + _registers.__rsp = value; + return; + case UNW_X86_64_RAX: + _registers.__rax = value; + return; + case UNW_X86_64_RDX: + _registers.__rdx = value; + return; + case UNW_X86_64_RCX: + _registers.__rcx = value; + return; + case UNW_X86_64_RBX: + _registers.__rbx = value; + return; + case UNW_X86_64_RSI: + _registers.__rsi = value; + return; + case UNW_X86_64_RDI: + _registers.__rdi = value; + return; + case UNW_X86_64_RBP: + _registers.__rbp = value; + return; + case UNW_X86_64_RSP: + _registers.__rsp = value; + return; + case UNW_X86_64_R8: + _registers.__r8 = value; + return; + case UNW_X86_64_R9: + _registers.__r9 = value; + return; + case UNW_X86_64_R10: + _registers.__r10 = value; + return; + case UNW_X86_64_R11: + _registers.__r11 = value; + return; + case UNW_X86_64_R12: + _registers.__r12 = value; + return; + case UNW_X86_64_R13: + _registers.__r13 = value; + return; + case UNW_X86_64_R14: + _registers.__r14 = value; + return; + case UNW_X86_64_R15: + _registers.__r15 = value; + return; + } + _LIBUNWIND_ABORT("unsupported x86_64 register"); +} + +inline const char *Registers_x86_64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + case UNW_X86_64_RIP: + return "rip"; + case UNW_REG_SP: + return "rsp"; + case UNW_X86_64_RAX: + return "rax"; + case UNW_X86_64_RDX: + return "rdx"; + case UNW_X86_64_RCX: + return "rcx"; + case UNW_X86_64_RBX: + return "rbx"; + case UNW_X86_64_RSI: + return "rsi"; + case UNW_X86_64_RDI: + return "rdi"; + case UNW_X86_64_RBP: + return "rbp"; + case UNW_X86_64_RSP: + return "rsp"; + case UNW_X86_64_R8: + return "r8"; + case UNW_X86_64_R9: + return "r9"; + case UNW_X86_64_R10: + return "r10"; + case UNW_X86_64_R11: + return "r11"; + case UNW_X86_64_R12: + return "r12"; + case UNW_X86_64_R13: + return "r13"; + case UNW_X86_64_R14: + return "r14"; + case UNW_X86_64_R15: + return "r15"; + case UNW_X86_64_XMM0: + return "xmm0"; + case UNW_X86_64_XMM1: + return "xmm1"; + case UNW_X86_64_XMM2: + return "xmm2"; + case UNW_X86_64_XMM3: + return "xmm3"; + case UNW_X86_64_XMM4: + return "xmm4"; + case UNW_X86_64_XMM5: + return "xmm5"; + case UNW_X86_64_XMM6: + return "xmm6"; + case UNW_X86_64_XMM7: + return "xmm7"; + case UNW_X86_64_XMM8: + return "xmm8"; + case UNW_X86_64_XMM9: + return "xmm9"; + case UNW_X86_64_XMM10: + return "xmm10"; + case UNW_X86_64_XMM11: + return "xmm11"; + case UNW_X86_64_XMM12: + return "xmm12"; + case UNW_X86_64_XMM13: + return "xmm13"; + case UNW_X86_64_XMM14: + return "xmm14"; + case UNW_X86_64_XMM15: + return "xmm15"; + default: + return "unknown register"; + } +} + +inline double Registers_x86_64::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no x86_64 float registers"); +} + +inline void Registers_x86_64::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no x86_64 float registers"); +} + +inline bool Registers_x86_64::validVectorRegister(int regNum) const { +#if defined(_WIN64) + if (regNum < UNW_X86_64_XMM0) + return false; + if (regNum > UNW_X86_64_XMM15) + return false; + return true; +#else + (void)regNum; // suppress unused parameter warning + return false; +#endif +} + +inline v128 Registers_x86_64::getVectorRegister(int regNum) const { +#if defined(_WIN64) + assert(validVectorRegister(regNum)); + return _xmm[regNum - UNW_X86_64_XMM0]; +#else + (void)regNum; // suppress unused parameter warning + _LIBUNWIND_ABORT("no x86_64 vector registers"); +#endif +} + +inline void Registers_x86_64::setVectorRegister(int regNum, v128 value) { +#if defined(_WIN64) + assert(validVectorRegister(regNum)); + _xmm[regNum - UNW_X86_64_XMM0] = value; +#else + (void)regNum; (void)value; // suppress unused parameter warnings + _LIBUNWIND_ABORT("no x86_64 vector registers"); +#endif +} +#endif // _LIBUNWIND_TARGET_X86_64 + + +#if defined(_LIBUNWIND_TARGET_PPC) +/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC +/// process. +class _LIBUNWIND_HIDDEN Registers_ppc { +public: + Registers_ppc(); + Registers_ppc(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC; + } + static int getArch() { return REGISTERS_PPC; } + + uint64_t getSP() const { return _registers.__r1; } + void setSP(uint32_t value) { _registers.__r1 = value; } + uint64_t getIP() const { return _registers.__srr0; } + void setIP(uint32_t value) { _registers.__srr0 = value; } + uint64_t getCR() const { return _registers.__cr; } + void setCR(uint32_t value) { _registers.__cr = value; } + +private: + struct ppc_thread_state_t { + unsigned int __srr0; /* Instruction address register (PC) */ + unsigned int __srr1; /* Machine state register (supervisor) */ + unsigned int __r0; + unsigned int __r1; + unsigned int __r2; + unsigned int __r3; + unsigned int __r4; + unsigned int __r5; + unsigned int __r6; + unsigned int __r7; + unsigned int __r8; + unsigned int __r9; + unsigned int __r10; + unsigned int __r11; + unsigned int __r12; + unsigned int __r13; + unsigned int __r14; + unsigned int __r15; + unsigned int __r16; + unsigned int __r17; + unsigned int __r18; + unsigned int __r19; + unsigned int __r20; + unsigned int __r21; + unsigned int __r22; + unsigned int __r23; + unsigned int __r24; + unsigned int __r25; + unsigned int __r26; + unsigned int __r27; + unsigned int __r28; + unsigned int __r29; + unsigned int __r30; + unsigned int __r31; + unsigned int __cr; /* Condition register */ + unsigned int __xer; /* User's integer exception register */ + unsigned int __lr; /* Link register */ + unsigned int __ctr; /* Count register */ + unsigned int __mq; /* MQ register (601 only) */ + unsigned int __vrsave; /* Vector Save Register */ + }; + + struct ppc_float_state_t { + double __fpregs[32]; + + unsigned int __fpscr_pad; /* fpscr is 64 bits, 32 bits of rubbish */ + unsigned int __fpscr; /* floating point status register */ + }; + + ppc_thread_state_t _registers; + ppc_float_state_t _floatRegisters; + v128 _vectorRegisters[32]; // offset 424 +}; + +inline Registers_ppc::Registers_ppc(const void *registers) { + static_assert((check_fit::does_fit), + "ppc registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); + static_assert(sizeof(ppc_thread_state_t) == 160, + "expected float register offset to be 160"); + memcpy(&_floatRegisters, + static_cast(registers) + sizeof(ppc_thread_state_t), + sizeof(_floatRegisters)); + static_assert(sizeof(ppc_thread_state_t) + sizeof(ppc_float_state_t) == 424, + "expected vector register offset to be 424 bytes"); + memcpy(_vectorRegisters, + static_cast(registers) + sizeof(ppc_thread_state_t) + + sizeof(ppc_float_state_t), + sizeof(_vectorRegisters)); +} + +inline Registers_ppc::Registers_ppc() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_floatRegisters, 0, sizeof(_floatRegisters)); + memset(&_vectorRegisters, 0, sizeof(_vectorRegisters)); +} + +inline bool Registers_ppc::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum == UNW_PPC_VRSAVE) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_PPC_R31) + return true; + if (regNum == UNW_PPC_MQ) + return true; + if (regNum == UNW_PPC_LR) + return true; + if (regNum == UNW_PPC_CTR) + return true; + if ((UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7)) + return true; + return false; +} + +inline uint32_t Registers_ppc::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__srr0; + case UNW_REG_SP: + return _registers.__r1; + case UNW_PPC_R0: + return _registers.__r0; + case UNW_PPC_R1: + return _registers.__r1; + case UNW_PPC_R2: + return _registers.__r2; + case UNW_PPC_R3: + return _registers.__r3; + case UNW_PPC_R4: + return _registers.__r4; + case UNW_PPC_R5: + return _registers.__r5; + case UNW_PPC_R6: + return _registers.__r6; + case UNW_PPC_R7: + return _registers.__r7; + case UNW_PPC_R8: + return _registers.__r8; + case UNW_PPC_R9: + return _registers.__r9; + case UNW_PPC_R10: + return _registers.__r10; + case UNW_PPC_R11: + return _registers.__r11; + case UNW_PPC_R12: + return _registers.__r12; + case UNW_PPC_R13: + return _registers.__r13; + case UNW_PPC_R14: + return _registers.__r14; + case UNW_PPC_R15: + return _registers.__r15; + case UNW_PPC_R16: + return _registers.__r16; + case UNW_PPC_R17: + return _registers.__r17; + case UNW_PPC_R18: + return _registers.__r18; + case UNW_PPC_R19: + return _registers.__r19; + case UNW_PPC_R20: + return _registers.__r20; + case UNW_PPC_R21: + return _registers.__r21; + case UNW_PPC_R22: + return _registers.__r22; + case UNW_PPC_R23: + return _registers.__r23; + case UNW_PPC_R24: + return _registers.__r24; + case UNW_PPC_R25: + return _registers.__r25; + case UNW_PPC_R26: + return _registers.__r26; + case UNW_PPC_R27: + return _registers.__r27; + case UNW_PPC_R28: + return _registers.__r28; + case UNW_PPC_R29: + return _registers.__r29; + case UNW_PPC_R30: + return _registers.__r30; + case UNW_PPC_R31: + return _registers.__r31; + case UNW_PPC_LR: + return _registers.__lr; + case UNW_PPC_CR0: + return (_registers.__cr & 0xF0000000); + case UNW_PPC_CR1: + return (_registers.__cr & 0x0F000000); + case UNW_PPC_CR2: + return (_registers.__cr & 0x00F00000); + case UNW_PPC_CR3: + return (_registers.__cr & 0x000F0000); + case UNW_PPC_CR4: + return (_registers.__cr & 0x0000F000); + case UNW_PPC_CR5: + return (_registers.__cr & 0x00000F00); + case UNW_PPC_CR6: + return (_registers.__cr & 0x000000F0); + case UNW_PPC_CR7: + return (_registers.__cr & 0x0000000F); + case UNW_PPC_VRSAVE: + return _registers.__vrsave; + } + _LIBUNWIND_ABORT("unsupported ppc register"); +} + +inline void Registers_ppc::setRegister(int regNum, uint32_t value) { + //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value); + switch (regNum) { + case UNW_REG_IP: + _registers.__srr0 = value; + return; + case UNW_REG_SP: + _registers.__r1 = value; + return; + case UNW_PPC_R0: + _registers.__r0 = value; + return; + case UNW_PPC_R1: + _registers.__r1 = value; + return; + case UNW_PPC_R2: + _registers.__r2 = value; + return; + case UNW_PPC_R3: + _registers.__r3 = value; + return; + case UNW_PPC_R4: + _registers.__r4 = value; + return; + case UNW_PPC_R5: + _registers.__r5 = value; + return; + case UNW_PPC_R6: + _registers.__r6 = value; + return; + case UNW_PPC_R7: + _registers.__r7 = value; + return; + case UNW_PPC_R8: + _registers.__r8 = value; + return; + case UNW_PPC_R9: + _registers.__r9 = value; + return; + case UNW_PPC_R10: + _registers.__r10 = value; + return; + case UNW_PPC_R11: + _registers.__r11 = value; + return; + case UNW_PPC_R12: + _registers.__r12 = value; + return; + case UNW_PPC_R13: + _registers.__r13 = value; + return; + case UNW_PPC_R14: + _registers.__r14 = value; + return; + case UNW_PPC_R15: + _registers.__r15 = value; + return; + case UNW_PPC_R16: + _registers.__r16 = value; + return; + case UNW_PPC_R17: + _registers.__r17 = value; + return; + case UNW_PPC_R18: + _registers.__r18 = value; + return; + case UNW_PPC_R19: + _registers.__r19 = value; + return; + case UNW_PPC_R20: + _registers.__r20 = value; + return; + case UNW_PPC_R21: + _registers.__r21 = value; + return; + case UNW_PPC_R22: + _registers.__r22 = value; + return; + case UNW_PPC_R23: + _registers.__r23 = value; + return; + case UNW_PPC_R24: + _registers.__r24 = value; + return; + case UNW_PPC_R25: + _registers.__r25 = value; + return; + case UNW_PPC_R26: + _registers.__r26 = value; + return; + case UNW_PPC_R27: + _registers.__r27 = value; + return; + case UNW_PPC_R28: + _registers.__r28 = value; + return; + case UNW_PPC_R29: + _registers.__r29 = value; + return; + case UNW_PPC_R30: + _registers.__r30 = value; + return; + case UNW_PPC_R31: + _registers.__r31 = value; + return; + case UNW_PPC_MQ: + _registers.__mq = value; + return; + case UNW_PPC_LR: + _registers.__lr = value; + return; + case UNW_PPC_CTR: + _registers.__ctr = value; + return; + case UNW_PPC_CR0: + _registers.__cr &= 0x0FFFFFFF; + _registers.__cr |= (value & 0xF0000000); + return; + case UNW_PPC_CR1: + _registers.__cr &= 0xF0FFFFFF; + _registers.__cr |= (value & 0x0F000000); + return; + case UNW_PPC_CR2: + _registers.__cr &= 0xFF0FFFFF; + _registers.__cr |= (value & 0x00F00000); + return; + case UNW_PPC_CR3: + _registers.__cr &= 0xFFF0FFFF; + _registers.__cr |= (value & 0x000F0000); + return; + case UNW_PPC_CR4: + _registers.__cr &= 0xFFFF0FFF; + _registers.__cr |= (value & 0x0000F000); + return; + case UNW_PPC_CR5: + _registers.__cr &= 0xFFFFF0FF; + _registers.__cr |= (value & 0x00000F00); + return; + case UNW_PPC_CR6: + _registers.__cr &= 0xFFFFFF0F; + _registers.__cr |= (value & 0x000000F0); + return; + case UNW_PPC_CR7: + _registers.__cr &= 0xFFFFFFF0; + _registers.__cr |= (value & 0x0000000F); + return; + case UNW_PPC_VRSAVE: + _registers.__vrsave = value; + return; + // not saved + return; + case UNW_PPC_XER: + _registers.__xer = value; + return; + case UNW_PPC_AP: + case UNW_PPC_VSCR: + case UNW_PPC_SPEFSCR: + // not saved + return; + } + _LIBUNWIND_ABORT("unsupported ppc register"); +} + +inline bool Registers_ppc::validFloatRegister(int regNum) const { + if (regNum < UNW_PPC_F0) + return false; + if (regNum > UNW_PPC_F31) + return false; + return true; +} + +inline double Registers_ppc::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _floatRegisters.__fpregs[regNum - UNW_PPC_F0]; +} + +inline void Registers_ppc::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _floatRegisters.__fpregs[regNum - UNW_PPC_F0] = value; +} + +inline bool Registers_ppc::validVectorRegister(int regNum) const { + if (regNum < UNW_PPC_V0) + return false; + if (regNum > UNW_PPC_V31) + return false; + return true; +} + +inline v128 Registers_ppc::getVectorRegister(int regNum) const { + assert(validVectorRegister(regNum)); + v128 result = _vectorRegisters[regNum - UNW_PPC_V0]; + return result; +} + +inline void Registers_ppc::setVectorRegister(int regNum, v128 value) { + assert(validVectorRegister(regNum)); + _vectorRegisters[regNum - UNW_PPC_V0] = value; +} + +inline const char *Registers_ppc::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC_R0: + return "r0"; + case UNW_PPC_R1: + return "r1"; + case UNW_PPC_R2: + return "r2"; + case UNW_PPC_R3: + return "r3"; + case UNW_PPC_R4: + return "r4"; + case UNW_PPC_R5: + return "r5"; + case UNW_PPC_R6: + return "r6"; + case UNW_PPC_R7: + return "r7"; + case UNW_PPC_R8: + return "r8"; + case UNW_PPC_R9: + return "r9"; + case UNW_PPC_R10: + return "r10"; + case UNW_PPC_R11: + return "r11"; + case UNW_PPC_R12: + return "r12"; + case UNW_PPC_R13: + return "r13"; + case UNW_PPC_R14: + return "r14"; + case UNW_PPC_R15: + return "r15"; + case UNW_PPC_R16: + return "r16"; + case UNW_PPC_R17: + return "r17"; + case UNW_PPC_R18: + return "r18"; + case UNW_PPC_R19: + return "r19"; + case UNW_PPC_R20: + return "r20"; + case UNW_PPC_R21: + return "r21"; + case UNW_PPC_R22: + return "r22"; + case UNW_PPC_R23: + return "r23"; + case UNW_PPC_R24: + return "r24"; + case UNW_PPC_R25: + return "r25"; + case UNW_PPC_R26: + return "r26"; + case UNW_PPC_R27: + return "r27"; + case UNW_PPC_R28: + return "r28"; + case UNW_PPC_R29: + return "r29"; + case UNW_PPC_R30: + return "r30"; + case UNW_PPC_R31: + return "r31"; + case UNW_PPC_F0: + return "fp0"; + case UNW_PPC_F1: + return "fp1"; + case UNW_PPC_F2: + return "fp2"; + case UNW_PPC_F3: + return "fp3"; + case UNW_PPC_F4: + return "fp4"; + case UNW_PPC_F5: + return "fp5"; + case UNW_PPC_F6: + return "fp6"; + case UNW_PPC_F7: + return "fp7"; + case UNW_PPC_F8: + return "fp8"; + case UNW_PPC_F9: + return "fp9"; + case UNW_PPC_F10: + return "fp10"; + case UNW_PPC_F11: + return "fp11"; + case UNW_PPC_F12: + return "fp12"; + case UNW_PPC_F13: + return "fp13"; + case UNW_PPC_F14: + return "fp14"; + case UNW_PPC_F15: + return "fp15"; + case UNW_PPC_F16: + return "fp16"; + case UNW_PPC_F17: + return "fp17"; + case UNW_PPC_F18: + return "fp18"; + case UNW_PPC_F19: + return "fp19"; + case UNW_PPC_F20: + return "fp20"; + case UNW_PPC_F21: + return "fp21"; + case UNW_PPC_F22: + return "fp22"; + case UNW_PPC_F23: + return "fp23"; + case UNW_PPC_F24: + return "fp24"; + case UNW_PPC_F25: + return "fp25"; + case UNW_PPC_F26: + return "fp26"; + case UNW_PPC_F27: + return "fp27"; + case UNW_PPC_F28: + return "fp28"; + case UNW_PPC_F29: + return "fp29"; + case UNW_PPC_F30: + return "fp30"; + case UNW_PPC_F31: + return "fp31"; + case UNW_PPC_LR: + return "lr"; + default: + return "unknown register"; + } + +} +#endif // _LIBUNWIND_TARGET_PPC + +#if defined(_LIBUNWIND_TARGET_PPC64) +/// Registers_ppc64 holds the register state of a thread in a 64-bit PowerPC +/// process. +class _LIBUNWIND_HIDDEN Registers_ppc64 { +public: + Registers_ppc64(); + Registers_ppc64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64; + } + static int getArch() { return REGISTERS_PPC64; } + + uint64_t getSP() const { return _registers.__r1; } + void setSP(uint64_t value) { _registers.__r1 = value; } + uint64_t getIP() const { return _registers.__srr0; } + void setIP(uint64_t value) { _registers.__srr0 = value; } + uint64_t getCR() const { return _registers.__cr; } + void setCR(uint64_t value) { _registers.__cr = value; } + +private: + struct ppc64_thread_state_t { + uint64_t __srr0; // Instruction address register (PC) + uint64_t __srr1; // Machine state register (supervisor) + uint64_t __r0; + uint64_t __r1; + uint64_t __r2; + uint64_t __r3; + uint64_t __r4; + uint64_t __r5; + uint64_t __r6; + uint64_t __r7; + uint64_t __r8; + uint64_t __r9; + uint64_t __r10; + uint64_t __r11; + uint64_t __r12; + uint64_t __r13; + uint64_t __r14; + uint64_t __r15; + uint64_t __r16; + uint64_t __r17; + uint64_t __r18; + uint64_t __r19; + uint64_t __r20; + uint64_t __r21; + uint64_t __r22; + uint64_t __r23; + uint64_t __r24; + uint64_t __r25; + uint64_t __r26; + uint64_t __r27; + uint64_t __r28; + uint64_t __r29; + uint64_t __r30; + uint64_t __r31; + uint64_t __cr; // Condition register + uint64_t __xer; // User's integer exception register + uint64_t __lr; // Link register + uint64_t __ctr; // Count register + uint64_t __vrsave; // Vector Save Register + }; + + union ppc64_vsr_t { + struct asfloat_s { + double f; + uint64_t v2; + } asfloat; + v128 v; + }; + + ppc64_thread_state_t _registers; + ppc64_vsr_t _vectorScalarRegisters[64]; + + static int getVectorRegNum(int num); +}; + +inline Registers_ppc64::Registers_ppc64(const void *registers) { + static_assert((check_fit::does_fit), + "ppc64 registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); + static_assert(sizeof(_registers) == 312, + "expected vector scalar register offset to be 312"); + memcpy(&_vectorScalarRegisters, + static_cast(registers) + sizeof(_registers), + sizeof(_vectorScalarRegisters)); + static_assert(sizeof(_registers) + + sizeof(_vectorScalarRegisters) == 1336, + "expected vector register offset to be 1336 bytes"); +} + +inline Registers_ppc64::Registers_ppc64() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_vectorScalarRegisters, 0, sizeof(_vectorScalarRegisters)); +} + +inline bool Registers_ppc64::validRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + case UNW_REG_SP: + case UNW_PPC64_XER: + case UNW_PPC64_LR: + case UNW_PPC64_CTR: + case UNW_PPC64_VRSAVE: + return true; + } + + if (regNum >= UNW_PPC64_R0 && regNum <= UNW_PPC64_R31) + return true; + if (regNum >= UNW_PPC64_CR0 && regNum <= UNW_PPC64_CR7) + return true; + + return false; +} + +inline uint64_t Registers_ppc64::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__srr0; + case UNW_PPC64_R0: + return _registers.__r0; + case UNW_PPC64_R1: + case UNW_REG_SP: + return _registers.__r1; + case UNW_PPC64_R2: + return _registers.__r2; + case UNW_PPC64_R3: + return _registers.__r3; + case UNW_PPC64_R4: + return _registers.__r4; + case UNW_PPC64_R5: + return _registers.__r5; + case UNW_PPC64_R6: + return _registers.__r6; + case UNW_PPC64_R7: + return _registers.__r7; + case UNW_PPC64_R8: + return _registers.__r8; + case UNW_PPC64_R9: + return _registers.__r9; + case UNW_PPC64_R10: + return _registers.__r10; + case UNW_PPC64_R11: + return _registers.__r11; + case UNW_PPC64_R12: + return _registers.__r12; + case UNW_PPC64_R13: + return _registers.__r13; + case UNW_PPC64_R14: + return _registers.__r14; + case UNW_PPC64_R15: + return _registers.__r15; + case UNW_PPC64_R16: + return _registers.__r16; + case UNW_PPC64_R17: + return _registers.__r17; + case UNW_PPC64_R18: + return _registers.__r18; + case UNW_PPC64_R19: + return _registers.__r19; + case UNW_PPC64_R20: + return _registers.__r20; + case UNW_PPC64_R21: + return _registers.__r21; + case UNW_PPC64_R22: + return _registers.__r22; + case UNW_PPC64_R23: + return _registers.__r23; + case UNW_PPC64_R24: + return _registers.__r24; + case UNW_PPC64_R25: + return _registers.__r25; + case UNW_PPC64_R26: + return _registers.__r26; + case UNW_PPC64_R27: + return _registers.__r27; + case UNW_PPC64_R28: + return _registers.__r28; + case UNW_PPC64_R29: + return _registers.__r29; + case UNW_PPC64_R30: + return _registers.__r30; + case UNW_PPC64_R31: + return _registers.__r31; + case UNW_PPC64_CR0: + return (_registers.__cr & 0xF0000000); + case UNW_PPC64_CR1: + return (_registers.__cr & 0x0F000000); + case UNW_PPC64_CR2: + return (_registers.__cr & 0x00F00000); + case UNW_PPC64_CR3: + return (_registers.__cr & 0x000F0000); + case UNW_PPC64_CR4: + return (_registers.__cr & 0x0000F000); + case UNW_PPC64_CR5: + return (_registers.__cr & 0x00000F00); + case UNW_PPC64_CR6: + return (_registers.__cr & 0x000000F0); + case UNW_PPC64_CR7: + return (_registers.__cr & 0x0000000F); + case UNW_PPC64_XER: + return _registers.__xer; + case UNW_PPC64_LR: + return _registers.__lr; + case UNW_PPC64_CTR: + return _registers.__ctr; + case UNW_PPC64_VRSAVE: + return _registers.__vrsave; + } + _LIBUNWIND_ABORT("unsupported ppc64 register"); +} + +inline void Registers_ppc64::setRegister(int regNum, uint64_t value) { + switch (regNum) { + case UNW_REG_IP: + _registers.__srr0 = value; + return; + case UNW_PPC64_R0: + _registers.__r0 = value; + return; + case UNW_PPC64_R1: + case UNW_REG_SP: + _registers.__r1 = value; + return; + case UNW_PPC64_R2: + _registers.__r2 = value; + return; + case UNW_PPC64_R3: + _registers.__r3 = value; + return; + case UNW_PPC64_R4: + _registers.__r4 = value; + return; + case UNW_PPC64_R5: + _registers.__r5 = value; + return; + case UNW_PPC64_R6: + _registers.__r6 = value; + return; + case UNW_PPC64_R7: + _registers.__r7 = value; + return; + case UNW_PPC64_R8: + _registers.__r8 = value; + return; + case UNW_PPC64_R9: + _registers.__r9 = value; + return; + case UNW_PPC64_R10: + _registers.__r10 = value; + return; + case UNW_PPC64_R11: + _registers.__r11 = value; + return; + case UNW_PPC64_R12: + _registers.__r12 = value; + return; + case UNW_PPC64_R13: + _registers.__r13 = value; + return; + case UNW_PPC64_R14: + _registers.__r14 = value; + return; + case UNW_PPC64_R15: + _registers.__r15 = value; + return; + case UNW_PPC64_R16: + _registers.__r16 = value; + return; + case UNW_PPC64_R17: + _registers.__r17 = value; + return; + case UNW_PPC64_R18: + _registers.__r18 = value; + return; + case UNW_PPC64_R19: + _registers.__r19 = value; + return; + case UNW_PPC64_R20: + _registers.__r20 = value; + return; + case UNW_PPC64_R21: + _registers.__r21 = value; + return; + case UNW_PPC64_R22: + _registers.__r22 = value; + return; + case UNW_PPC64_R23: + _registers.__r23 = value; + return; + case UNW_PPC64_R24: + _registers.__r24 = value; + return; + case UNW_PPC64_R25: + _registers.__r25 = value; + return; + case UNW_PPC64_R26: + _registers.__r26 = value; + return; + case UNW_PPC64_R27: + _registers.__r27 = value; + return; + case UNW_PPC64_R28: + _registers.__r28 = value; + return; + case UNW_PPC64_R29: + _registers.__r29 = value; + return; + case UNW_PPC64_R30: + _registers.__r30 = value; + return; + case UNW_PPC64_R31: + _registers.__r31 = value; + return; + case UNW_PPC64_CR0: + _registers.__cr &= 0x0FFFFFFF; + _registers.__cr |= (value & 0xF0000000); + return; + case UNW_PPC64_CR1: + _registers.__cr &= 0xF0FFFFFF; + _registers.__cr |= (value & 0x0F000000); + return; + case UNW_PPC64_CR2: + _registers.__cr &= 0xFF0FFFFF; + _registers.__cr |= (value & 0x00F00000); + return; + case UNW_PPC64_CR3: + _registers.__cr &= 0xFFF0FFFF; + _registers.__cr |= (value & 0x000F0000); + return; + case UNW_PPC64_CR4: + _registers.__cr &= 0xFFFF0FFF; + _registers.__cr |= (value & 0x0000F000); + return; + case UNW_PPC64_CR5: + _registers.__cr &= 0xFFFFF0FF; + _registers.__cr |= (value & 0x00000F00); + return; + case UNW_PPC64_CR6: + _registers.__cr &= 0xFFFFFF0F; + _registers.__cr |= (value & 0x000000F0); + return; + case UNW_PPC64_CR7: + _registers.__cr &= 0xFFFFFFF0; + _registers.__cr |= (value & 0x0000000F); + return; + case UNW_PPC64_XER: + _registers.__xer = value; + return; + case UNW_PPC64_LR: + _registers.__lr = value; + return; + case UNW_PPC64_CTR: + _registers.__ctr = value; + return; + case UNW_PPC64_VRSAVE: + _registers.__vrsave = value; + return; + } + _LIBUNWIND_ABORT("unsupported ppc64 register"); +} + +inline bool Registers_ppc64::validFloatRegister(int regNum) const { + return regNum >= UNW_PPC64_F0 && regNum <= UNW_PPC64_F31; +} + +inline double Registers_ppc64::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _vectorScalarRegisters[regNum - UNW_PPC64_F0].asfloat.f; +} + +inline void Registers_ppc64::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _vectorScalarRegisters[regNum - UNW_PPC64_F0].asfloat.f = value; +} + +inline bool Registers_ppc64::validVectorRegister(int regNum) const { +#if defined(__VSX__) + if (regNum >= UNW_PPC64_VS0 && regNum <= UNW_PPC64_VS31) + return true; + if (regNum >= UNW_PPC64_VS32 && regNum <= UNW_PPC64_VS63) + return true; +#elif defined(__ALTIVEC__) + if (regNum >= UNW_PPC64_V0 && regNum <= UNW_PPC64_V31) + return true; +#endif + return false; +} + +inline int Registers_ppc64::getVectorRegNum(int num) +{ + if (num >= UNW_PPC64_VS0 && num <= UNW_PPC64_VS31) + return num - UNW_PPC64_VS0; + else + return num - UNW_PPC64_VS32 + 32; +} + +inline v128 Registers_ppc64::getVectorRegister(int regNum) const { + assert(validVectorRegister(regNum)); + return _vectorScalarRegisters[getVectorRegNum(regNum)].v; +} + +inline void Registers_ppc64::setVectorRegister(int regNum, v128 value) { + assert(validVectorRegister(regNum)); + _vectorScalarRegisters[getVectorRegNum(regNum)].v = value; +} + +inline const char *Registers_ppc64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC64_R0: + return "r0"; + case UNW_PPC64_R1: + return "r1"; + case UNW_PPC64_R2: + return "r2"; + case UNW_PPC64_R3: + return "r3"; + case UNW_PPC64_R4: + return "r4"; + case UNW_PPC64_R5: + return "r5"; + case UNW_PPC64_R6: + return "r6"; + case UNW_PPC64_R7: + return "r7"; + case UNW_PPC64_R8: + return "r8"; + case UNW_PPC64_R9: + return "r9"; + case UNW_PPC64_R10: + return "r10"; + case UNW_PPC64_R11: + return "r11"; + case UNW_PPC64_R12: + return "r12"; + case UNW_PPC64_R13: + return "r13"; + case UNW_PPC64_R14: + return "r14"; + case UNW_PPC64_R15: + return "r15"; + case UNW_PPC64_R16: + return "r16"; + case UNW_PPC64_R17: + return "r17"; + case UNW_PPC64_R18: + return "r18"; + case UNW_PPC64_R19: + return "r19"; + case UNW_PPC64_R20: + return "r20"; + case UNW_PPC64_R21: + return "r21"; + case UNW_PPC64_R22: + return "r22"; + case UNW_PPC64_R23: + return "r23"; + case UNW_PPC64_R24: + return "r24"; + case UNW_PPC64_R25: + return "r25"; + case UNW_PPC64_R26: + return "r26"; + case UNW_PPC64_R27: + return "r27"; + case UNW_PPC64_R28: + return "r28"; + case UNW_PPC64_R29: + return "r29"; + case UNW_PPC64_R30: + return "r30"; + case UNW_PPC64_R31: + return "r31"; + case UNW_PPC64_CR0: + return "cr0"; + case UNW_PPC64_CR1: + return "cr1"; + case UNW_PPC64_CR2: + return "cr2"; + case UNW_PPC64_CR3: + return "cr3"; + case UNW_PPC64_CR4: + return "cr4"; + case UNW_PPC64_CR5: + return "cr5"; + case UNW_PPC64_CR6: + return "cr6"; + case UNW_PPC64_CR7: + return "cr7"; + case UNW_PPC64_XER: + return "xer"; + case UNW_PPC64_LR: + return "lr"; + case UNW_PPC64_CTR: + return "ctr"; + case UNW_PPC64_VRSAVE: + return "vrsave"; + case UNW_PPC64_F0: + return "fp0"; + case UNW_PPC64_F1: + return "fp1"; + case UNW_PPC64_F2: + return "fp2"; + case UNW_PPC64_F3: + return "fp3"; + case UNW_PPC64_F4: + return "fp4"; + case UNW_PPC64_F5: + return "fp5"; + case UNW_PPC64_F6: + return "fp6"; + case UNW_PPC64_F7: + return "fp7"; + case UNW_PPC64_F8: + return "fp8"; + case UNW_PPC64_F9: + return "fp9"; + case UNW_PPC64_F10: + return "fp10"; + case UNW_PPC64_F11: + return "fp11"; + case UNW_PPC64_F12: + return "fp12"; + case UNW_PPC64_F13: + return "fp13"; + case UNW_PPC64_F14: + return "fp14"; + case UNW_PPC64_F15: + return "fp15"; + case UNW_PPC64_F16: + return "fp16"; + case UNW_PPC64_F17: + return "fp17"; + case UNW_PPC64_F18: + return "fp18"; + case UNW_PPC64_F19: + return "fp19"; + case UNW_PPC64_F20: + return "fp20"; + case UNW_PPC64_F21: + return "fp21"; + case UNW_PPC64_F22: + return "fp22"; + case UNW_PPC64_F23: + return "fp23"; + case UNW_PPC64_F24: + return "fp24"; + case UNW_PPC64_F25: + return "fp25"; + case UNW_PPC64_F26: + return "fp26"; + case UNW_PPC64_F27: + return "fp27"; + case UNW_PPC64_F28: + return "fp28"; + case UNW_PPC64_F29: + return "fp29"; + case UNW_PPC64_F30: + return "fp30"; + case UNW_PPC64_F31: + return "fp31"; + case UNW_PPC64_V0: + return "v0"; + case UNW_PPC64_V1: + return "v1"; + case UNW_PPC64_V2: + return "v2"; + case UNW_PPC64_V3: + return "v3"; + case UNW_PPC64_V4: + return "v4"; + case UNW_PPC64_V5: + return "v5"; + case UNW_PPC64_V6: + return "v6"; + case UNW_PPC64_V7: + return "v7"; + case UNW_PPC64_V8: + return "v8"; + case UNW_PPC64_V9: + return "v9"; + case UNW_PPC64_V10: + return "v10"; + case UNW_PPC64_V11: + return "v11"; + case UNW_PPC64_V12: + return "v12"; + case UNW_PPC64_V13: + return "v13"; + case UNW_PPC64_V14: + return "v14"; + case UNW_PPC64_V15: + return "v15"; + case UNW_PPC64_V16: + return "v16"; + case UNW_PPC64_V17: + return "v17"; + case UNW_PPC64_V18: + return "v18"; + case UNW_PPC64_V19: + return "v19"; + case UNW_PPC64_V20: + return "v20"; + case UNW_PPC64_V21: + return "v21"; + case UNW_PPC64_V22: + return "v22"; + case UNW_PPC64_V23: + return "v23"; + case UNW_PPC64_V24: + return "v24"; + case UNW_PPC64_V25: + return "v25"; + case UNW_PPC64_V26: + return "v26"; + case UNW_PPC64_V27: + return "v27"; + case UNW_PPC64_V28: + return "v28"; + case UNW_PPC64_V29: + return "v29"; + case UNW_PPC64_V30: + return "v30"; + case UNW_PPC64_V31: + return "v31"; + } + return "unknown register"; +} +#endif // _LIBUNWIND_TARGET_PPC64 + + +#if defined(_LIBUNWIND_TARGET_AARCH64) +/// Registers_arm64 holds the register state of a thread in a 64-bit arm +/// process. +class _LIBUNWIND_HIDDEN Registers_arm64; +extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *); +class _LIBUNWIND_HIDDEN Registers_arm64 { +public: + Registers_arm64(); + Registers_arm64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto() { __libunwind_Registers_arm64_jumpto(this); } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64; + } + static int getArch() { return REGISTERS_ARM64; } + + uint64_t getSP() const { return _registers.__sp; } + void setSP(uint64_t value) { _registers.__sp = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint64_t value) { _registers.__pc = value; } + uint64_t getFP() const { return _registers.__fp; } + void setFP(uint64_t value) { _registers.__fp = value; } + +private: + struct GPRs { + uint64_t __x[29]; // x0-x28 + uint64_t __fp; // Frame pointer x29 + uint64_t __lr; // Link register x30 + uint64_t __sp; // Stack pointer x31 + uint64_t __pc; // Program counter + uint64_t __ra_sign_state; // RA sign state register + }; + + GPRs _registers; + double _vectorHalfRegisters[32]; + // Currently only the lower double in 128-bit vectore registers + // is perserved during unwinding. We could define new register + // numbers (> 96) which mean whole vector registers, then this + // struct would need to change to contain whole vector registers. +}; + +inline Registers_arm64::Registers_arm64(const void *registers) { + static_assert((check_fit::does_fit), + "arm64 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); + static_assert(sizeof(GPRs) == 0x110, + "expected VFP registers to be at offset 272"); + memcpy(_vectorHalfRegisters, + static_cast(registers) + sizeof(GPRs), + sizeof(_vectorHalfRegisters)); +} + +inline Registers_arm64::Registers_arm64() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_vectorHalfRegisters, 0, sizeof(_vectorHalfRegisters)); +} + +inline bool Registers_arm64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 95) + return false; + if (regNum == UNW_AARCH64_RA_SIGN_STATE) + return true; + if ((regNum > 32) && (regNum < 64)) + return false; + return true; +} + +inline uint64_t Registers_arm64::getRegister(int regNum) const { + if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC) + return _registers.__pc; + if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP) + return _registers.__sp; + if (regNum == UNW_AARCH64_RA_SIGN_STATE) + return _registers.__ra_sign_state; + if (regNum == UNW_AARCH64_FP) + return _registers.__fp; + if (regNum == UNW_AARCH64_LR) + return _registers.__lr; + if ((regNum >= 0) && (regNum < 29)) + return _registers.__x[regNum]; + _LIBUNWIND_ABORT("unsupported arm64 register"); +} + +inline void Registers_arm64::setRegister(int regNum, uint64_t value) { + if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC) + _registers.__pc = value; + else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP) + _registers.__sp = value; + else if (regNum == UNW_AARCH64_RA_SIGN_STATE) + _registers.__ra_sign_state = value; + else if (regNum == UNW_AARCH64_FP) + _registers.__fp = value; + else if (regNum == UNW_AARCH64_LR) + _registers.__lr = value; + else if ((regNum >= 0) && (regNum < 29)) + _registers.__x[regNum] = value; + else + _LIBUNWIND_ABORT("unsupported arm64 register"); +} + +inline const char *Registers_arm64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_REG_SP: + return "sp"; + case UNW_AARCH64_X0: + return "x0"; + case UNW_AARCH64_X1: + return "x1"; + case UNW_AARCH64_X2: + return "x2"; + case UNW_AARCH64_X3: + return "x3"; + case UNW_AARCH64_X4: + return "x4"; + case UNW_AARCH64_X5: + return "x5"; + case UNW_AARCH64_X6: + return "x6"; + case UNW_AARCH64_X7: + return "x7"; + case UNW_AARCH64_X8: + return "x8"; + case UNW_AARCH64_X9: + return "x9"; + case UNW_AARCH64_X10: + return "x10"; + case UNW_AARCH64_X11: + return "x11"; + case UNW_AARCH64_X12: + return "x12"; + case UNW_AARCH64_X13: + return "x13"; + case UNW_AARCH64_X14: + return "x14"; + case UNW_AARCH64_X15: + return "x15"; + case UNW_AARCH64_X16: + return "x16"; + case UNW_AARCH64_X17: + return "x17"; + case UNW_AARCH64_X18: + return "x18"; + case UNW_AARCH64_X19: + return "x19"; + case UNW_AARCH64_X20: + return "x20"; + case UNW_AARCH64_X21: + return "x21"; + case UNW_AARCH64_X22: + return "x22"; + case UNW_AARCH64_X23: + return "x23"; + case UNW_AARCH64_X24: + return "x24"; + case UNW_AARCH64_X25: + return "x25"; + case UNW_AARCH64_X26: + return "x26"; + case UNW_AARCH64_X27: + return "x27"; + case UNW_AARCH64_X28: + return "x28"; + case UNW_AARCH64_FP: + return "fp"; + case UNW_AARCH64_LR: + return "lr"; + case UNW_AARCH64_SP: + return "sp"; + case UNW_AARCH64_PC: + return "pc"; + case UNW_AARCH64_V0: + return "d0"; + case UNW_AARCH64_V1: + return "d1"; + case UNW_AARCH64_V2: + return "d2"; + case UNW_AARCH64_V3: + return "d3"; + case UNW_AARCH64_V4: + return "d4"; + case UNW_AARCH64_V5: + return "d5"; + case UNW_AARCH64_V6: + return "d6"; + case UNW_AARCH64_V7: + return "d7"; + case UNW_AARCH64_V8: + return "d8"; + case UNW_AARCH64_V9: + return "d9"; + case UNW_AARCH64_V10: + return "d10"; + case UNW_AARCH64_V11: + return "d11"; + case UNW_AARCH64_V12: + return "d12"; + case UNW_AARCH64_V13: + return "d13"; + case UNW_AARCH64_V14: + return "d14"; + case UNW_AARCH64_V15: + return "d15"; + case UNW_AARCH64_V16: + return "d16"; + case UNW_AARCH64_V17: + return "d17"; + case UNW_AARCH64_V18: + return "d18"; + case UNW_AARCH64_V19: + return "d19"; + case UNW_AARCH64_V20: + return "d20"; + case UNW_AARCH64_V21: + return "d21"; + case UNW_AARCH64_V22: + return "d22"; + case UNW_AARCH64_V23: + return "d23"; + case UNW_AARCH64_V24: + return "d24"; + case UNW_AARCH64_V25: + return "d25"; + case UNW_AARCH64_V26: + return "d26"; + case UNW_AARCH64_V27: + return "d27"; + case UNW_AARCH64_V28: + return "d28"; + case UNW_AARCH64_V29: + return "d29"; + case UNW_AARCH64_V30: + return "d30"; + case UNW_AARCH64_V31: + return "d31"; + default: + return "unknown register"; + } +} + +inline bool Registers_arm64::validFloatRegister(int regNum) const { + if (regNum < UNW_AARCH64_V0) + return false; + if (regNum > UNW_AARCH64_V31) + return false; + return true; +} + +inline double Registers_arm64::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _vectorHalfRegisters[regNum - UNW_AARCH64_V0]; +} + +inline void Registers_arm64::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _vectorHalfRegisters[regNum - UNW_AARCH64_V0] = value; +} + +inline bool Registers_arm64::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_arm64::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no arm64 vector register support yet"); +} + +inline void Registers_arm64::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no arm64 vector register support yet"); +} +#endif // _LIBUNWIND_TARGET_AARCH64 + +#if defined(_LIBUNWIND_TARGET_ARM) +/// Registers_arm holds the register state of a thread in a 32-bit arm +/// process. +/// +/// NOTE: Assumes VFPv3. On ARM processors without a floating point unit, +/// this uses more memory than required. +class _LIBUNWIND_HIDDEN Registers_arm { +public: + Registers_arm(); + Registers_arm(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + unw_fpreg_t getFloatRegister(int num); + void setFloatRegister(int num, unw_fpreg_t value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto() { + restoreSavedFloatRegisters(); + restoreCoreAndJumpTo(); + } + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM; + } + static int getArch() { return REGISTERS_ARM; } + + uint32_t getSP() const { return _registers.__sp; } + void setSP(uint32_t value) { _registers.__sp = value; } + uint32_t getIP() const { return _registers.__pc; } + void setIP(uint32_t value) { _registers.__pc = value; } + + void saveVFPAsX() { + assert(_use_X_for_vfp_save || !_saved_vfp_d0_d15); + _use_X_for_vfp_save = true; + } + + void restoreSavedFloatRegisters() { + if (_saved_vfp_d0_d15) { + if (_use_X_for_vfp_save) + restoreVFPWithFLDMX(_vfp_d0_d15_pad); + else + restoreVFPWithFLDMD(_vfp_d0_d15_pad); + } + if (_saved_vfp_d16_d31) + restoreVFPv3(_vfp_d16_d31); +#if defined(__ARM_WMMX) + if (_saved_iwmmx) + restoreiWMMX(_iwmmx); + if (_saved_iwmmx_control) + restoreiWMMXControl(_iwmmx_control); +#endif + } + +private: + struct GPRs { + uint32_t __r[13]; // r0-r12 + uint32_t __sp; // Stack pointer r13 + uint32_t __lr; // Link register r14 + uint32_t __pc; // Program counter r15 + }; + + struct PseudoRegisters { + uint32_t __pac; // Return Authentication Code (PAC) + }; + + static void saveVFPWithFSTMD(void*); + static void saveVFPWithFSTMX(void*); + static void saveVFPv3(void*); + static void restoreVFPWithFLDMD(void*); + static void restoreVFPWithFLDMX(void*); + static void restoreVFPv3(void*); +#if defined(__ARM_WMMX) + static void saveiWMMX(void*); + static void saveiWMMXControl(uint32_t*); + static void restoreiWMMX(void*); + static void restoreiWMMXControl(uint32_t*); +#endif + void restoreCoreAndJumpTo(); + + // ARM registers + GPRs _registers; + PseudoRegisters _pseudo_registers; + + // We save floating point registers lazily because we can't know ahead of + // time which ones are used. See EHABI #4.7. + + // Whether D0-D15 are saved in the FTSMX instead of FSTMD format. + // + // See EHABI #7.5 that explains how matching instruction sequences for load + // and store need to be used to correctly restore the exact register bits. + bool _use_X_for_vfp_save; + // Whether VFP D0-D15 are saved. + bool _saved_vfp_d0_d15; + // Whether VFPv3 D16-D31 are saved. + bool _saved_vfp_d16_d31; + // VFP registers D0-D15, + padding if saved using FSTMX + unw_fpreg_t _vfp_d0_d15_pad[17]; + // VFPv3 registers D16-D31, always saved using FSTMD + unw_fpreg_t _vfp_d16_d31[16]; +#if defined(__ARM_WMMX) + // Whether iWMMX data registers are saved. + bool _saved_iwmmx; + // Whether iWMMX control registers are saved. + mutable bool _saved_iwmmx_control; + // iWMMX registers + unw_fpreg_t _iwmmx[16]; + // iWMMX control registers + mutable uint32_t _iwmmx_control[4]; +#endif +}; + +inline Registers_arm::Registers_arm(const void *registers) + : _use_X_for_vfp_save(false), + _saved_vfp_d0_d15(false), + _saved_vfp_d16_d31(false) { + static_assert((check_fit::does_fit), + "arm registers do not fit into unw_context_t"); + // See __unw_getcontext() note about data. + memcpy(&_registers, registers, sizeof(_registers)); + memset(&_pseudo_registers, 0, sizeof(_pseudo_registers)); + memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad)); + memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31)); +#if defined(__ARM_WMMX) + _saved_iwmmx = false; + _saved_iwmmx_control = false; + memset(&_iwmmx, 0, sizeof(_iwmmx)); + memset(&_iwmmx_control, 0, sizeof(_iwmmx_control)); +#endif +} + +inline Registers_arm::Registers_arm() + : _use_X_for_vfp_save(false), + _saved_vfp_d0_d15(false), + _saved_vfp_d16_d31(false) { + memset(&_registers, 0, sizeof(_registers)); + memset(&_pseudo_registers, 0, sizeof(_pseudo_registers)); + memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad)); + memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31)); +#if defined(__ARM_WMMX) + _saved_iwmmx = false; + _saved_iwmmx_control = false; + memset(&_iwmmx, 0, sizeof(_iwmmx)); + memset(&_iwmmx_control, 0, sizeof(_iwmmx_control)); +#endif +} + +inline bool Registers_arm::validRegister(int regNum) const { + // Returns true for all non-VFP registers supported by the EHABI + // virtual register set (VRS). + if (regNum == UNW_REG_IP) + return true; + + if (regNum == UNW_REG_SP) + return true; + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) + return true; + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) + return true; +#endif + +#ifdef __ARM_FEATURE_PAUTH + if (regNum == UNW_ARM_RA_AUTH_CODE) + return true; +#endif + + return false; +} + +inline uint32_t Registers_arm::getRegister(int regNum) const { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) + return _registers.__sp; + + if (regNum == UNW_ARM_LR) + return _registers.__lr; + + if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) + return _registers.__pc; + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R12) + return _registers.__r[regNum]; + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) { + if (!_saved_iwmmx_control) { + _saved_iwmmx_control = true; + saveiWMMXControl(_iwmmx_control); + } + return _iwmmx_control[regNum - UNW_ARM_WC0]; + } +#endif + +#ifdef __ARM_FEATURE_PAUTH + if (regNum == UNW_ARM_RA_AUTH_CODE) + return _pseudo_registers.__pac; +#endif + + _LIBUNWIND_ABORT("unsupported arm register"); +} + +inline void Registers_arm::setRegister(int regNum, uint32_t value) { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) { + _registers.__sp = value; + return; + } + + if (regNum == UNW_ARM_LR) { + _registers.__lr = value; + return; + } + + if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) { + _registers.__pc = value; + return; + } + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R12) { + _registers.__r[regNum] = value; + return; + } + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) { + if (!_saved_iwmmx_control) { + _saved_iwmmx_control = true; + saveiWMMXControl(_iwmmx_control); + } + _iwmmx_control[regNum - UNW_ARM_WC0] = value; + return; + } +#endif + + if (regNum == UNW_ARM_RA_AUTH_CODE) { + _pseudo_registers.__pac = value; + return; + } + + _LIBUNWIND_ABORT("unsupported arm register"); +} + +inline const char *Registers_arm::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + case UNW_ARM_IP: // UNW_ARM_R15 is alias + return "pc"; + case UNW_ARM_LR: // UNW_ARM_R14 is alias + return "lr"; + case UNW_REG_SP: + case UNW_ARM_SP: // UNW_ARM_R13 is alias + return "sp"; + case UNW_ARM_R0: + return "r0"; + case UNW_ARM_R1: + return "r1"; + case UNW_ARM_R2: + return "r2"; + case UNW_ARM_R3: + return "r3"; + case UNW_ARM_R4: + return "r4"; + case UNW_ARM_R5: + return "r5"; + case UNW_ARM_R6: + return "r6"; + case UNW_ARM_R7: + return "r7"; + case UNW_ARM_R8: + return "r8"; + case UNW_ARM_R9: + return "r9"; + case UNW_ARM_R10: + return "r10"; + case UNW_ARM_R11: + return "r11"; + case UNW_ARM_R12: + return "r12"; + case UNW_ARM_S0: + return "s0"; + case UNW_ARM_S1: + return "s1"; + case UNW_ARM_S2: + return "s2"; + case UNW_ARM_S3: + return "s3"; + case UNW_ARM_S4: + return "s4"; + case UNW_ARM_S5: + return "s5"; + case UNW_ARM_S6: + return "s6"; + case UNW_ARM_S7: + return "s7"; + case UNW_ARM_S8: + return "s8"; + case UNW_ARM_S9: + return "s9"; + case UNW_ARM_S10: + return "s10"; + case UNW_ARM_S11: + return "s11"; + case UNW_ARM_S12: + return "s12"; + case UNW_ARM_S13: + return "s13"; + case UNW_ARM_S14: + return "s14"; + case UNW_ARM_S15: + return "s15"; + case UNW_ARM_S16: + return "s16"; + case UNW_ARM_S17: + return "s17"; + case UNW_ARM_S18: + return "s18"; + case UNW_ARM_S19: + return "s19"; + case UNW_ARM_S20: + return "s20"; + case UNW_ARM_S21: + return "s21"; + case UNW_ARM_S22: + return "s22"; + case UNW_ARM_S23: + return "s23"; + case UNW_ARM_S24: + return "s24"; + case UNW_ARM_S25: + return "s25"; + case UNW_ARM_S26: + return "s26"; + case UNW_ARM_S27: + return "s27"; + case UNW_ARM_S28: + return "s28"; + case UNW_ARM_S29: + return "s29"; + case UNW_ARM_S30: + return "s30"; + case UNW_ARM_S31: + return "s31"; + case UNW_ARM_D0: + return "d0"; + case UNW_ARM_D1: + return "d1"; + case UNW_ARM_D2: + return "d2"; + case UNW_ARM_D3: + return "d3"; + case UNW_ARM_D4: + return "d4"; + case UNW_ARM_D5: + return "d5"; + case UNW_ARM_D6: + return "d6"; + case UNW_ARM_D7: + return "d7"; + case UNW_ARM_D8: + return "d8"; + case UNW_ARM_D9: + return "d9"; + case UNW_ARM_D10: + return "d10"; + case UNW_ARM_D11: + return "d11"; + case UNW_ARM_D12: + return "d12"; + case UNW_ARM_D13: + return "d13"; + case UNW_ARM_D14: + return "d14"; + case UNW_ARM_D15: + return "d15"; + case UNW_ARM_D16: + return "d16"; + case UNW_ARM_D17: + return "d17"; + case UNW_ARM_D18: + return "d18"; + case UNW_ARM_D19: + return "d19"; + case UNW_ARM_D20: + return "d20"; + case UNW_ARM_D21: + return "d21"; + case UNW_ARM_D22: + return "d22"; + case UNW_ARM_D23: + return "d23"; + case UNW_ARM_D24: + return "d24"; + case UNW_ARM_D25: + return "d25"; + case UNW_ARM_D26: + return "d26"; + case UNW_ARM_D27: + return "d27"; + case UNW_ARM_D28: + return "d28"; + case UNW_ARM_D29: + return "d29"; + case UNW_ARM_D30: + return "d30"; + case UNW_ARM_D31: + return "d31"; + default: + return "unknown register"; + } +} + +inline bool Registers_arm::validFloatRegister(int regNum) const { + // NOTE: Consider the intel MMX registers floating points so the + // __unw_get_fpreg can be used to transmit the 64-bit data back. + return ((regNum >= UNW_ARM_D0) && (regNum <= UNW_ARM_D31)) +#if defined(__ARM_WMMX) + || ((regNum >= UNW_ARM_WR0) && (regNum <= UNW_ARM_WR15)) +#endif + ; +} + +inline unw_fpreg_t Registers_arm::getFloatRegister(int regNum) { + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D15) { + if (!_saved_vfp_d0_d15) { + _saved_vfp_d0_d15 = true; + if (_use_X_for_vfp_save) + saveVFPWithFSTMX(_vfp_d0_d15_pad); + else + saveVFPWithFSTMD(_vfp_d0_d15_pad); + } + return _vfp_d0_d15_pad[regNum - UNW_ARM_D0]; + } + + if (regNum >= UNW_ARM_D16 && regNum <= UNW_ARM_D31) { + if (!_saved_vfp_d16_d31) { + _saved_vfp_d16_d31 = true; + saveVFPv3(_vfp_d16_d31); + } + return _vfp_d16_d31[regNum - UNW_ARM_D16]; + } + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WR0 && regNum <= UNW_ARM_WR15) { + if (!_saved_iwmmx) { + _saved_iwmmx = true; + saveiWMMX(_iwmmx); + } + return _iwmmx[regNum - UNW_ARM_WR0]; + } +#endif + + _LIBUNWIND_ABORT("Unknown ARM float register"); +} + +inline void Registers_arm::setFloatRegister(int regNum, unw_fpreg_t value) { + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D15) { + if (!_saved_vfp_d0_d15) { + _saved_vfp_d0_d15 = true; + if (_use_X_for_vfp_save) + saveVFPWithFSTMX(_vfp_d0_d15_pad); + else + saveVFPWithFSTMD(_vfp_d0_d15_pad); + } + _vfp_d0_d15_pad[regNum - UNW_ARM_D0] = value; + return; + } + + if (regNum >= UNW_ARM_D16 && regNum <= UNW_ARM_D31) { + if (!_saved_vfp_d16_d31) { + _saved_vfp_d16_d31 = true; + saveVFPv3(_vfp_d16_d31); + } + _vfp_d16_d31[regNum - UNW_ARM_D16] = value; + return; + } + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WR0 && regNum <= UNW_ARM_WR15) { + if (!_saved_iwmmx) { + _saved_iwmmx = true; + saveiWMMX(_iwmmx); + } + _iwmmx[regNum - UNW_ARM_WR0] = value; + return; + } +#endif + + _LIBUNWIND_ABORT("Unknown ARM float register"); +} + +inline bool Registers_arm::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_arm::getVectorRegister(int) const { + _LIBUNWIND_ABORT("ARM vector support not implemented"); +} + +inline void Registers_arm::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("ARM vector support not implemented"); +} +#endif // _LIBUNWIND_TARGET_ARM + + +#if defined(_LIBUNWIND_TARGET_OR1K) +/// Registers_or1k holds the register state of a thread in an OpenRISC1000 +/// process. +class _LIBUNWIND_HIDDEN Registers_or1k { +public: + Registers_or1k(); + Registers_or1k(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K; + } + static int getArch() { return REGISTERS_OR1K; } + + uint64_t getSP() const { return _registers.__r[1]; } + void setSP(uint32_t value) { _registers.__r[1] = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint32_t value) { _registers.__pc = value; } + +private: + struct or1k_thread_state_t { + unsigned int __r[32]; // r0-r31 + unsigned int __pc; // Program counter + unsigned int __epcr; // Program counter at exception + }; + + or1k_thread_state_t _registers; +}; + +inline Registers_or1k::Registers_or1k(const void *registers) { + static_assert((check_fit::does_fit), + "or1k registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_or1k::Registers_or1k() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_or1k::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_OR1K_R31) + return true; + if (regNum == UNW_OR1K_EPCR) + return true; + return false; +} + +inline uint32_t Registers_or1k::getRegister(int regNum) const { + if (regNum >= UNW_OR1K_R0 && regNum <= UNW_OR1K_R31) + return _registers.__r[regNum - UNW_OR1K_R0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__pc; + case UNW_REG_SP: + return _registers.__r[1]; + case UNW_OR1K_EPCR: + return _registers.__epcr; + } + _LIBUNWIND_ABORT("unsupported or1k register"); +} + +inline void Registers_or1k::setRegister(int regNum, uint32_t value) { + if (regNum >= UNW_OR1K_R0 && regNum <= UNW_OR1K_R31) { + _registers.__r[regNum - UNW_OR1K_R0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__pc = value; + return; + case UNW_REG_SP: + _registers.__r[1] = value; + return; + case UNW_OR1K_EPCR: + _registers.__epcr = value; + return; + } + _LIBUNWIND_ABORT("unsupported or1k register"); +} + +inline bool Registers_or1k::validFloatRegister(int /* regNum */) const { + return false; +} + +inline double Registers_or1k::getFloatRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("or1k float support not implemented"); +} + +inline void Registers_or1k::setFloatRegister(int /* regNum */, + double /* value */) { + _LIBUNWIND_ABORT("or1k float support not implemented"); +} + +inline bool Registers_or1k::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_or1k::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("or1k vector support not implemented"); +} + +inline void Registers_or1k::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("or1k vector support not implemented"); +} + +inline const char *Registers_or1k::getRegisterName(int regNum) { + switch (regNum) { + case UNW_OR1K_R0: + return "r0"; + case UNW_OR1K_R1: + return "r1"; + case UNW_OR1K_R2: + return "r2"; + case UNW_OR1K_R3: + return "r3"; + case UNW_OR1K_R4: + return "r4"; + case UNW_OR1K_R5: + return "r5"; + case UNW_OR1K_R6: + return "r6"; + case UNW_OR1K_R7: + return "r7"; + case UNW_OR1K_R8: + return "r8"; + case UNW_OR1K_R9: + return "r9"; + case UNW_OR1K_R10: + return "r10"; + case UNW_OR1K_R11: + return "r11"; + case UNW_OR1K_R12: + return "r12"; + case UNW_OR1K_R13: + return "r13"; + case UNW_OR1K_R14: + return "r14"; + case UNW_OR1K_R15: + return "r15"; + case UNW_OR1K_R16: + return "r16"; + case UNW_OR1K_R17: + return "r17"; + case UNW_OR1K_R18: + return "r18"; + case UNW_OR1K_R19: + return "r19"; + case UNW_OR1K_R20: + return "r20"; + case UNW_OR1K_R21: + return "r21"; + case UNW_OR1K_R22: + return "r22"; + case UNW_OR1K_R23: + return "r23"; + case UNW_OR1K_R24: + return "r24"; + case UNW_OR1K_R25: + return "r25"; + case UNW_OR1K_R26: + return "r26"; + case UNW_OR1K_R27: + return "r27"; + case UNW_OR1K_R28: + return "r28"; + case UNW_OR1K_R29: + return "r29"; + case UNW_OR1K_R30: + return "r30"; + case UNW_OR1K_R31: + return "r31"; + case UNW_OR1K_EPCR: + return "EPCR"; + default: + return "unknown register"; + } + +} +#endif // _LIBUNWIND_TARGET_OR1K + +#if defined(_LIBUNWIND_TARGET_MIPS_O32) +/// Registers_mips_o32 holds the register state of a thread in a 32-bit MIPS +/// process. +class _LIBUNWIND_HIDDEN Registers_mips_o32 { +public: + Registers_mips_o32(); + Registers_mips_o32(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; + } + static int getArch() { return REGISTERS_MIPS_O32; } + + uint32_t getSP() const { return _registers.__r[29]; } + void setSP(uint32_t value) { _registers.__r[29] = value; } + uint32_t getIP() const { return _registers.__pc; } + void setIP(uint32_t value) { _registers.__pc = value; } + +private: + struct mips_o32_thread_state_t { + uint32_t __r[32]; + uint32_t __pc; + uint32_t __hi; + uint32_t __lo; + }; + + mips_o32_thread_state_t _registers; +#ifdef __mips_hard_float + /// O32 with 32-bit floating point registers only uses half of this + /// space. However, using the same layout for 32-bit vs 64-bit + /// floating point registers results in a single context size for + /// O32 with hard float. + uint32_t _padding; + double _floats[32]; +#endif +}; + +inline Registers_mips_o32::Registers_mips_o32(const void *registers) { + static_assert((check_fit::does_fit), + "mips_o32 registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_mips_o32::Registers_mips_o32() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_mips_o32::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_MIPS_R31) + return true; +#if __mips_isa_rev != 6 + if (regNum == UNW_MIPS_HI) + return true; + if (regNum == UNW_MIPS_LO) + return true; +#endif +#if defined(__mips_hard_float) && __mips_fpr == 32 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) + return true; +#endif + // FIXME: DSP accumulator registers, MSA registers + return false; +} + +inline uint32_t Registers_mips_o32::getRegister(int regNum) const { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) + return _registers.__r[regNum - UNW_MIPS_R0]; +#if defined(__mips_hard_float) && __mips_fpr == 32 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) { + uint32_t *p; + + if (regNum % 2 == 0) + p = (uint32_t *)&_floats[regNum - UNW_MIPS_F0]; + else + p = (uint32_t *)&_floats[(regNum - 1) - UNW_MIPS_F0] + 1; + return *p; + } +#endif + + switch (regNum) { + case UNW_REG_IP: + return _registers.__pc; + case UNW_REG_SP: + return _registers.__r[29]; + case UNW_MIPS_HI: + return _registers.__hi; + case UNW_MIPS_LO: + return _registers.__lo; + } + _LIBUNWIND_ABORT("unsupported mips_o32 register"); +} + +inline void Registers_mips_o32::setRegister(int regNum, uint32_t value) { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) { + _registers.__r[regNum - UNW_MIPS_R0] = value; + return; + } +#if defined(__mips_hard_float) && __mips_fpr == 32 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) { + uint32_t *p; + + if (regNum % 2 == 0) + p = (uint32_t *)&_floats[regNum - UNW_MIPS_F0]; + else + p = (uint32_t *)&_floats[(regNum - 1) - UNW_MIPS_F0] + 1; + *p = value; + return; + } +#endif + + switch (regNum) { + case UNW_REG_IP: + _registers.__pc = value; + return; + case UNW_REG_SP: + _registers.__r[29] = value; + return; + case UNW_MIPS_HI: + _registers.__hi = value; + return; + case UNW_MIPS_LO: + _registers.__lo = value; + return; + } + _LIBUNWIND_ABORT("unsupported mips_o32 register"); +} + +inline bool Registers_mips_o32::validFloatRegister(int regNum) const { +#if defined(__mips_hard_float) && __mips_fpr == 64 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) + return true; +#else + (void)regNum; +#endif + return false; +} + +inline double Registers_mips_o32::getFloatRegister(int regNum) const { +#if defined(__mips_hard_float) && __mips_fpr == 64 + assert(validFloatRegister(regNum)); + return _floats[regNum - UNW_MIPS_F0]; +#else + (void)regNum; + _LIBUNWIND_ABORT("mips_o32 float support not implemented"); +#endif +} + +inline void Registers_mips_o32::setFloatRegister(int regNum, + double value) { +#if defined(__mips_hard_float) && __mips_fpr == 64 + assert(validFloatRegister(regNum)); + _floats[regNum - UNW_MIPS_F0] = value; +#else + (void)regNum; + (void)value; + _LIBUNWIND_ABORT("mips_o32 float support not implemented"); +#endif +} + +inline bool Registers_mips_o32::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_mips_o32::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("mips_o32 vector support not implemented"); +} + +inline void Registers_mips_o32::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("mips_o32 vector support not implemented"); +} + +inline const char *Registers_mips_o32::getRegisterName(int regNum) { + switch (regNum) { + case UNW_MIPS_R0: + return "$0"; + case UNW_MIPS_R1: + return "$1"; + case UNW_MIPS_R2: + return "$2"; + case UNW_MIPS_R3: + return "$3"; + case UNW_MIPS_R4: + return "$4"; + case UNW_MIPS_R5: + return "$5"; + case UNW_MIPS_R6: + return "$6"; + case UNW_MIPS_R7: + return "$7"; + case UNW_MIPS_R8: + return "$8"; + case UNW_MIPS_R9: + return "$9"; + case UNW_MIPS_R10: + return "$10"; + case UNW_MIPS_R11: + return "$11"; + case UNW_MIPS_R12: + return "$12"; + case UNW_MIPS_R13: + return "$13"; + case UNW_MIPS_R14: + return "$14"; + case UNW_MIPS_R15: + return "$15"; + case UNW_MIPS_R16: + return "$16"; + case UNW_MIPS_R17: + return "$17"; + case UNW_MIPS_R18: + return "$18"; + case UNW_MIPS_R19: + return "$19"; + case UNW_MIPS_R20: + return "$20"; + case UNW_MIPS_R21: + return "$21"; + case UNW_MIPS_R22: + return "$22"; + case UNW_MIPS_R23: + return "$23"; + case UNW_MIPS_R24: + return "$24"; + case UNW_MIPS_R25: + return "$25"; + case UNW_MIPS_R26: + return "$26"; + case UNW_MIPS_R27: + return "$27"; + case UNW_MIPS_R28: + return "$28"; + case UNW_MIPS_R29: + return "$29"; + case UNW_MIPS_R30: + return "$30"; + case UNW_MIPS_R31: + return "$31"; + case UNW_MIPS_F0: + return "$f0"; + case UNW_MIPS_F1: + return "$f1"; + case UNW_MIPS_F2: + return "$f2"; + case UNW_MIPS_F3: + return "$f3"; + case UNW_MIPS_F4: + return "$f4"; + case UNW_MIPS_F5: + return "$f5"; + case UNW_MIPS_F6: + return "$f6"; + case UNW_MIPS_F7: + return "$f7"; + case UNW_MIPS_F8: + return "$f8"; + case UNW_MIPS_F9: + return "$f9"; + case UNW_MIPS_F10: + return "$f10"; + case UNW_MIPS_F11: + return "$f11"; + case UNW_MIPS_F12: + return "$f12"; + case UNW_MIPS_F13: + return "$f13"; + case UNW_MIPS_F14: + return "$f14"; + case UNW_MIPS_F15: + return "$f15"; + case UNW_MIPS_F16: + return "$f16"; + case UNW_MIPS_F17: + return "$f17"; + case UNW_MIPS_F18: + return "$f18"; + case UNW_MIPS_F19: + return "$f19"; + case UNW_MIPS_F20: + return "$f20"; + case UNW_MIPS_F21: + return "$f21"; + case UNW_MIPS_F22: + return "$f22"; + case UNW_MIPS_F23: + return "$f23"; + case UNW_MIPS_F24: + return "$f24"; + case UNW_MIPS_F25: + return "$f25"; + case UNW_MIPS_F26: + return "$f26"; + case UNW_MIPS_F27: + return "$f27"; + case UNW_MIPS_F28: + return "$f28"; + case UNW_MIPS_F29: + return "$f29"; + case UNW_MIPS_F30: + return "$f30"; + case UNW_MIPS_F31: + return "$f31"; + case UNW_MIPS_HI: + return "$hi"; + case UNW_MIPS_LO: + return "$lo"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_MIPS_O32 + +#if defined(_LIBUNWIND_TARGET_MIPS_NEWABI) +/// Registers_mips_newabi holds the register state of a thread in a +/// MIPS process using NEWABI (the N32 or N64 ABIs). +class _LIBUNWIND_HIDDEN Registers_mips_newabi { +public: + Registers_mips_newabi(); + Registers_mips_newabi(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; + } + static int getArch() { return REGISTERS_MIPS_NEWABI; } + + uint64_t getSP() const { return _registers.__r[29]; } + void setSP(uint64_t value) { _registers.__r[29] = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint64_t value) { _registers.__pc = value; } + +private: + struct mips_newabi_thread_state_t { + uint64_t __r[32]; + uint64_t __pc; + uint64_t __hi; + uint64_t __lo; + }; + + mips_newabi_thread_state_t _registers; +#ifdef __mips_hard_float + double _floats[32]; +#endif +}; + +inline Registers_mips_newabi::Registers_mips_newabi(const void *registers) { + static_assert((check_fit::does_fit), + "mips_newabi registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_mips_newabi::Registers_mips_newabi() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_mips_newabi::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_MIPS_R31) + return true; +#if __mips_isa_rev != 6 + if (regNum == UNW_MIPS_HI) + return true; + if (regNum == UNW_MIPS_LO) + return true; +#endif + // FIXME: Hard float, DSP accumulator registers, MSA registers + return false; +} + +inline uint64_t Registers_mips_newabi::getRegister(int regNum) const { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) + return _registers.__r[regNum - UNW_MIPS_R0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__pc; + case UNW_REG_SP: + return _registers.__r[29]; + case UNW_MIPS_HI: + return _registers.__hi; + case UNW_MIPS_LO: + return _registers.__lo; + } + _LIBUNWIND_ABORT("unsupported mips_newabi register"); +} + +inline void Registers_mips_newabi::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) { + _registers.__r[regNum - UNW_MIPS_R0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__pc = value; + return; + case UNW_REG_SP: + _registers.__r[29] = value; + return; + case UNW_MIPS_HI: + _registers.__hi = value; + return; + case UNW_MIPS_LO: + _registers.__lo = value; + return; + } + _LIBUNWIND_ABORT("unsupported mips_newabi register"); +} + +inline bool Registers_mips_newabi::validFloatRegister(int regNum) const { +#ifdef __mips_hard_float + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) + return true; +#else + (void)regNum; +#endif + return false; +} + +inline double Registers_mips_newabi::getFloatRegister(int regNum) const { +#ifdef __mips_hard_float + assert(validFloatRegister(regNum)); + return _floats[regNum - UNW_MIPS_F0]; +#else + (void)regNum; + _LIBUNWIND_ABORT("mips_newabi float support not implemented"); +#endif +} + +inline void Registers_mips_newabi::setFloatRegister(int regNum, + double value) { +#ifdef __mips_hard_float + assert(validFloatRegister(regNum)); + _floats[regNum - UNW_MIPS_F0] = value; +#else + (void)regNum; + (void)value; + _LIBUNWIND_ABORT("mips_newabi float support not implemented"); +#endif +} + +inline bool Registers_mips_newabi::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_mips_newabi::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("mips_newabi vector support not implemented"); +} + +inline void Registers_mips_newabi::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("mips_newabi vector support not implemented"); +} + +inline const char *Registers_mips_newabi::getRegisterName(int regNum) { + switch (regNum) { + case UNW_MIPS_R0: + return "$0"; + case UNW_MIPS_R1: + return "$1"; + case UNW_MIPS_R2: + return "$2"; + case UNW_MIPS_R3: + return "$3"; + case UNW_MIPS_R4: + return "$4"; + case UNW_MIPS_R5: + return "$5"; + case UNW_MIPS_R6: + return "$6"; + case UNW_MIPS_R7: + return "$7"; + case UNW_MIPS_R8: + return "$8"; + case UNW_MIPS_R9: + return "$9"; + case UNW_MIPS_R10: + return "$10"; + case UNW_MIPS_R11: + return "$11"; + case UNW_MIPS_R12: + return "$12"; + case UNW_MIPS_R13: + return "$13"; + case UNW_MIPS_R14: + return "$14"; + case UNW_MIPS_R15: + return "$15"; + case UNW_MIPS_R16: + return "$16"; + case UNW_MIPS_R17: + return "$17"; + case UNW_MIPS_R18: + return "$18"; + case UNW_MIPS_R19: + return "$19"; + case UNW_MIPS_R20: + return "$20"; + case UNW_MIPS_R21: + return "$21"; + case UNW_MIPS_R22: + return "$22"; + case UNW_MIPS_R23: + return "$23"; + case UNW_MIPS_R24: + return "$24"; + case UNW_MIPS_R25: + return "$25"; + case UNW_MIPS_R26: + return "$26"; + case UNW_MIPS_R27: + return "$27"; + case UNW_MIPS_R28: + return "$28"; + case UNW_MIPS_R29: + return "$29"; + case UNW_MIPS_R30: + return "$30"; + case UNW_MIPS_R31: + return "$31"; + case UNW_MIPS_F0: + return "$f0"; + case UNW_MIPS_F1: + return "$f1"; + case UNW_MIPS_F2: + return "$f2"; + case UNW_MIPS_F3: + return "$f3"; + case UNW_MIPS_F4: + return "$f4"; + case UNW_MIPS_F5: + return "$f5"; + case UNW_MIPS_F6: + return "$f6"; + case UNW_MIPS_F7: + return "$f7"; + case UNW_MIPS_F8: + return "$f8"; + case UNW_MIPS_F9: + return "$f9"; + case UNW_MIPS_F10: + return "$f10"; + case UNW_MIPS_F11: + return "$f11"; + case UNW_MIPS_F12: + return "$f12"; + case UNW_MIPS_F13: + return "$f13"; + case UNW_MIPS_F14: + return "$f14"; + case UNW_MIPS_F15: + return "$f15"; + case UNW_MIPS_F16: + return "$f16"; + case UNW_MIPS_F17: + return "$f17"; + case UNW_MIPS_F18: + return "$f18"; + case UNW_MIPS_F19: + return "$f19"; + case UNW_MIPS_F20: + return "$f20"; + case UNW_MIPS_F21: + return "$f21"; + case UNW_MIPS_F22: + return "$f22"; + case UNW_MIPS_F23: + return "$f23"; + case UNW_MIPS_F24: + return "$f24"; + case UNW_MIPS_F25: + return "$f25"; + case UNW_MIPS_F26: + return "$f26"; + case UNW_MIPS_F27: + return "$f27"; + case UNW_MIPS_F28: + return "$f28"; + case UNW_MIPS_F29: + return "$f29"; + case UNW_MIPS_F30: + return "$f30"; + case UNW_MIPS_F31: + return "$f31"; + case UNW_MIPS_HI: + return "$hi"; + case UNW_MIPS_LO: + return "$lo"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_MIPS_NEWABI + +#if defined(_LIBUNWIND_TARGET_SPARC) +/// Registers_sparc holds the register state of a thread in a 32-bit Sparc +/// process. +class _LIBUNWIND_HIDDEN Registers_sparc { +public: + Registers_sparc(); + Registers_sparc(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC; + } + static int getArch() { return REGISTERS_SPARC; } + + uint64_t getSP() const { return _registers.__regs[UNW_SPARC_O6]; } + void setSP(uint32_t value) { _registers.__regs[UNW_SPARC_O6] = value; } + uint64_t getIP() const { return _registers.__regs[UNW_SPARC_O7]; } + void setIP(uint32_t value) { _registers.__regs[UNW_SPARC_O7] = value; } + +private: + struct sparc_thread_state_t { + unsigned int __regs[32]; + }; + + sparc_thread_state_t _registers; +}; + +inline Registers_sparc::Registers_sparc(const void *registers) { + static_assert((check_fit::does_fit), + "sparc registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_sparc::Registers_sparc() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_sparc::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_SPARC_I7) + return true; + return false; +} + +inline uint32_t Registers_sparc::getRegister(int regNum) const { + if ((UNW_SPARC_G0 <= regNum) && (regNum <= UNW_SPARC_I7)) { + return _registers.__regs[regNum]; + } + + switch (regNum) { + case UNW_REG_IP: + return _registers.__regs[UNW_SPARC_O7]; + case UNW_REG_SP: + return _registers.__regs[UNW_SPARC_O6]; + } + _LIBUNWIND_ABORT("unsupported sparc register"); +} + +inline void Registers_sparc::setRegister(int regNum, uint32_t value) { + if ((UNW_SPARC_G0 <= regNum) && (regNum <= UNW_SPARC_I7)) { + _registers.__regs[regNum] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__regs[UNW_SPARC_O7] = value; + return; + case UNW_REG_SP: + _registers.__regs[UNW_SPARC_O6] = value; + return; + } + _LIBUNWIND_ABORT("unsupported sparc register"); +} + +inline bool Registers_sparc::validFloatRegister(int) const { return false; } + +inline double Registers_sparc::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no Sparc float registers"); +} + +inline void Registers_sparc::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no Sparc float registers"); +} + +inline bool Registers_sparc::validVectorRegister(int) const { return false; } + +inline v128 Registers_sparc::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no Sparc vector registers"); +} + +inline void Registers_sparc::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no Sparc vector registers"); +} + +inline const char *Registers_sparc::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_SPARC_G0: + return "g0"; + case UNW_SPARC_G1: + return "g1"; + case UNW_SPARC_G2: + return "g2"; + case UNW_SPARC_G3: + return "g3"; + case UNW_SPARC_G4: + return "g4"; + case UNW_SPARC_G5: + return "g5"; + case UNW_SPARC_G6: + return "g6"; + case UNW_SPARC_G7: + return "g7"; + case UNW_SPARC_O0: + return "o0"; + case UNW_SPARC_O1: + return "o1"; + case UNW_SPARC_O2: + return "o2"; + case UNW_SPARC_O3: + return "o3"; + case UNW_SPARC_O4: + return "o4"; + case UNW_SPARC_O5: + return "o5"; + case UNW_REG_SP: + case UNW_SPARC_O6: + return "sp"; + case UNW_SPARC_O7: + return "o7"; + case UNW_SPARC_L0: + return "l0"; + case UNW_SPARC_L1: + return "l1"; + case UNW_SPARC_L2: + return "l2"; + case UNW_SPARC_L3: + return "l3"; + case UNW_SPARC_L4: + return "l4"; + case UNW_SPARC_L5: + return "l5"; + case UNW_SPARC_L6: + return "l6"; + case UNW_SPARC_L7: + return "l7"; + case UNW_SPARC_I0: + return "i0"; + case UNW_SPARC_I1: + return "i1"; + case UNW_SPARC_I2: + return "i2"; + case UNW_SPARC_I3: + return "i3"; + case UNW_SPARC_I4: + return "i4"; + case UNW_SPARC_I5: + return "i5"; + case UNW_SPARC_I6: + return "fp"; + case UNW_SPARC_I7: + return "i7"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_SPARC + +#if defined(_LIBUNWIND_TARGET_SPARC64) +/// Registers_sparc64 holds the register state of a thread in a 64-bit +/// sparc process. +class _LIBUNWIND_HIDDEN Registers_sparc64 { +public: + Registers_sparc64() = default; + Registers_sparc64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC64; + } + static int getArch() { return REGISTERS_SPARC64; } + + uint64_t getSP() const { return _registers.__regs[UNW_SPARC_O6] + 2047; } + void setSP(uint64_t value) { _registers.__regs[UNW_SPARC_O6] = value - 2047; } + uint64_t getIP() const { return _registers.__regs[UNW_SPARC_O7]; } + void setIP(uint64_t value) { _registers.__regs[UNW_SPARC_O7] = value; } + uint64_t getWCookie() const { return _wcookie; } + +private: + struct sparc64_thread_state_t { + uint64_t __regs[32]; + }; + + sparc64_thread_state_t _registers{}; + uint64_t _wcookie = 0; +}; + +inline Registers_sparc64::Registers_sparc64(const void *registers) { + static_assert((check_fit::does_fit), + "sparc64 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); + memcpy(&_wcookie, + static_cast(registers) + sizeof(_registers), + sizeof(_wcookie)); +} + +inline bool Registers_sparc64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_SPARC_I7) + return true; + return false; +} + +inline uint64_t Registers_sparc64::getRegister(int regNum) const { + if (regNum >= UNW_SPARC_G0 && regNum <= UNW_SPARC_I7) + return _registers.__regs[regNum]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__regs[UNW_SPARC_O7]; + case UNW_REG_SP: + return _registers.__regs[UNW_SPARC_O6] + 2047; + } + _LIBUNWIND_ABORT("unsupported sparc64 register"); +} + +inline void Registers_sparc64::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_SPARC_G0 && regNum <= UNW_SPARC_I7) { + _registers.__regs[regNum] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__regs[UNW_SPARC_O7] = value; + return; + case UNW_REG_SP: + _registers.__regs[UNW_SPARC_O6] = value - 2047; + return; + } + _LIBUNWIND_ABORT("unsupported sparc64 register"); +} + +inline bool Registers_sparc64::validFloatRegister(int) const { return false; } + +inline double Registers_sparc64::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no sparc64 float registers"); +} + +inline void Registers_sparc64::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no sparc64 float registers"); +} + +inline bool Registers_sparc64::validVectorRegister(int) const { return false; } + +inline v128 Registers_sparc64::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no sparc64 vector registers"); +} + +inline void Registers_sparc64::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no sparc64 vector registers"); +} + +inline const char *Registers_sparc64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_SPARC_G0: + return "g0"; + case UNW_SPARC_G1: + return "g1"; + case UNW_SPARC_G2: + return "g2"; + case UNW_SPARC_G3: + return "g3"; + case UNW_SPARC_G4: + return "g4"; + case UNW_SPARC_G5: + return "g5"; + case UNW_SPARC_G6: + return "g6"; + case UNW_SPARC_G7: + return "g7"; + case UNW_SPARC_O0: + return "o0"; + case UNW_SPARC_O1: + return "o1"; + case UNW_SPARC_O2: + return "o2"; + case UNW_SPARC_O3: + return "o3"; + case UNW_SPARC_O4: + return "o4"; + case UNW_SPARC_O5: + return "o5"; + case UNW_REG_SP: + case UNW_SPARC_O6: + return "o6"; + case UNW_SPARC_O7: + return "o7"; + case UNW_SPARC_L0: + return "l0"; + case UNW_SPARC_L1: + return "l1"; + case UNW_SPARC_L2: + return "l2"; + case UNW_SPARC_L3: + return "l3"; + case UNW_SPARC_L4: + return "l4"; + case UNW_SPARC_L5: + return "l5"; + case UNW_SPARC_L6: + return "l6"; + case UNW_SPARC_L7: + return "l7"; + case UNW_SPARC_I0: + return "i0"; + case UNW_SPARC_I1: + return "i1"; + case UNW_SPARC_I2: + return "i2"; + case UNW_SPARC_I3: + return "i3"; + case UNW_SPARC_I4: + return "i4"; + case UNW_SPARC_I5: + return "i5"; + case UNW_SPARC_I6: + return "i6"; + case UNW_SPARC_I7: + return "i7"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_SPARC64 + +#if defined(_LIBUNWIND_TARGET_HEXAGON) +/// Registers_hexagon holds the register state of a thread in a Hexagon QDSP6 +/// process. +class _LIBUNWIND_HIDDEN Registers_hexagon { +public: + Registers_hexagon(); + Registers_hexagon(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON; + } + static int getArch() { return REGISTERS_HEXAGON; } + + uint32_t getSP() const { return _registers.__r[UNW_HEXAGON_R29]; } + void setSP(uint32_t value) { _registers.__r[UNW_HEXAGON_R29] = value; } + uint32_t getIP() const { return _registers.__r[UNW_HEXAGON_PC]; } + void setIP(uint32_t value) { _registers.__r[UNW_HEXAGON_PC] = value; } + +private: + struct hexagon_thread_state_t { + unsigned int __r[35]; + }; + + hexagon_thread_state_t _registers; +}; + +inline Registers_hexagon::Registers_hexagon(const void *registers) { + static_assert((check_fit::does_fit), + "hexagon registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_hexagon::Registers_hexagon() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_hexagon::validRegister(int regNum) const { + if (regNum <= UNW_HEXAGON_R31) + return true; + return false; +} + +inline uint32_t Registers_hexagon::getRegister(int regNum) const { + if (regNum >= UNW_HEXAGON_R0 && regNum <= UNW_HEXAGON_R31) + return _registers.__r[regNum - UNW_HEXAGON_R0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__r[UNW_HEXAGON_PC]; + case UNW_REG_SP: + return _registers.__r[UNW_HEXAGON_R29]; + } + _LIBUNWIND_ABORT("unsupported hexagon register"); +} + +inline void Registers_hexagon::setRegister(int regNum, uint32_t value) { + if (regNum >= UNW_HEXAGON_R0 && regNum <= UNW_HEXAGON_R31) { + _registers.__r[regNum - UNW_HEXAGON_R0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__r[UNW_HEXAGON_PC] = value; + return; + case UNW_REG_SP: + _registers.__r[UNW_HEXAGON_R29] = value; + return; + } + _LIBUNWIND_ABORT("unsupported hexagon register"); +} + +inline bool Registers_hexagon::validFloatRegister(int /* regNum */) const { + return false; +} + +inline double Registers_hexagon::getFloatRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("hexagon float support not implemented"); +} + +inline void Registers_hexagon::setFloatRegister(int /* regNum */, + double /* value */) { + _LIBUNWIND_ABORT("hexagon float support not implemented"); +} + +inline bool Registers_hexagon::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_hexagon::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("hexagon vector support not implemented"); +} + +inline void Registers_hexagon::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("hexagon vector support not implemented"); +} + +inline const char *Registers_hexagon::getRegisterName(int regNum) { + switch (regNum) { + case UNW_HEXAGON_R0: + return "r0"; + case UNW_HEXAGON_R1: + return "r1"; + case UNW_HEXAGON_R2: + return "r2"; + case UNW_HEXAGON_R3: + return "r3"; + case UNW_HEXAGON_R4: + return "r4"; + case UNW_HEXAGON_R5: + return "r5"; + case UNW_HEXAGON_R6: + return "r6"; + case UNW_HEXAGON_R7: + return "r7"; + case UNW_HEXAGON_R8: + return "r8"; + case UNW_HEXAGON_R9: + return "r9"; + case UNW_HEXAGON_R10: + return "r10"; + case UNW_HEXAGON_R11: + return "r11"; + case UNW_HEXAGON_R12: + return "r12"; + case UNW_HEXAGON_R13: + return "r13"; + case UNW_HEXAGON_R14: + return "r14"; + case UNW_HEXAGON_R15: + return "r15"; + case UNW_HEXAGON_R16: + return "r16"; + case UNW_HEXAGON_R17: + return "r17"; + case UNW_HEXAGON_R18: + return "r18"; + case UNW_HEXAGON_R19: + return "r19"; + case UNW_HEXAGON_R20: + return "r20"; + case UNW_HEXAGON_R21: + return "r21"; + case UNW_HEXAGON_R22: + return "r22"; + case UNW_HEXAGON_R23: + return "r23"; + case UNW_HEXAGON_R24: + return "r24"; + case UNW_HEXAGON_R25: + return "r25"; + case UNW_HEXAGON_R26: + return "r26"; + case UNW_HEXAGON_R27: + return "r27"; + case UNW_HEXAGON_R28: + return "r28"; + case UNW_HEXAGON_R29: + return "r29"; + case UNW_HEXAGON_R30: + return "r30"; + case UNW_HEXAGON_R31: + return "r31"; + default: + return "unknown register"; + } + +} +#endif // _LIBUNWIND_TARGET_HEXAGON + + +#if defined(_LIBUNWIND_TARGET_RISCV) +/// Registers_riscv holds the register state of a thread in a RISC-V +/// process. + +// This check makes it safe when LIBUNWIND_ENABLE_CROSS_UNWINDING enabled. +# ifdef __riscv +# if __riscv_xlen == 32 +typedef uint32_t reg_t; +# elif __riscv_xlen == 64 +typedef uint64_t reg_t; +# else +# error "Unsupported __riscv_xlen" +# endif + +# if defined(__riscv_flen) +# if __riscv_flen == 64 +typedef double fp_t; +# elif __riscv_flen == 32 +typedef float fp_t; +# else +# error "Unsupported __riscv_flen" +# endif +# else +// This is just for suppressing undeclared error of fp_t. +typedef double fp_t; +# endif +# else +// Use Max possible width when cross unwinding +typedef uint64_t reg_t; +typedef double fp_t; +# define __riscv_xlen 64 +# define __riscv_flen 64 +#endif + +/// Registers_riscv holds the register state of a thread. +class _LIBUNWIND_HIDDEN Registers_riscv { +public: + Registers_riscv(); + Registers_riscv(const void *registers); + + bool validRegister(int num) const; + reg_t getRegister(int num) const; + void setRegister(int num, reg_t value); + bool validFloatRegister(int num) const; + fp_t getFloatRegister(int num) const; + void setFloatRegister(int num, fp_t value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV; + } + static int getArch() { return REGISTERS_RISCV; } + + reg_t getSP() const { return _registers[2]; } + void setSP(reg_t value) { _registers[2] = value; } + reg_t getIP() const { return _registers[0]; } + void setIP(reg_t value) { _registers[0] = value; } + +private: + // _registers[0] holds the pc + reg_t _registers[32]; +# if defined(__riscv_flen) + fp_t _floats[32]; +# endif +}; + +inline Registers_riscv::Registers_riscv(const void *registers) { + static_assert((check_fit::does_fit), + "riscv registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); +# if __riscv_xlen == 32 + static_assert(sizeof(_registers) == 0x80, + "expected float registers to be at offset 128"); +# elif __riscv_xlen == 64 + static_assert(sizeof(_registers) == 0x100, + "expected float registers to be at offset 256"); +# else +# error "Unexpected float registers." +# endif + +# if defined(__riscv_flen) + memcpy(_floats, + static_cast(registers) + sizeof(_registers), + sizeof(_floats)); +# endif +} + +inline Registers_riscv::Registers_riscv() { + memset(&_registers, 0, sizeof(_registers)); +# if defined(__riscv_flen) + memset(&_floats, 0, sizeof(_floats)); +# endif +} + +inline bool Registers_riscv::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum == UNW_RISCV_VLENB) + return true; + if (regNum > UNW_RISCV_F31) + return false; + return true; +} + +inline reg_t Registers_riscv::getRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return _registers[0]; + if (regNum == UNW_REG_SP) + return _registers[2]; + if (regNum == UNW_RISCV_X0) + return 0; + if ((regNum > 0) && (regNum < 32)) + return _registers[regNum]; + if (regNum == UNW_RISCV_VLENB) { + reg_t vlenb; + __asm__("csrr %0, 0xC22" : "=r"(vlenb)); + return vlenb; + } + _LIBUNWIND_ABORT("unsupported riscv register"); +} + +inline void Registers_riscv::setRegister(int regNum, reg_t value) { + if (regNum == UNW_REG_IP) + _registers[0] = value; + else if (regNum == UNW_REG_SP) + _registers[2] = value; + else if (regNum == UNW_RISCV_X0) + /* x0 is hardwired to zero */ + return; + else if ((regNum > 0) && (regNum < 32)) + _registers[regNum] = value; + else + _LIBUNWIND_ABORT("unsupported riscv register"); +} + +inline const char *Registers_riscv::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_REG_SP: + return "sp"; + case UNW_RISCV_X0: + return "zero"; + case UNW_RISCV_X1: + return "ra"; + case UNW_RISCV_X2: + return "sp"; + case UNW_RISCV_X3: + return "gp"; + case UNW_RISCV_X4: + return "tp"; + case UNW_RISCV_X5: + return "t0"; + case UNW_RISCV_X6: + return "t1"; + case UNW_RISCV_X7: + return "t2"; + case UNW_RISCV_X8: + return "s0"; + case UNW_RISCV_X9: + return "s1"; + case UNW_RISCV_X10: + return "a0"; + case UNW_RISCV_X11: + return "a1"; + case UNW_RISCV_X12: + return "a2"; + case UNW_RISCV_X13: + return "a3"; + case UNW_RISCV_X14: + return "a4"; + case UNW_RISCV_X15: + return "a5"; + case UNW_RISCV_X16: + return "a6"; + case UNW_RISCV_X17: + return "a7"; + case UNW_RISCV_X18: + return "s2"; + case UNW_RISCV_X19: + return "s3"; + case UNW_RISCV_X20: + return "s4"; + case UNW_RISCV_X21: + return "s5"; + case UNW_RISCV_X22: + return "s6"; + case UNW_RISCV_X23: + return "s7"; + case UNW_RISCV_X24: + return "s8"; + case UNW_RISCV_X25: + return "s9"; + case UNW_RISCV_X26: + return "s10"; + case UNW_RISCV_X27: + return "s11"; + case UNW_RISCV_X28: + return "t3"; + case UNW_RISCV_X29: + return "t4"; + case UNW_RISCV_X30: + return "t5"; + case UNW_RISCV_X31: + return "t6"; + case UNW_RISCV_F0: + return "ft0"; + case UNW_RISCV_F1: + return "ft1"; + case UNW_RISCV_F2: + return "ft2"; + case UNW_RISCV_F3: + return "ft3"; + case UNW_RISCV_F4: + return "ft4"; + case UNW_RISCV_F5: + return "ft5"; + case UNW_RISCV_F6: + return "ft6"; + case UNW_RISCV_F7: + return "ft7"; + case UNW_RISCV_F8: + return "fs0"; + case UNW_RISCV_F9: + return "fs1"; + case UNW_RISCV_F10: + return "fa0"; + case UNW_RISCV_F11: + return "fa1"; + case UNW_RISCV_F12: + return "fa2"; + case UNW_RISCV_F13: + return "fa3"; + case UNW_RISCV_F14: + return "fa4"; + case UNW_RISCV_F15: + return "fa5"; + case UNW_RISCV_F16: + return "fa6"; + case UNW_RISCV_F17: + return "fa7"; + case UNW_RISCV_F18: + return "fs2"; + case UNW_RISCV_F19: + return "fs3"; + case UNW_RISCV_F20: + return "fs4"; + case UNW_RISCV_F21: + return "fs5"; + case UNW_RISCV_F22: + return "fs6"; + case UNW_RISCV_F23: + return "fs7"; + case UNW_RISCV_F24: + return "fs8"; + case UNW_RISCV_F25: + return "fs9"; + case UNW_RISCV_F26: + return "fs10"; + case UNW_RISCV_F27: + return "fs11"; + case UNW_RISCV_F28: + return "ft8"; + case UNW_RISCV_F29: + return "ft9"; + case UNW_RISCV_F30: + return "ft10"; + case UNW_RISCV_F31: + return "ft11"; + case UNW_RISCV_VLENB: + return "vlenb"; + default: + return "unknown register"; + } +} + +inline bool Registers_riscv::validFloatRegister(int regNum) const { +# if defined(__riscv_flen) + if (regNum < UNW_RISCV_F0) + return false; + if (regNum > UNW_RISCV_F31) + return false; + return true; +# else + (void)regNum; + return false; +# endif +} + +inline fp_t Registers_riscv::getFloatRegister(int regNum) const { +# if defined(__riscv_flen) + assert(validFloatRegister(regNum)); + return _floats[regNum - UNW_RISCV_F0]; +# else + (void)regNum; + _LIBUNWIND_ABORT("libunwind not built with float support"); +# endif +} + +inline void Registers_riscv::setFloatRegister(int regNum, fp_t value) { +# if defined(__riscv_flen) + assert(validFloatRegister(regNum)); + _floats[regNum - UNW_RISCV_F0] = value; +# else + (void)regNum; + (void)value; + _LIBUNWIND_ABORT("libunwind not built with float support"); +# endif +} + +inline bool Registers_riscv::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_riscv::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no riscv vector register support yet"); +} + +inline void Registers_riscv::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no riscv vector register support yet"); +} +#endif // _LIBUNWIND_TARGET_RISCV + +#if defined(_LIBUNWIND_TARGET_VE) +/// Registers_ve holds the register state of a thread in a VE process. +class _LIBUNWIND_HIDDEN Registers_ve { +public: + Registers_ve(); + Registers_ve(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE; + } + static int getArch() { return REGISTERS_VE; } + + uint64_t getSP() const { return _registers.__s[11]; } + void setSP(uint64_t value) { _registers.__s[11] = value; } + uint64_t getIP() const { return _registers.__ic; } + void setIP(uint64_t value) { _registers.__ic = value; } + +private: + // FIXME: Need to store not only scalar registers but also vector and vector + // mask registers. VEOS uses mcontext_t defined in ucontext.h. It takes + // 524288 bytes (65536*8 bytes), though. Currently, we use libunwind for + // SjLj exception support only, so Registers_ve is not implemented completely. + struct ve_thread_state_t { + uint64_t __s[64]; // s0-s64 + uint64_t __ic; // Instruction counter (IC) + uint64_t __vixr; // Vector Index Register + uint64_t __vl; // Vector Length Register + }; + + ve_thread_state_t _registers; // total 67 registers + + // Currently no vector register is preserved. +}; + +inline Registers_ve::Registers_ve(const void *registers) { + static_assert((check_fit::does_fit), + "ve registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); + static_assert(sizeof(_registers) == 536, + "expected vector register offset to be 536"); +} + +inline Registers_ve::Registers_ve() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_ve::validRegister(int regNum) const { + if (regNum >= UNW_VE_S0 && regNum <= UNW_VE_S63) + return true; + + switch (regNum) { + case UNW_REG_IP: + case UNW_REG_SP: + case UNW_VE_VIXR: + case UNW_VE_VL: + return true; + default: + return false; + } +} + +inline uint64_t Registers_ve::getRegister(int regNum) const { + if (regNum >= UNW_VE_S0 && regNum <= UNW_VE_S63) + return _registers.__s[regNum - UNW_VE_S0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__ic; + case UNW_REG_SP: + return _registers.__s[11]; + case UNW_VE_VIXR: + return _registers.__vixr; + case UNW_VE_VL: + return _registers.__vl; + } + _LIBUNWIND_ABORT("unsupported ve register"); +} + +inline void Registers_ve::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_VE_S0 && regNum <= UNW_VE_S63) { + _registers.__s[regNum - UNW_VE_S0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__ic = value; + return; + case UNW_REG_SP: + _registers.__s[11] = value; + return; + case UNW_VE_VIXR: + _registers.__vixr = value; + return; + case UNW_VE_VL: + _registers.__vl = value; + return; + } + _LIBUNWIND_ABORT("unsupported ve register"); +} + +inline bool Registers_ve::validFloatRegister(int /* regNum */) const { + return false; +} + +inline double Registers_ve::getFloatRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("VE doesn't have float registers"); +} + +inline void Registers_ve::setFloatRegister(int /* regNum */, + double /* value */) { + _LIBUNWIND_ABORT("VE doesn't have float registers"); +} + +inline bool Registers_ve::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_ve::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("VE vector support not implemented"); +} + +inline void Registers_ve::setVectorRegister(int /* regNum */, + v128 /* value */) { + _LIBUNWIND_ABORT("VE vector support not implemented"); +} + +inline const char *Registers_ve::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_VE_VIXR: + return "vixr"; + case UNW_VE_VL: + return "vl"; + case UNW_VE_S0: + return "s0"; + case UNW_VE_S1: + return "s1"; + case UNW_VE_S2: + return "s2"; + case UNW_VE_S3: + return "s3"; + case UNW_VE_S4: + return "s4"; + case UNW_VE_S5: + return "s5"; + case UNW_VE_S6: + return "s6"; + case UNW_VE_S7: + return "s7"; + case UNW_VE_S8: + return "s8"; + case UNW_VE_S9: + return "s9"; + case UNW_VE_S10: + return "s10"; + case UNW_VE_S11: + return "s11"; + case UNW_VE_S12: + return "s12"; + case UNW_VE_S13: + return "s13"; + case UNW_VE_S14: + return "s14"; + case UNW_VE_S15: + return "s15"; + case UNW_VE_S16: + return "s16"; + case UNW_VE_S17: + return "s17"; + case UNW_VE_S18: + return "s18"; + case UNW_VE_S19: + return "s19"; + case UNW_VE_S20: + return "s20"; + case UNW_VE_S21: + return "s21"; + case UNW_VE_S22: + return "s22"; + case UNW_VE_S23: + return "s23"; + case UNW_VE_S24: + return "s24"; + case UNW_VE_S25: + return "s25"; + case UNW_VE_S26: + return "s26"; + case UNW_VE_S27: + return "s27"; + case UNW_VE_S28: + return "s28"; + case UNW_VE_S29: + return "s29"; + case UNW_VE_S30: + return "s30"; + case UNW_VE_S31: + return "s31"; + case UNW_VE_S32: + return "s32"; + case UNW_VE_S33: + return "s33"; + case UNW_VE_S34: + return "s34"; + case UNW_VE_S35: + return "s35"; + case UNW_VE_S36: + return "s36"; + case UNW_VE_S37: + return "s37"; + case UNW_VE_S38: + return "s38"; + case UNW_VE_S39: + return "s39"; + case UNW_VE_S40: + return "s40"; + case UNW_VE_S41: + return "s41"; + case UNW_VE_S42: + return "s42"; + case UNW_VE_S43: + return "s43"; + case UNW_VE_S44: + return "s44"; + case UNW_VE_S45: + return "s45"; + case UNW_VE_S46: + return "s46"; + case UNW_VE_S47: + return "s47"; + case UNW_VE_S48: + return "s48"; + case UNW_VE_S49: + return "s49"; + case UNW_VE_S50: + return "s50"; + case UNW_VE_S51: + return "s51"; + case UNW_VE_S52: + return "s52"; + case UNW_VE_S53: + return "s53"; + case UNW_VE_S54: + return "s54"; + case UNW_VE_S55: + return "s55"; + case UNW_VE_S56: + return "s56"; + case UNW_VE_S57: + return "s57"; + case UNW_VE_S58: + return "s58"; + case UNW_VE_S59: + return "s59"; + case UNW_VE_S60: + return "s60"; + case UNW_VE_S61: + return "s61"; + case UNW_VE_S62: + return "s62"; + case UNW_VE_S63: + return "s63"; + case UNW_VE_V0: + return "v0"; + case UNW_VE_V1: + return "v1"; + case UNW_VE_V2: + return "v2"; + case UNW_VE_V3: + return "v3"; + case UNW_VE_V4: + return "v4"; + case UNW_VE_V5: + return "v5"; + case UNW_VE_V6: + return "v6"; + case UNW_VE_V7: + return "v7"; + case UNW_VE_V8: + return "v8"; + case UNW_VE_V9: + return "v9"; + case UNW_VE_V10: + return "v10"; + case UNW_VE_V11: + return "v11"; + case UNW_VE_V12: + return "v12"; + case UNW_VE_V13: + return "v13"; + case UNW_VE_V14: + return "v14"; + case UNW_VE_V15: + return "v15"; + case UNW_VE_V16: + return "v16"; + case UNW_VE_V17: + return "v17"; + case UNW_VE_V18: + return "v18"; + case UNW_VE_V19: + return "v19"; + case UNW_VE_V20: + return "v20"; + case UNW_VE_V21: + return "v21"; + case UNW_VE_V22: + return "v22"; + case UNW_VE_V23: + return "v23"; + case UNW_VE_V24: + return "v24"; + case UNW_VE_V25: + return "v25"; + case UNW_VE_V26: + return "v26"; + case UNW_VE_V27: + return "v27"; + case UNW_VE_V28: + return "v28"; + case UNW_VE_V29: + return "v29"; + case UNW_VE_V30: + return "v30"; + case UNW_VE_V31: + return "v31"; + case UNW_VE_V32: + return "v32"; + case UNW_VE_V33: + return "v33"; + case UNW_VE_V34: + return "v34"; + case UNW_VE_V35: + return "v35"; + case UNW_VE_V36: + return "v36"; + case UNW_VE_V37: + return "v37"; + case UNW_VE_V38: + return "v38"; + case UNW_VE_V39: + return "v39"; + case UNW_VE_V40: + return "v40"; + case UNW_VE_V41: + return "v41"; + case UNW_VE_V42: + return "v42"; + case UNW_VE_V43: + return "v43"; + case UNW_VE_V44: + return "v44"; + case UNW_VE_V45: + return "v45"; + case UNW_VE_V46: + return "v46"; + case UNW_VE_V47: + return "v47"; + case UNW_VE_V48: + return "v48"; + case UNW_VE_V49: + return "v49"; + case UNW_VE_V50: + return "v50"; + case UNW_VE_V51: + return "v51"; + case UNW_VE_V52: + return "v52"; + case UNW_VE_V53: + return "v53"; + case UNW_VE_V54: + return "v54"; + case UNW_VE_V55: + return "v55"; + case UNW_VE_V56: + return "v56"; + case UNW_VE_V57: + return "v57"; + case UNW_VE_V58: + return "v58"; + case UNW_VE_V59: + return "v59"; + case UNW_VE_V60: + return "v60"; + case UNW_VE_V61: + return "v61"; + case UNW_VE_V62: + return "v62"; + case UNW_VE_V63: + return "v63"; + case UNW_VE_VM0: + return "vm0"; + case UNW_VE_VM1: + return "vm1"; + case UNW_VE_VM2: + return "vm2"; + case UNW_VE_VM3: + return "vm3"; + case UNW_VE_VM4: + return "vm4"; + case UNW_VE_VM5: + return "vm5"; + case UNW_VE_VM6: + return "vm6"; + case UNW_VE_VM7: + return "vm7"; + case UNW_VE_VM8: + return "vm8"; + case UNW_VE_VM9: + return "vm9"; + case UNW_VE_VM10: + return "vm10"; + case UNW_VE_VM11: + return "vm11"; + case UNW_VE_VM12: + return "vm12"; + case UNW_VE_VM13: + return "vm13"; + case UNW_VE_VM14: + return "vm14"; + case UNW_VE_VM15: + return "vm15"; + } + return "unknown register"; +} +#endif // _LIBUNWIND_TARGET_VE + +#if defined(_LIBUNWIND_TARGET_S390X) +/// Registers_s390x holds the register state of a thread in a +/// 64-bit Linux on IBM zSystems process. +class _LIBUNWIND_HIDDEN Registers_s390x { +public: + Registers_s390x(); + Registers_s390x(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_S390X; + } + static int getArch() { return REGISTERS_S390X; } + + uint64_t getSP() const { return _registers.__gpr[15]; } + void setSP(uint64_t value) { _registers.__gpr[15] = value; } + uint64_t getIP() const { return _registers.__pswa; } + void setIP(uint64_t value) { _registers.__pswa = value; } + +private: + struct s390x_thread_state_t { + uint64_t __pswm; // Problem Status Word: Mask + uint64_t __pswa; // Problem Status Word: Address (PC) + uint64_t __gpr[16]; // General Purpose Registers + double __fpr[16]; // Floating-Point Registers + }; + + s390x_thread_state_t _registers; +}; + +inline Registers_s390x::Registers_s390x(const void *registers) { + static_assert((check_fit::does_fit), + "s390x registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_s390x::Registers_s390x() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_s390x::validRegister(int regNum) const { + switch (regNum) { + case UNW_S390X_PSWM: + case UNW_S390X_PSWA: + case UNW_REG_IP: + case UNW_REG_SP: + return true; + } + + if (regNum >= UNW_S390X_R0 && regNum <= UNW_S390X_R15) + return true; + + return false; +} + +inline uint64_t Registers_s390x::getRegister(int regNum) const { + if (regNum >= UNW_S390X_R0 && regNum <= UNW_S390X_R15) + return _registers.__gpr[regNum - UNW_S390X_R0]; + + switch (regNum) { + case UNW_S390X_PSWM: + return _registers.__pswm; + case UNW_S390X_PSWA: + case UNW_REG_IP: + return _registers.__pswa; + case UNW_REG_SP: + return _registers.__gpr[15]; + } + _LIBUNWIND_ABORT("unsupported s390x register"); +} + +inline void Registers_s390x::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_S390X_R0 && regNum <= UNW_S390X_R15) { + _registers.__gpr[regNum - UNW_S390X_R0] = value; + return; + } + + switch (regNum) { + case UNW_S390X_PSWM: + _registers.__pswm = value; + return; + case UNW_S390X_PSWA: + case UNW_REG_IP: + _registers.__pswa = value; + return; + case UNW_REG_SP: + _registers.__gpr[15] = value; + return; + } + _LIBUNWIND_ABORT("unsupported s390x register"); +} + +inline bool Registers_s390x::validFloatRegister(int regNum) const { + return regNum >= UNW_S390X_F0 && regNum <= UNW_S390X_F15; +} + +inline double Registers_s390x::getFloatRegister(int regNum) const { + // NOTE: FPR DWARF register numbers are not consecutive. + switch (regNum) { + case UNW_S390X_F0: + return _registers.__fpr[0]; + case UNW_S390X_F1: + return _registers.__fpr[1]; + case UNW_S390X_F2: + return _registers.__fpr[2]; + case UNW_S390X_F3: + return _registers.__fpr[3]; + case UNW_S390X_F4: + return _registers.__fpr[4]; + case UNW_S390X_F5: + return _registers.__fpr[5]; + case UNW_S390X_F6: + return _registers.__fpr[6]; + case UNW_S390X_F7: + return _registers.__fpr[7]; + case UNW_S390X_F8: + return _registers.__fpr[8]; + case UNW_S390X_F9: + return _registers.__fpr[9]; + case UNW_S390X_F10: + return _registers.__fpr[10]; + case UNW_S390X_F11: + return _registers.__fpr[11]; + case UNW_S390X_F12: + return _registers.__fpr[12]; + case UNW_S390X_F13: + return _registers.__fpr[13]; + case UNW_S390X_F14: + return _registers.__fpr[14]; + case UNW_S390X_F15: + return _registers.__fpr[15]; + } + _LIBUNWIND_ABORT("unsupported s390x register"); +} + +inline void Registers_s390x::setFloatRegister(int regNum, double value) { + // NOTE: FPR DWARF register numbers are not consecutive. + switch (regNum) { + case UNW_S390X_F0: + _registers.__fpr[0] = value; + return; + case UNW_S390X_F1: + _registers.__fpr[1] = value; + return; + case UNW_S390X_F2: + _registers.__fpr[2] = value; + return; + case UNW_S390X_F3: + _registers.__fpr[3] = value; + return; + case UNW_S390X_F4: + _registers.__fpr[4] = value; + return; + case UNW_S390X_F5: + _registers.__fpr[5] = value; + return; + case UNW_S390X_F6: + _registers.__fpr[6] = value; + return; + case UNW_S390X_F7: + _registers.__fpr[7] = value; + return; + case UNW_S390X_F8: + _registers.__fpr[8] = value; + return; + case UNW_S390X_F9: + _registers.__fpr[9] = value; + return; + case UNW_S390X_F10: + _registers.__fpr[10] = value; + return; + case UNW_S390X_F11: + _registers.__fpr[11] = value; + return; + case UNW_S390X_F12: + _registers.__fpr[12] = value; + return; + case UNW_S390X_F13: + _registers.__fpr[13] = value; + return; + case UNW_S390X_F14: + _registers.__fpr[14] = value; + return; + case UNW_S390X_F15: + _registers.__fpr[15] = value; + return; + } + _LIBUNWIND_ABORT("unsupported s390x register"); +} + +inline bool Registers_s390x::validVectorRegister(int /*regNum*/) const { + return false; +} + +inline v128 Registers_s390x::getVectorRegister(int /*regNum*/) const { + _LIBUNWIND_ABORT("s390x vector support not implemented"); +} + +inline void Registers_s390x::setVectorRegister(int /*regNum*/, v128 /*value*/) { + _LIBUNWIND_ABORT("s390x vector support not implemented"); +} + +inline const char *Registers_s390x::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_S390X_R0: + return "r0"; + case UNW_S390X_R1: + return "r1"; + case UNW_S390X_R2: + return "r2"; + case UNW_S390X_R3: + return "r3"; + case UNW_S390X_R4: + return "r4"; + case UNW_S390X_R5: + return "r5"; + case UNW_S390X_R6: + return "r6"; + case UNW_S390X_R7: + return "r7"; + case UNW_S390X_R8: + return "r8"; + case UNW_S390X_R9: + return "r9"; + case UNW_S390X_R10: + return "r10"; + case UNW_S390X_R11: + return "r11"; + case UNW_S390X_R12: + return "r12"; + case UNW_S390X_R13: + return "r13"; + case UNW_S390X_R14: + return "r14"; + case UNW_S390X_R15: + return "r15"; + case UNW_S390X_F0: + return "f0"; + case UNW_S390X_F1: + return "f1"; + case UNW_S390X_F2: + return "f2"; + case UNW_S390X_F3: + return "f3"; + case UNW_S390X_F4: + return "f4"; + case UNW_S390X_F5: + return "f5"; + case UNW_S390X_F6: + return "f6"; + case UNW_S390X_F7: + return "f7"; + case UNW_S390X_F8: + return "f8"; + case UNW_S390X_F9: + return "f9"; + case UNW_S390X_F10: + return "f10"; + case UNW_S390X_F11: + return "f11"; + case UNW_S390X_F12: + return "f12"; + case UNW_S390X_F13: + return "f13"; + case UNW_S390X_F14: + return "f14"; + case UNW_S390X_F15: + return "f15"; + } + return "unknown register"; +} +#endif // _LIBUNWIND_TARGET_S390X + +#if defined(_LIBUNWIND_TARGET_LOONGARCH) +/// Registers_loongarch holds the register state of a thread in a 64-bit +/// LoongArch process. +class _LIBUNWIND_HIDDEN Registers_loongarch { +public: + Registers_loongarch(); + Registers_loongarch(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static constexpr int lastDwarfRegNum() { + return _LIBUNWIND_HIGHEST_DWARF_REGISTER_LOONGARCH; + } + static int getArch() { return REGISTERS_LOONGARCH; } + + uint64_t getSP() const { return _registers.__r[3]; } + void setSP(uint64_t value) { _registers.__r[3] = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint64_t value) { _registers.__pc = value; } + +private: + struct loongarch_thread_state_t { + uint64_t __r[32]; + uint64_t __pc; + }; + + loongarch_thread_state_t _registers; +#if __loongarch_frlen == 64 + double _floats[32]; +#endif +}; + +inline Registers_loongarch::Registers_loongarch(const void *registers) { + static_assert((check_fit::does_fit), + "loongarch registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); + static_assert(sizeof(_registers) == 0x108, + "expected float registers to be at offset 264"); +#if __loongarch_frlen == 64 + memcpy(_floats, static_cast(registers) + sizeof(_registers), + sizeof(_floats)); +#endif +} + +inline Registers_loongarch::Registers_loongarch() { + memset(&_registers, 0, sizeof(_registers)); +#if __loongarch_frlen == 64 + memset(&_floats, 0, sizeof(_floats)); +#endif +} + +inline bool Registers_loongarch::validRegister(int regNum) const { + if (regNum == UNW_REG_IP || regNum == UNW_REG_SP) + return true; + if (regNum < 0 || regNum > UNW_LOONGARCH_F31) + return false; + return true; +} + +inline uint64_t Registers_loongarch::getRegister(int regNum) const { + if (regNum >= UNW_LOONGARCH_R0 && regNum <= UNW_LOONGARCH_R31) + return _registers.__r[regNum - UNW_LOONGARCH_R0]; + + if (regNum == UNW_REG_IP) + return _registers.__pc; + if (regNum == UNW_REG_SP) + return _registers.__r[3]; + _LIBUNWIND_ABORT("unsupported loongarch register"); +} + +inline void Registers_loongarch::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_LOONGARCH_R0 && regNum <= UNW_LOONGARCH_R31) + _registers.__r[regNum - UNW_LOONGARCH_R0] = value; + else if (regNum == UNW_REG_IP) + _registers.__pc = value; + else if (regNum == UNW_REG_SP) + _registers.__r[3] = value; + else + _LIBUNWIND_ABORT("unsupported loongarch register"); +} + +inline const char *Registers_loongarch::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "$pc"; + case UNW_REG_SP: + return "$sp"; + case UNW_LOONGARCH_R0: + return "$r0"; + case UNW_LOONGARCH_R1: + return "$r1"; + case UNW_LOONGARCH_R2: + return "$r2"; + case UNW_LOONGARCH_R3: + return "$r3"; + case UNW_LOONGARCH_R4: + return "$r4"; + case UNW_LOONGARCH_R5: + return "$r5"; + case UNW_LOONGARCH_R6: + return "$r6"; + case UNW_LOONGARCH_R7: + return "$r7"; + case UNW_LOONGARCH_R8: + return "$r8"; + case UNW_LOONGARCH_R9: + return "$r9"; + case UNW_LOONGARCH_R10: + return "$r10"; + case UNW_LOONGARCH_R11: + return "$r11"; + case UNW_LOONGARCH_R12: + return "$r12"; + case UNW_LOONGARCH_R13: + return "$r13"; + case UNW_LOONGARCH_R14: + return "$r14"; + case UNW_LOONGARCH_R15: + return "$r15"; + case UNW_LOONGARCH_R16: + return "$r16"; + case UNW_LOONGARCH_R17: + return "$r17"; + case UNW_LOONGARCH_R18: + return "$r18"; + case UNW_LOONGARCH_R19: + return "$r19"; + case UNW_LOONGARCH_R20: + return "$r20"; + case UNW_LOONGARCH_R21: + return "$r21"; + case UNW_LOONGARCH_R22: + return "$r22"; + case UNW_LOONGARCH_R23: + return "$r23"; + case UNW_LOONGARCH_R24: + return "$r24"; + case UNW_LOONGARCH_R25: + return "$r25"; + case UNW_LOONGARCH_R26: + return "$r26"; + case UNW_LOONGARCH_R27: + return "$r27"; + case UNW_LOONGARCH_R28: + return "$r28"; + case UNW_LOONGARCH_R29: + return "$r29"; + case UNW_LOONGARCH_R30: + return "$r30"; + case UNW_LOONGARCH_R31: + return "$r31"; + case UNW_LOONGARCH_F0: + return "$f0"; + case UNW_LOONGARCH_F1: + return "$f1"; + case UNW_LOONGARCH_F2: + return "$f2"; + case UNW_LOONGARCH_F3: + return "$f3"; + case UNW_LOONGARCH_F4: + return "$f4"; + case UNW_LOONGARCH_F5: + return "$f5"; + case UNW_LOONGARCH_F6: + return "$f6"; + case UNW_LOONGARCH_F7: + return "$f7"; + case UNW_LOONGARCH_F8: + return "$f8"; + case UNW_LOONGARCH_F9: + return "$f9"; + case UNW_LOONGARCH_F10: + return "$f10"; + case UNW_LOONGARCH_F11: + return "$f11"; + case UNW_LOONGARCH_F12: + return "$f12"; + case UNW_LOONGARCH_F13: + return "$f13"; + case UNW_LOONGARCH_F14: + return "$f14"; + case UNW_LOONGARCH_F15: + return "$f15"; + case UNW_LOONGARCH_F16: + return "$f16"; + case UNW_LOONGARCH_F17: + return "$f17"; + case UNW_LOONGARCH_F18: + return "$f18"; + case UNW_LOONGARCH_F19: + return "$f19"; + case UNW_LOONGARCH_F20: + return "$f20"; + case UNW_LOONGARCH_F21: + return "$f21"; + case UNW_LOONGARCH_F22: + return "$f22"; + case UNW_LOONGARCH_F23: + return "$f23"; + case UNW_LOONGARCH_F24: + return "$f24"; + case UNW_LOONGARCH_F25: + return "$f25"; + case UNW_LOONGARCH_F26: + return "$f26"; + case UNW_LOONGARCH_F27: + return "$f27"; + case UNW_LOONGARCH_F28: + return "$f28"; + case UNW_LOONGARCH_F29: + return "$f29"; + case UNW_LOONGARCH_F30: + return "$f30"; + case UNW_LOONGARCH_F31: + return "$f31"; + default: + return "unknown register"; + } +} + +inline bool Registers_loongarch::validFloatRegister(int regNum) const { + if (regNum < UNW_LOONGARCH_F0 || regNum > UNW_LOONGARCH_F31) + return false; + return true; +} + +inline double Registers_loongarch::getFloatRegister(int regNum) const { +#if __loongarch_frlen == 64 + assert(validFloatRegister(regNum)); + return _floats[regNum - UNW_LOONGARCH_F0]; +#else + _LIBUNWIND_ABORT("libunwind not built with float support"); +#endif +} + +inline void Registers_loongarch::setFloatRegister(int regNum, double value) { +#if __loongarch_frlen == 64 + assert(validFloatRegister(regNum)); + _floats[regNum - UNW_LOONGARCH_F0] = value; +#else + _LIBUNWIND_ABORT("libunwind not built with float support"); +#endif +} + +inline bool Registers_loongarch::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_loongarch::getVectorRegister(int) const { + _LIBUNWIND_ABORT("loongarch vector support not implemented"); +} + +inline void Registers_loongarch::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("loongarch vector support not implemented"); +} +#endif //_LIBUNWIND_TARGET_LOONGARCH + +} // namespace libunwind + +#endif // __REGISTERS_HPP__ diff --git a/third_party/libunwind/Unwind-EHABI.h b/third_party/libunwind/Unwind-EHABI.h new file mode 100644 index 000000000..67591c666 --- /dev/null +++ b/third_party/libunwind/Unwind-EHABI.h @@ -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__ diff --git a/third_party/libunwind/Unwind-sjlj.c b/third_party/libunwind/Unwind-sjlj.c new file mode 100644 index 000000000..ae8b73351 --- /dev/null +++ b/third_party/libunwind/Unwind-sjlj.c @@ -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 +#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) diff --git a/third_party/libunwind/UnwindCursor.hpp b/third_party/libunwind/UnwindCursor.hpp new file mode 100644 index 000000000..02d4e4954 --- /dev/null +++ b/third_party/libunwind/UnwindCursor.hpp @@ -0,0 +1,2953 @@ +//===----------------------------------------------------------------------===// +// +// 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++ interface to lower levels of libunwind +//===----------------------------------------------------------------------===// + +#ifndef __UNWINDCURSOR_HPP__ +#define __UNWINDCURSOR_HPP__ + +#include "third_party/libunwind/cet_unwind.h" +#include "libc/isystem/stdint.h" +#include "libc/stdio/stdio.h" +#include "libc/isystem/stdlib.h" +#include "third_party/libunwind/include/unwind.h" + +#ifdef _WIN32 + #include + #include +#endif +#ifdef __APPLE__ + #include +#endif +#ifdef _AIX +#include +#include +#include +#endif + +#if defined(_LIBUNWIND_TARGET_LINUX) && \ + (defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \ + defined(_LIBUNWIND_TARGET_S390X)) +#include +#include +#include +#define _LIBUNWIND_CHECK_LINUX_SIGRETURN 1 +#endif + +#include "third_party/libunwind/AddressSpace.hpp" +#include "third_party/libunwind/CompactUnwinder.hpp" +#include "third_party/libunwind/config.h" +#include "third_party/libunwind/DwarfInstructions.hpp" +#include "third_party/libunwind/EHHeaderParser.hpp" +#include "third_party/libunwind/include/libunwind.h" +#include "third_party/libunwind/libunwind_ext.h" +#include "third_party/libunwind/Registers.hpp" +#include "third_party/libunwind/RWMutex.hpp" +#include "third_party/libunwind/Unwind-EHABI.h" + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) +// Provide a definition for the DISPATCHER_CONTEXT struct for old (Win7 and +// earlier) SDKs. +// MinGW-w64 has always provided this struct. + #if defined(_WIN32) && defined(_LIBUNWIND_TARGET_X86_64) && \ + !defined(__MINGW32__) && VER_PRODUCTBUILD < 8000 +struct _DISPATCHER_CONTEXT { + ULONG64 ControlPc; + ULONG64 ImageBase; + PRUNTIME_FUNCTION FunctionEntry; + ULONG64 EstablisherFrame; + ULONG64 TargetIp; + PCONTEXT ContextRecord; + PEXCEPTION_ROUTINE LanguageHandler; + PVOID HandlerData; + PUNWIND_HISTORY_TABLE HistoryTable; + ULONG ScopeIndex; + ULONG Fill0; +}; + #endif + +struct UNWIND_INFO { + uint8_t Version : 3; + uint8_t Flags : 5; + uint8_t SizeOfProlog; + uint8_t CountOfCodes; + uint8_t FrameRegister : 4; + uint8_t FrameOffset : 4; + uint16_t UnwindCodes[2]; +}; + +extern "C" _Unwind_Reason_Code __libunwind_seh_personality( + int, _Unwind_Action, uint64_t, _Unwind_Exception *, + struct _Unwind_Context *); + +#endif + +namespace libunwind { + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +/// Cache of recently found FDEs. +template +class _LIBUNWIND_HIDDEN DwarfFDECache { + typedef typename A::pint_t pint_t; +public: + static constexpr pint_t kSearchAll = static_cast(-1); + static pint_t findFDE(pint_t mh, pint_t pc); + static void add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde); + static void removeAllIn(pint_t mh); + static void iterateCacheEntries(void (*func)(unw_word_t ip_start, + unw_word_t ip_end, + unw_word_t fde, unw_word_t mh)); + +private: + + struct entry { + pint_t mh; + pint_t ip_start; + pint_t ip_end; + pint_t fde; + }; + + // These fields are all static to avoid needing an initializer. + // There is only one instance of this class per process. + static RWMutex _lock; +#ifdef __APPLE__ + static void dyldUnloadHook(const struct mach_header *mh, intptr_t slide); + static bool _registeredForDyldUnloads; +#endif + static entry *_buffer; + static entry *_bufferUsed; + static entry *_bufferEnd; + static entry _initialBuffer[64]; +}; + +template +typename DwarfFDECache::entry * +DwarfFDECache::_buffer = _initialBuffer; + +template +typename DwarfFDECache::entry * +DwarfFDECache::_bufferUsed = _initialBuffer; + +template +typename DwarfFDECache::entry * +DwarfFDECache::_bufferEnd = &_initialBuffer[64]; + +template +typename DwarfFDECache::entry DwarfFDECache::_initialBuffer[64]; + +template +RWMutex DwarfFDECache::_lock; + +#ifdef __APPLE__ +template +bool DwarfFDECache::_registeredForDyldUnloads = false; +#endif + +template +typename A::pint_t DwarfFDECache::findFDE(pint_t mh, pint_t pc) { + pint_t result = 0; + _LIBUNWIND_LOG_IF_FALSE(_lock.lock_shared()); + for (entry *p = _buffer; p < _bufferUsed; ++p) { + if ((mh == p->mh) || (mh == kSearchAll)) { + if ((p->ip_start <= pc) && (pc < p->ip_end)) { + result = p->fde; + break; + } + } + } + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock_shared()); + return result; +} + +template +void DwarfFDECache::add(pint_t mh, pint_t ip_start, pint_t ip_end, + pint_t fde) { +#if !defined(_LIBUNWIND_NO_HEAP) + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + if (_bufferUsed >= _bufferEnd) { + size_t oldSize = (size_t)(_bufferEnd - _buffer); + size_t newSize = oldSize * 4; + // Can't use operator new (we are below it). + entry *newBuffer = (entry *)malloc(newSize * sizeof(entry)); + memcpy(newBuffer, _buffer, oldSize * sizeof(entry)); + if (_buffer != _initialBuffer) + free(_buffer); + _buffer = newBuffer; + _bufferUsed = &newBuffer[oldSize]; + _bufferEnd = &newBuffer[newSize]; + } + _bufferUsed->mh = mh; + _bufferUsed->ip_start = ip_start; + _bufferUsed->ip_end = ip_end; + _bufferUsed->fde = fde; + ++_bufferUsed; +#ifdef __APPLE__ + if (!_registeredForDyldUnloads) { + _dyld_register_func_for_remove_image(&dyldUnloadHook); + _registeredForDyldUnloads = true; + } +#endif + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +#endif +} + +template +void DwarfFDECache::removeAllIn(pint_t mh) { + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + entry *d = _buffer; + for (const entry *s = _buffer; s < _bufferUsed; ++s) { + if (s->mh != mh) { + if (d != s) + *d = *s; + ++d; + } + } + _bufferUsed = d; + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +} + +#ifdef __APPLE__ +template +void DwarfFDECache::dyldUnloadHook(const struct mach_header *mh, intptr_t ) { + removeAllIn((pint_t) mh); +} +#endif + +template +void DwarfFDECache::iterateCacheEntries(void (*func)( + unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) { + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + for (entry *p = _buffer; p < _bufferUsed; ++p) { + (*func)(p->ip_start, p->ip_end, p->fde, p->mh); + } + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +} +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + + +#define arrayoffsetof(type, index, field) ((size_t)(&((type *)0)[index].field)) + +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) +template class UnwindSectionHeader { +public: + UnwindSectionHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t version() const { + return _addressSpace.get32(_addr + + offsetof(unwind_info_section_header, version)); + } + uint32_t commonEncodingsArraySectionOffset() const { + return _addressSpace.get32(_addr + + offsetof(unwind_info_section_header, + commonEncodingsArraySectionOffset)); + } + uint32_t commonEncodingsArrayCount() const { + return _addressSpace.get32(_addr + offsetof(unwind_info_section_header, + commonEncodingsArrayCount)); + } + uint32_t personalityArraySectionOffset() const { + return _addressSpace.get32(_addr + offsetof(unwind_info_section_header, + personalityArraySectionOffset)); + } + uint32_t personalityArrayCount() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, personalityArrayCount)); + } + uint32_t indexSectionOffset() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, indexSectionOffset)); + } + uint32_t indexCount() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, indexCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionIndexArray { +public: + UnwindSectionIndexArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + functionOffset)); + } + uint32_t secondLevelPagesSectionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + secondLevelPagesSectionOffset)); + } + uint32_t lsdaIndexArraySectionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + lsdaIndexArraySectionOffset)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionRegularPageHeader { +public: + UnwindSectionRegularPageHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t kind() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_regular_second_level_page_header, kind)); + } + uint16_t entryPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_regular_second_level_page_header, + entryPageOffset)); + } + uint16_t entryCount() const { + return _addressSpace.get16( + _addr + + offsetof(unwind_info_regular_second_level_page_header, entryCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionRegularArray { +public: + UnwindSectionRegularArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_regular_second_level_entry, index, + functionOffset)); + } + uint32_t encoding(uint32_t index) const { + return _addressSpace.get32( + _addr + + arrayoffsetof(unwind_info_regular_second_level_entry, index, encoding)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionCompressedPageHeader { +public: + UnwindSectionCompressedPageHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t kind() const { + return _addressSpace.get32( + _addr + + offsetof(unwind_info_compressed_second_level_page_header, kind)); + } + uint16_t entryPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + entryPageOffset)); + } + uint16_t entryCount() const { + return _addressSpace.get16( + _addr + + offsetof(unwind_info_compressed_second_level_page_header, entryCount)); + } + uint16_t encodingsPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + encodingsPageOffset)); + } + uint16_t encodingsCount() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + encodingsCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionCompressedArray { +public: + UnwindSectionCompressedArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET( + _addressSpace.get32(_addr + index * sizeof(uint32_t))); + } + uint16_t encodingIndex(uint32_t index) const { + return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX( + _addressSpace.get32(_addr + index * sizeof(uint32_t))); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionLsdaArray { +public: + UnwindSectionLsdaArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, + index, functionOffset)); + } + uint32_t lsdaOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, + index, lsdaOffset)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + +class _LIBUNWIND_HIDDEN AbstractUnwindCursor { +public: + // NOTE: provide a class specific placement deallocation function (S5.3.4 p20) + // This avoids an unnecessary dependency to libc++abi. + void operator delete(void *, size_t) {} + + virtual ~AbstractUnwindCursor() {} + virtual bool validReg(int) { _LIBUNWIND_ABORT("validReg not implemented"); } + virtual unw_word_t getReg(int) { _LIBUNWIND_ABORT("getReg not implemented"); } + virtual void setReg(int, unw_word_t) { + _LIBUNWIND_ABORT("setReg not implemented"); + } + virtual bool validFloatReg(int) { + _LIBUNWIND_ABORT("validFloatReg not implemented"); + } + virtual unw_fpreg_t getFloatReg(int) { + _LIBUNWIND_ABORT("getFloatReg not implemented"); + } + virtual void setFloatReg(int, unw_fpreg_t) { + _LIBUNWIND_ABORT("setFloatReg not implemented"); + } + virtual int step(bool = false) { _LIBUNWIND_ABORT("step not implemented"); } + virtual void getInfo(unw_proc_info_t *) { + _LIBUNWIND_ABORT("getInfo not implemented"); + } + virtual void jumpto() { _LIBUNWIND_ABORT("jumpto not implemented"); } + virtual bool isSignalFrame() { + _LIBUNWIND_ABORT("isSignalFrame not implemented"); + } + virtual bool getFunctionName(char *, size_t, unw_word_t *) { + _LIBUNWIND_ABORT("getFunctionName not implemented"); + } + virtual void setInfoBasedOnIPRegister(bool = false) { + _LIBUNWIND_ABORT("setInfoBasedOnIPRegister not implemented"); + } + virtual const char *getRegisterName(int) { + _LIBUNWIND_ABORT("getRegisterName not implemented"); + } +#ifdef __arm__ + virtual void saveVFPAsX() { _LIBUNWIND_ABORT("saveVFPAsX not implemented"); } +#endif + +#ifdef _AIX + virtual uintptr_t getDataRelBase() { + _LIBUNWIND_ABORT("getDataRelBase not implemented"); + } +#endif + +#if defined(_LIBUNWIND_USE_CET) + virtual void *get_registers() { + _LIBUNWIND_ABORT("get_registers not implemented"); + } +#endif +}; + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32) + +/// \c UnwindCursor contains all state (including all register values) during +/// an unwind. This is normally stack-allocated inside a unw_cursor_t. +template +class UnwindCursor : public AbstractUnwindCursor { + typedef typename A::pint_t pint_t; +public: + UnwindCursor(unw_context_t *context, A &as); + UnwindCursor(CONTEXT *context, A &as); + UnwindCursor(A &as, void *threadArg); + virtual ~UnwindCursor() {} + virtual bool validReg(int); + virtual unw_word_t getReg(int); + virtual void setReg(int, unw_word_t); + virtual bool validFloatReg(int); + virtual unw_fpreg_t getFloatReg(int); + virtual void setFloatReg(int, unw_fpreg_t); + virtual int step(bool = false); + virtual void getInfo(unw_proc_info_t *); + virtual void jumpto(); + virtual bool isSignalFrame(); + virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); + virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); + virtual const char *getRegisterName(int num); +#ifdef __arm__ + virtual void saveVFPAsX(); +#endif + + DISPATCHER_CONTEXT *getDispatcherContext() { return &_dispContext; } + void setDispatcherContext(DISPATCHER_CONTEXT *disp) { + _dispContext = *disp; + _info.lsda = reinterpret_cast(_dispContext.HandlerData); + if (_dispContext.LanguageHandler) { + _info.handler = reinterpret_cast(__libunwind_seh_personality); + } else + _info.handler = 0; + } + + // libunwind does not and should not depend on C++ library which means that we + // need our own definition of inline placement new. + static void *operator new(size_t, UnwindCursor *p) { return p; } + +private: + + pint_t getLastPC() const { return _dispContext.ControlPc; } + void setLastPC(pint_t pc) { _dispContext.ControlPc = pc; } + RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) { +#ifdef __arm__ + // Remove the thumb bit; FunctionEntry ranges don't include the thumb bit. + pc &= ~1U; +#endif + // If pc points exactly at the end of the range, we might resolve the + // next function instead. Decrement pc by 1 to fit inside the current + // function. + pc -= 1; + _dispContext.FunctionEntry = RtlLookupFunctionEntry(pc, + &_dispContext.ImageBase, + _dispContext.HistoryTable); + *base = _dispContext.ImageBase; + return _dispContext.FunctionEntry; + } + bool getInfoFromSEH(pint_t pc); + int stepWithSEHData() { + _dispContext.LanguageHandler = RtlVirtualUnwind(UNW_FLAG_UHANDLER, + _dispContext.ImageBase, + _dispContext.ControlPc, + _dispContext.FunctionEntry, + _dispContext.ContextRecord, + &_dispContext.HandlerData, + &_dispContext.EstablisherFrame, + NULL); + // Update some fields of the unwind info now, since we have them. + _info.lsda = reinterpret_cast(_dispContext.HandlerData); + if (_dispContext.LanguageHandler) { + _info.handler = reinterpret_cast(__libunwind_seh_personality); + } else + _info.handler = 0; + return UNW_STEP_SUCCESS; + } + + A &_addressSpace; + unw_proc_info_t _info; + DISPATCHER_CONTEXT _dispContext; + CONTEXT _msContext; + UNWIND_HISTORY_TABLE _histTable; + bool _unwindInfoMissing; +}; + + +template +UnwindCursor::UnwindCursor(unw_context_t *context, A &as) + : _addressSpace(as), _unwindInfoMissing(false) { + static_assert((check_fit, unw_cursor_t>::does_fit), + "UnwindCursor<> does not fit in unw_cursor_t"); + static_assert((alignof(UnwindCursor) <= alignof(unw_cursor_t)), + "UnwindCursor<> requires more alignment than unw_cursor_t"); + memset(&_info, 0, sizeof(_info)); + memset(&_histTable, 0, sizeof(_histTable)); + memset(&_dispContext, 0, sizeof(_dispContext)); + _dispContext.ContextRecord = &_msContext; + _dispContext.HistoryTable = &_histTable; + // Initialize MS context from ours. + R r(context); + RtlCaptureContext(&_msContext); + _msContext.ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_FLOATING_POINT; +#if defined(_LIBUNWIND_TARGET_X86_64) + _msContext.Rax = r.getRegister(UNW_X86_64_RAX); + _msContext.Rcx = r.getRegister(UNW_X86_64_RCX); + _msContext.Rdx = r.getRegister(UNW_X86_64_RDX); + _msContext.Rbx = r.getRegister(UNW_X86_64_RBX); + _msContext.Rsp = r.getRegister(UNW_X86_64_RSP); + _msContext.Rbp = r.getRegister(UNW_X86_64_RBP); + _msContext.Rsi = r.getRegister(UNW_X86_64_RSI); + _msContext.Rdi = r.getRegister(UNW_X86_64_RDI); + _msContext.R8 = r.getRegister(UNW_X86_64_R8); + _msContext.R9 = r.getRegister(UNW_X86_64_R9); + _msContext.R10 = r.getRegister(UNW_X86_64_R10); + _msContext.R11 = r.getRegister(UNW_X86_64_R11); + _msContext.R12 = r.getRegister(UNW_X86_64_R12); + _msContext.R13 = r.getRegister(UNW_X86_64_R13); + _msContext.R14 = r.getRegister(UNW_X86_64_R14); + _msContext.R15 = r.getRegister(UNW_X86_64_R15); + _msContext.Rip = r.getRegister(UNW_REG_IP); + union { + v128 v; + M128A m; + } t; + t.v = r.getVectorRegister(UNW_X86_64_XMM0); + _msContext.Xmm0 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM1); + _msContext.Xmm1 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM2); + _msContext.Xmm2 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM3); + _msContext.Xmm3 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM4); + _msContext.Xmm4 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM5); + _msContext.Xmm5 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM6); + _msContext.Xmm6 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM7); + _msContext.Xmm7 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM8); + _msContext.Xmm8 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM9); + _msContext.Xmm9 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM10); + _msContext.Xmm10 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM11); + _msContext.Xmm11 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM12); + _msContext.Xmm12 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM13); + _msContext.Xmm13 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM14); + _msContext.Xmm14 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM15); + _msContext.Xmm15 = t.m; +#elif defined(_LIBUNWIND_TARGET_ARM) + _msContext.R0 = r.getRegister(UNW_ARM_R0); + _msContext.R1 = r.getRegister(UNW_ARM_R1); + _msContext.R2 = r.getRegister(UNW_ARM_R2); + _msContext.R3 = r.getRegister(UNW_ARM_R3); + _msContext.R4 = r.getRegister(UNW_ARM_R4); + _msContext.R5 = r.getRegister(UNW_ARM_R5); + _msContext.R6 = r.getRegister(UNW_ARM_R6); + _msContext.R7 = r.getRegister(UNW_ARM_R7); + _msContext.R8 = r.getRegister(UNW_ARM_R8); + _msContext.R9 = r.getRegister(UNW_ARM_R9); + _msContext.R10 = r.getRegister(UNW_ARM_R10); + _msContext.R11 = r.getRegister(UNW_ARM_R11); + _msContext.R12 = r.getRegister(UNW_ARM_R12); + _msContext.Sp = r.getRegister(UNW_ARM_SP); + _msContext.Lr = r.getRegister(UNW_ARM_LR); + _msContext.Pc = r.getRegister(UNW_ARM_IP); + for (int i = UNW_ARM_D0; i <= UNW_ARM_D31; ++i) { + union { + uint64_t w; + double d; + } d; + d.d = r.getFloatRegister(i); + _msContext.D[i - UNW_ARM_D0] = d.w; + } +#elif defined(_LIBUNWIND_TARGET_AARCH64) + for (int i = UNW_AARCH64_X0; i <= UNW_ARM64_X30; ++i) + _msContext.X[i - UNW_AARCH64_X0] = r.getRegister(i); + _msContext.Sp = r.getRegister(UNW_REG_SP); + _msContext.Pc = r.getRegister(UNW_REG_IP); + for (int i = UNW_AARCH64_V0; i <= UNW_ARM64_D31; ++i) + _msContext.V[i - UNW_AARCH64_V0].D[0] = r.getFloatRegister(i); +#endif +} + +template +UnwindCursor::UnwindCursor(CONTEXT *context, A &as) + : _addressSpace(as), _unwindInfoMissing(false) { + static_assert((check_fit, unw_cursor_t>::does_fit), + "UnwindCursor<> does not fit in unw_cursor_t"); + memset(&_info, 0, sizeof(_info)); + memset(&_histTable, 0, sizeof(_histTable)); + memset(&_dispContext, 0, sizeof(_dispContext)); + _dispContext.ContextRecord = &_msContext; + _dispContext.HistoryTable = &_histTable; + _msContext = *context; +} + + +template +bool UnwindCursor::validReg(int regNum) { + if (regNum == UNW_REG_IP || regNum == UNW_REG_SP) return true; +#if defined(_LIBUNWIND_TARGET_X86_64) + if (regNum >= UNW_X86_64_RAX && regNum <= UNW_X86_64_RIP) return true; +#elif defined(_LIBUNWIND_TARGET_ARM) + if ((regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) || + regNum == UNW_ARM_RA_AUTH_CODE) + return true; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + if (regNum >= UNW_AARCH64_X0 && regNum <= UNW_ARM64_X30) return true; +#endif + return false; +} + +template +unw_word_t UnwindCursor::getReg(int regNum) { + switch (regNum) { +#if defined(_LIBUNWIND_TARGET_X86_64) + case UNW_X86_64_RIP: + case UNW_REG_IP: return _msContext.Rip; + case UNW_X86_64_RAX: return _msContext.Rax; + case UNW_X86_64_RDX: return _msContext.Rdx; + case UNW_X86_64_RCX: return _msContext.Rcx; + case UNW_X86_64_RBX: return _msContext.Rbx; + case UNW_REG_SP: + case UNW_X86_64_RSP: return _msContext.Rsp; + case UNW_X86_64_RBP: return _msContext.Rbp; + case UNW_X86_64_RSI: return _msContext.Rsi; + case UNW_X86_64_RDI: return _msContext.Rdi; + case UNW_X86_64_R8: return _msContext.R8; + case UNW_X86_64_R9: return _msContext.R9; + case UNW_X86_64_R10: return _msContext.R10; + case UNW_X86_64_R11: return _msContext.R11; + case UNW_X86_64_R12: return _msContext.R12; + case UNW_X86_64_R13: return _msContext.R13; + case UNW_X86_64_R14: return _msContext.R14; + case UNW_X86_64_R15: return _msContext.R15; +#elif defined(_LIBUNWIND_TARGET_ARM) + case UNW_ARM_R0: return _msContext.R0; + case UNW_ARM_R1: return _msContext.R1; + case UNW_ARM_R2: return _msContext.R2; + case UNW_ARM_R3: return _msContext.R3; + case UNW_ARM_R4: return _msContext.R4; + case UNW_ARM_R5: return _msContext.R5; + case UNW_ARM_R6: return _msContext.R6; + case UNW_ARM_R7: return _msContext.R7; + case UNW_ARM_R8: return _msContext.R8; + case UNW_ARM_R9: return _msContext.R9; + case UNW_ARM_R10: return _msContext.R10; + case UNW_ARM_R11: return _msContext.R11; + case UNW_ARM_R12: return _msContext.R12; + case UNW_REG_SP: + case UNW_ARM_SP: return _msContext.Sp; + case UNW_ARM_LR: return _msContext.Lr; + case UNW_REG_IP: + case UNW_ARM_IP: return _msContext.Pc; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + case UNW_REG_SP: return _msContext.Sp; + case UNW_REG_IP: return _msContext.Pc; + default: return _msContext.X[regNum - UNW_AARCH64_X0]; +#endif + } + _LIBUNWIND_ABORT("unsupported register"); +} + +template +void UnwindCursor::setReg(int regNum, unw_word_t value) { + switch (regNum) { +#if defined(_LIBUNWIND_TARGET_X86_64) + case UNW_X86_64_RIP: + case UNW_REG_IP: _msContext.Rip = value; break; + case UNW_X86_64_RAX: _msContext.Rax = value; break; + case UNW_X86_64_RDX: _msContext.Rdx = value; break; + case UNW_X86_64_RCX: _msContext.Rcx = value; break; + case UNW_X86_64_RBX: _msContext.Rbx = value; break; + case UNW_REG_SP: + case UNW_X86_64_RSP: _msContext.Rsp = value; break; + case UNW_X86_64_RBP: _msContext.Rbp = value; break; + case UNW_X86_64_RSI: _msContext.Rsi = value; break; + case UNW_X86_64_RDI: _msContext.Rdi = value; break; + case UNW_X86_64_R8: _msContext.R8 = value; break; + case UNW_X86_64_R9: _msContext.R9 = value; break; + case UNW_X86_64_R10: _msContext.R10 = value; break; + case UNW_X86_64_R11: _msContext.R11 = value; break; + case UNW_X86_64_R12: _msContext.R12 = value; break; + case UNW_X86_64_R13: _msContext.R13 = value; break; + case UNW_X86_64_R14: _msContext.R14 = value; break; + case UNW_X86_64_R15: _msContext.R15 = value; break; +#elif defined(_LIBUNWIND_TARGET_ARM) + case UNW_ARM_R0: _msContext.R0 = value; break; + case UNW_ARM_R1: _msContext.R1 = value; break; + case UNW_ARM_R2: _msContext.R2 = value; break; + case UNW_ARM_R3: _msContext.R3 = value; break; + case UNW_ARM_R4: _msContext.R4 = value; break; + case UNW_ARM_R5: _msContext.R5 = value; break; + case UNW_ARM_R6: _msContext.R6 = value; break; + case UNW_ARM_R7: _msContext.R7 = value; break; + case UNW_ARM_R8: _msContext.R8 = value; break; + case UNW_ARM_R9: _msContext.R9 = value; break; + case UNW_ARM_R10: _msContext.R10 = value; break; + case UNW_ARM_R11: _msContext.R11 = value; break; + case UNW_ARM_R12: _msContext.R12 = value; break; + case UNW_REG_SP: + case UNW_ARM_SP: _msContext.Sp = value; break; + case UNW_ARM_LR: _msContext.Lr = value; break; + case UNW_REG_IP: + case UNW_ARM_IP: _msContext.Pc = value; break; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + case UNW_REG_SP: _msContext.Sp = value; break; + case UNW_REG_IP: _msContext.Pc = value; break; + case UNW_AARCH64_X0: + case UNW_AARCH64_X1: + case UNW_AARCH64_X2: + case UNW_AARCH64_X3: + case UNW_AARCH64_X4: + case UNW_AARCH64_X5: + case UNW_AARCH64_X6: + case UNW_AARCH64_X7: + case UNW_AARCH64_X8: + case UNW_AARCH64_X9: + case UNW_AARCH64_X10: + case UNW_AARCH64_X11: + case UNW_AARCH64_X12: + case UNW_AARCH64_X13: + case UNW_AARCH64_X14: + case UNW_AARCH64_X15: + case UNW_AARCH64_X16: + case UNW_AARCH64_X17: + case UNW_AARCH64_X18: + case UNW_AARCH64_X19: + case UNW_AARCH64_X20: + case UNW_AARCH64_X21: + case UNW_AARCH64_X22: + case UNW_AARCH64_X23: + case UNW_AARCH64_X24: + case UNW_AARCH64_X25: + case UNW_AARCH64_X26: + case UNW_AARCH64_X27: + case UNW_AARCH64_X28: + case UNW_AARCH64_FP: + case UNW_AARCH64_LR: _msContext.X[regNum - UNW_ARM64_X0] = value; break; +#endif + default: + _LIBUNWIND_ABORT("unsupported register"); + } +} + +template +bool UnwindCursor::validFloatReg(int regNum) { +#if defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) return true; + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) return true; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + if (regNum >= UNW_AARCH64_V0 && regNum <= UNW_ARM64_D31) return true; +#else + (void)regNum; +#endif + return false; +} + +template +unw_fpreg_t UnwindCursor::getFloatReg(int regNum) { +#if defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) { + union { + uint32_t w; + float f; + } d; + d.w = _msContext.S[regNum - UNW_ARM_S0]; + return d.f; + } + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) { + union { + uint64_t w; + double d; + } d; + d.w = _msContext.D[regNum - UNW_ARM_D0]; + return d.d; + } + _LIBUNWIND_ABORT("unsupported float register"); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + return _msContext.V[regNum - UNW_AARCH64_V0].D[0]; +#else + (void)regNum; + _LIBUNWIND_ABORT("float registers unimplemented"); +#endif +} + +template +void UnwindCursor::setFloatReg(int regNum, unw_fpreg_t value) { +#if defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) { + union { + uint32_t w; + float f; + } d; + d.f = (float)value; + _msContext.S[regNum - UNW_ARM_S0] = d.w; + } + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) { + union { + uint64_t w; + double d; + } d; + d.d = value; + _msContext.D[regNum - UNW_ARM_D0] = d.w; + } + _LIBUNWIND_ABORT("unsupported float register"); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + _msContext.V[regNum - UNW_AARCH64_V0].D[0] = value; +#else + (void)regNum; + (void)value; + _LIBUNWIND_ABORT("float registers unimplemented"); +#endif +} + +template void UnwindCursor::jumpto() { + RtlRestoreContext(&_msContext, nullptr); +} + +#ifdef __arm__ +template void UnwindCursor::saveVFPAsX() {} +#endif + +template +const char *UnwindCursor::getRegisterName(int regNum) { + return R::getRegisterName(regNum); +} + +template bool UnwindCursor::isSignalFrame() { + return false; +} + +#else // !defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) || !defined(_WIN32) + +/// UnwindCursor contains all state (including all register values) during +/// an unwind. This is normally stack allocated inside a unw_cursor_t. +template +class UnwindCursor : public AbstractUnwindCursor{ + typedef typename A::pint_t pint_t; +public: + UnwindCursor(unw_context_t *context, A &as); + UnwindCursor(A &as, void *threadArg); + virtual ~UnwindCursor() {} + virtual bool validReg(int); + virtual unw_word_t getReg(int); + virtual void setReg(int, unw_word_t); + virtual bool validFloatReg(int); + virtual unw_fpreg_t getFloatReg(int); + virtual void setFloatReg(int, unw_fpreg_t); + virtual int step(bool stage2 = false); + virtual void getInfo(unw_proc_info_t *); + virtual void jumpto(); + virtual bool isSignalFrame(); + virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); + virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); + virtual const char *getRegisterName(int num); +#ifdef __arm__ + virtual void saveVFPAsX(); +#endif + +#ifdef _AIX + virtual uintptr_t getDataRelBase(); +#endif + +#if defined(_LIBUNWIND_USE_CET) + virtual void *get_registers() { return &_registers; } +#endif + + // libunwind does not and should not depend on C++ library which means that we + // need our own definition of inline placement new. + static void *operator new(size_t, UnwindCursor *p) { return p; } + +private: + +#if defined(_LIBUNWIND_ARM_EHABI) + bool getInfoFromEHABISection(pint_t pc, const UnwindInfoSections §s); + + int stepWithEHABI() { + size_t len = 0; + size_t off = 0; + // FIXME: Calling decode_eht_entry() here is violating the libunwind + // abstraction layer. + const uint32_t *ehtp = + decode_eht_entry(reinterpret_cast(_info.unwind_info), + &off, &len); + if (_Unwind_VRS_Interpret((_Unwind_Context *)this, ehtp, off, len) != + _URC_CONTINUE_UNWIND) + return UNW_STEP_END; + return UNW_STEP_SUCCESS; + } +#endif + +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) + bool setInfoForSigReturn() { + R dummy; + return setInfoForSigReturn(dummy); + } + int stepThroughSigReturn() { + R dummy; + return stepThroughSigReturn(dummy); + } +#if defined(_LIBUNWIND_TARGET_AARCH64) + bool setInfoForSigReturn(Registers_arm64 &); + int stepThroughSigReturn(Registers_arm64 &); +#endif +#if defined(_LIBUNWIND_TARGET_RISCV) + bool setInfoForSigReturn(Registers_riscv &); + int stepThroughSigReturn(Registers_riscv &); +#endif +#if defined(_LIBUNWIND_TARGET_S390X) + bool setInfoForSigReturn(Registers_s390x &); + int stepThroughSigReturn(Registers_s390x &); +#endif + template bool setInfoForSigReturn(Registers &) { + return false; + } + template int stepThroughSigReturn(Registers &) { + return UNW_STEP_END; + } +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + bool getInfoFromFdeCie(const typename CFI_Parser::FDE_Info &fdeInfo, + const typename CFI_Parser::CIE_Info &cieInfo, + pint_t pc, uintptr_t dso_base); + bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint=0); + int stepWithDwarfFDE(bool stage2) { + return DwarfInstructions::stepWithDwarf( + _addressSpace, (pint_t)this->getReg(UNW_REG_IP), + (pint_t)_info.unwind_info, _registers, _isSignalFrame, stage2); + } +#endif + +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + bool getInfoFromCompactEncodingSection(pint_t pc, + const UnwindInfoSections §s); + int stepWithCompactEncoding(bool stage2 = false) { +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + if ( compactSaysUseDwarf() ) + return stepWithDwarfFDE(stage2); +#endif + R dummy; + return stepWithCompactEncoding(dummy); + } + +#if defined(_LIBUNWIND_TARGET_X86_64) + int stepWithCompactEncoding(Registers_x86_64 &) { + return CompactUnwinder_x86_64::stepWithCompactEncoding( + _info.format, _info.start_ip, _addressSpace, _registers); + } +#endif + +#if defined(_LIBUNWIND_TARGET_I386) + int stepWithCompactEncoding(Registers_x86 &) { + return CompactUnwinder_x86::stepWithCompactEncoding( + _info.format, (uint32_t)_info.start_ip, _addressSpace, _registers); + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC) + int stepWithCompactEncoding(Registers_ppc &) { + return UNW_EINVAL; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) + int stepWithCompactEncoding(Registers_ppc64 &) { + return UNW_EINVAL; + } +#endif + + +#if defined(_LIBUNWIND_TARGET_AARCH64) + int stepWithCompactEncoding(Registers_arm64 &) { + return CompactUnwinder_arm64::stepWithCompactEncoding( + _info.format, _info.start_ip, _addressSpace, _registers); + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_O32) + int stepWithCompactEncoding(Registers_mips_o32 &) { + return UNW_EINVAL; + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_NEWABI) + int stepWithCompactEncoding(Registers_mips_newabi &) { + return UNW_EINVAL; + } +#endif + +#if defined(_LIBUNWIND_TARGET_LOONGARCH) + int stepWithCompactEncoding(Registers_loongarch &) { return UNW_EINVAL; } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + int stepWithCompactEncoding(Registers_sparc &) { return UNW_EINVAL; } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC64) + int stepWithCompactEncoding(Registers_sparc64 &) { return UNW_EINVAL; } +#endif + +#if defined (_LIBUNWIND_TARGET_RISCV) + int stepWithCompactEncoding(Registers_riscv &) { + return UNW_EINVAL; + } +#endif + + bool compactSaysUseDwarf(uint32_t *offset=NULL) const { + R dummy; + return compactSaysUseDwarf(dummy, offset); + } + +#if defined(_LIBUNWIND_TARGET_X86_64) + bool compactSaysUseDwarf(Registers_x86_64 &, uint32_t *offset) const { + if ((_info.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_X86_64_DWARF_SECTION_OFFSET); + return true; + } + return false; + } +#endif + +#if defined(_LIBUNWIND_TARGET_I386) + bool compactSaysUseDwarf(Registers_x86 &, uint32_t *offset) const { + if ((_info.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_X86_DWARF_SECTION_OFFSET); + return true; + } + return false; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC) + bool compactSaysUseDwarf(Registers_ppc &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) + bool compactSaysUseDwarf(Registers_ppc64 &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_AARCH64) + bool compactSaysUseDwarf(Registers_arm64 &, uint32_t *offset) const { + if ((_info.format & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_ARM64_DWARF_SECTION_OFFSET); + return true; + } + return false; + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_O32) + bool compactSaysUseDwarf(Registers_mips_o32 &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_NEWABI) + bool compactSaysUseDwarf(Registers_mips_newabi &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_LOONGARCH) + bool compactSaysUseDwarf(Registers_loongarch &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + bool compactSaysUseDwarf(Registers_sparc &, uint32_t *) const { return true; } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC64) + bool compactSaysUseDwarf(Registers_sparc64 &, uint32_t *) const { + return true; + } +#endif + +#if defined (_LIBUNWIND_TARGET_RISCV) + bool compactSaysUseDwarf(Registers_riscv &, uint32_t *) const { + return true; + } +#endif + +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + compact_unwind_encoding_t dwarfEncoding() const { + R dummy; + return dwarfEncoding(dummy); + } + +#if defined(_LIBUNWIND_TARGET_X86_64) + compact_unwind_encoding_t dwarfEncoding(Registers_x86_64 &) const { + return UNWIND_X86_64_MODE_DWARF; + } +#endif + +#if defined(_LIBUNWIND_TARGET_I386) + compact_unwind_encoding_t dwarfEncoding(Registers_x86 &) const { + return UNWIND_X86_MODE_DWARF; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC) + compact_unwind_encoding_t dwarfEncoding(Registers_ppc &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) + compact_unwind_encoding_t dwarfEncoding(Registers_ppc64 &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_AARCH64) + compact_unwind_encoding_t dwarfEncoding(Registers_arm64 &) const { + return UNWIND_ARM64_MODE_DWARF; + } +#endif + +#if defined(_LIBUNWIND_TARGET_ARM) + compact_unwind_encoding_t dwarfEncoding(Registers_arm &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_OR1K) + compact_unwind_encoding_t dwarfEncoding(Registers_or1k &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_HEXAGON) + compact_unwind_encoding_t dwarfEncoding(Registers_hexagon &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_MIPS_O32) + compact_unwind_encoding_t dwarfEncoding(Registers_mips_o32 &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_MIPS_NEWABI) + compact_unwind_encoding_t dwarfEncoding(Registers_mips_newabi &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_LOONGARCH) + compact_unwind_encoding_t dwarfEncoding(Registers_loongarch &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + compact_unwind_encoding_t dwarfEncoding(Registers_sparc &) const { return 0; } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC64) + compact_unwind_encoding_t dwarfEncoding(Registers_sparc64 &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_RISCV) + compact_unwind_encoding_t dwarfEncoding(Registers_riscv &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_S390X) + compact_unwind_encoding_t dwarfEncoding(Registers_s390x &) const { + return 0; + } +#endif + +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + // For runtime environments using SEH unwind data without Windows runtime + // support. + pint_t getLastPC() const { /* FIXME: Implement */ return 0; } + void setLastPC(pint_t pc) { /* FIXME: Implement */ } + RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) { + /* FIXME: Implement */ + *base = 0; + return nullptr; + } + bool getInfoFromSEH(pint_t pc); + int stepWithSEHData() { /* FIXME: Implement */ return 0; } +#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + bool getInfoFromTBTable(pint_t pc, R ®isters); + int stepWithTBTable(pint_t pc, tbtable *TBTable, R ®isters, + bool &isSignalFrame); + int stepWithTBTableData() { + return stepWithTBTable(reinterpret_cast(this->getReg(UNW_REG_IP)), + reinterpret_cast(_info.unwind_info), + _registers, _isSignalFrame); + } +#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + + A &_addressSpace; + R _registers; + unw_proc_info_t _info; + bool _unwindInfoMissing; + bool _isSignalFrame; +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) + bool _isSigReturn = false; +#endif +}; + + +template +UnwindCursor::UnwindCursor(unw_context_t *context, A &as) + : _addressSpace(as), _registers(context), _unwindInfoMissing(false), + _isSignalFrame(false) { + static_assert((check_fit, unw_cursor_t>::does_fit), + "UnwindCursor<> does not fit in unw_cursor_t"); + static_assert((alignof(UnwindCursor) <= alignof(unw_cursor_t)), + "UnwindCursor<> requires more alignment than unw_cursor_t"); + memset(&_info, 0, sizeof(_info)); +} + +template +UnwindCursor::UnwindCursor(A &as, void *) + : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) { + memset(&_info, 0, sizeof(_info)); + // FIXME + // fill in _registers from thread arg +} + + +template +bool UnwindCursor::validReg(int regNum) { + return _registers.validRegister(regNum); +} + +template +unw_word_t UnwindCursor::getReg(int regNum) { + return _registers.getRegister(regNum); +} + +template +void UnwindCursor::setReg(int regNum, unw_word_t value) { + _registers.setRegister(regNum, (typename A::pint_t)value); +} + +template +bool UnwindCursor::validFloatReg(int regNum) { + return _registers.validFloatRegister(regNum); +} + +template +unw_fpreg_t UnwindCursor::getFloatReg(int regNum) { + return _registers.getFloatRegister(regNum); +} + +template +void UnwindCursor::setFloatReg(int regNum, unw_fpreg_t value) { + _registers.setFloatRegister(regNum, value); +} + +template void UnwindCursor::jumpto() { + _registers.jumpto(); +} + +#ifdef __arm__ +template void UnwindCursor::saveVFPAsX() { + _registers.saveVFPAsX(); +} +#endif + +#ifdef _AIX +template +uintptr_t UnwindCursor::getDataRelBase() { + return reinterpret_cast(_info.extra); +} +#endif + +template +const char *UnwindCursor::getRegisterName(int regNum) { + return _registers.getRegisterName(regNum); +} + +template bool UnwindCursor::isSignalFrame() { + return _isSignalFrame; +} + +#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + +#if defined(_LIBUNWIND_ARM_EHABI) +template +struct EHABISectionIterator { + typedef EHABISectionIterator _Self; + + typedef typename A::pint_t value_type; + typedef typename A::pint_t* pointer; + typedef typename A::pint_t& reference; + typedef size_t size_type; + typedef size_t difference_type; + + static _Self begin(A& addressSpace, const UnwindInfoSections& sects) { + return _Self(addressSpace, sects, 0); + } + static _Self end(A& addressSpace, const UnwindInfoSections& sects) { + return _Self(addressSpace, sects, + sects.arm_section_length / sizeof(EHABIIndexEntry)); + } + + EHABISectionIterator(A& addressSpace, const UnwindInfoSections& sects, size_t i) + : _i(i), _addressSpace(&addressSpace), _sects(§s) {} + + _Self& operator++() { ++_i; return *this; } + _Self& operator+=(size_t a) { _i += a; return *this; } + _Self& operator--() { assert(_i > 0); --_i; return *this; } + _Self& operator-=(size_t a) { assert(_i >= a); _i -= a; return *this; } + + _Self operator+(size_t a) { _Self out = *this; out._i += a; return out; } + _Self operator-(size_t a) { assert(_i >= a); _Self out = *this; out._i -= a; return out; } + + size_t operator-(const _Self& other) const { return _i - other._i; } + + bool operator==(const _Self& other) const { + assert(_addressSpace == other._addressSpace); + assert(_sects == other._sects); + return _i == other._i; + } + + bool operator!=(const _Self& other) const { + assert(_addressSpace == other._addressSpace); + assert(_sects == other._sects); + return _i != other._i; + } + + typename A::pint_t operator*() const { return functionAddress(); } + + typename A::pint_t functionAddress() const { + typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof( + EHABIIndexEntry, _i, functionOffset); + return indexAddr + signExtendPrel31(_addressSpace->get32(indexAddr)); + } + + typename A::pint_t dataAddress() { + typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof( + EHABIIndexEntry, _i, data); + return indexAddr; + } + + private: + size_t _i; + A* _addressSpace; + const UnwindInfoSections* _sects; +}; + +namespace { + +template +EHABISectionIterator EHABISectionUpperBound( + EHABISectionIterator first, + EHABISectionIterator last, + typename A::pint_t value) { + size_t len = last - first; + while (len > 0) { + size_t l2 = len / 2; + EHABISectionIterator m = first + l2; + if (value < *m) { + len = l2; + } else { + first = ++m; + len -= l2 + 1; + } + } + return first; +} + +} + +template +bool UnwindCursor::getInfoFromEHABISection( + pint_t pc, + const UnwindInfoSections §s) { + EHABISectionIterator begin = + EHABISectionIterator::begin(_addressSpace, sects); + EHABISectionIterator end = + EHABISectionIterator::end(_addressSpace, sects); + if (begin == end) + return false; + + EHABISectionIterator itNextPC = EHABISectionUpperBound(begin, end, pc); + if (itNextPC == begin) + return false; + EHABISectionIterator itThisPC = itNextPC - 1; + + pint_t thisPC = itThisPC.functionAddress(); + // If an exception is thrown from a function, corresponding to the last entry + // in the table, we don't really know the function extent and have to choose a + // value for nextPC. Choosing max() will allow the range check during trace to + // succeed. + pint_t nextPC = (itNextPC == end) ? UINTPTR_MAX : itNextPC.functionAddress(); + pint_t indexDataAddr = itThisPC.dataAddress(); + + if (indexDataAddr == 0) + return false; + + uint32_t indexData = _addressSpace.get32(indexDataAddr); + if (indexData == UNW_EXIDX_CANTUNWIND) + return false; + + // If the high bit is set, the exception handling table entry is inline inside + // the index table entry on the second word (aka |indexDataAddr|). Otherwise, + // the table points at an offset in the exception handling table (section 5 + // EHABI). + pint_t exceptionTableAddr; + uint32_t exceptionTableData; + bool isSingleWordEHT; + if (indexData & 0x80000000) { + exceptionTableAddr = indexDataAddr; + // TODO(ajwong): Should this data be 0? + exceptionTableData = indexData; + isSingleWordEHT = true; + } else { + exceptionTableAddr = indexDataAddr + signExtendPrel31(indexData); + exceptionTableData = _addressSpace.get32(exceptionTableAddr); + isSingleWordEHT = false; + } + + // Now we know the 3 things: + // exceptionTableAddr -- exception handler table entry. + // exceptionTableData -- the data inside the first word of the eht entry. + // isSingleWordEHT -- whether the entry is in the index. + unw_word_t personalityRoutine = 0xbadf00d; + bool scope32 = false; + uintptr_t lsda; + + // If the high bit in the exception handling table entry is set, the entry is + // in compact form (section 6.3 EHABI). + if (exceptionTableData & 0x80000000) { + // Grab the index of the personality routine from the compact form. + uint32_t choice = (exceptionTableData & 0x0f000000) >> 24; + uint32_t extraWords = 0; + switch (choice) { + case 0: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr0; + extraWords = 0; + scope32 = false; + lsda = isSingleWordEHT ? 0 : (exceptionTableAddr + 4); + break; + case 1: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr1; + extraWords = (exceptionTableData & 0x00ff0000) >> 16; + scope32 = false; + lsda = exceptionTableAddr + (extraWords + 1) * 4; + break; + case 2: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr2; + extraWords = (exceptionTableData & 0x00ff0000) >> 16; + scope32 = true; + lsda = exceptionTableAddr + (extraWords + 1) * 4; + break; + default: + _LIBUNWIND_ABORT("unknown personality routine"); + return false; + } + + if (isSingleWordEHT) { + if (extraWords != 0) { + _LIBUNWIND_ABORT("index inlined table detected but pr function " + "requires extra words"); + return false; + } + } + } else { + pint_t personalityAddr = + exceptionTableAddr + signExtendPrel31(exceptionTableData); + personalityRoutine = personalityAddr; + + // ARM EHABI # 6.2, # 9.2 + // + // +---- ehtp + // v + // +--------------------------------------+ + // | +--------+--------+--------+-------+ | + // | |0| prel31 to personalityRoutine | | + // | +--------+--------+--------+-------+ | + // | | N | unwind opcodes | | <-- UnwindData + // | +--------+--------+--------+-------+ | + // | | Word 2 unwind opcodes | | + // | +--------+--------+--------+-------+ | + // | ... | + // | +--------+--------+--------+-------+ | + // | | Word N unwind opcodes | | + // | +--------+--------+--------+-------+ | + // | | LSDA | | <-- lsda + // | | ... | | + // | +--------+--------+--------+-------+ | + // +--------------------------------------+ + + uint32_t *UnwindData = reinterpret_cast(exceptionTableAddr) + 1; + uint32_t FirstDataWord = *UnwindData; + size_t N = ((FirstDataWord >> 24) & 0xff); + size_t NDataWords = N + 1; + lsda = reinterpret_cast(UnwindData + NDataWords); + } + + _info.start_ip = thisPC; + _info.end_ip = nextPC; + _info.handler = personalityRoutine; + _info.unwind_info = exceptionTableAddr; + _info.lsda = lsda; + // flags is pr_cache.additional. See EHABI #7.2 for definition of bit 0. + _info.flags = (isSingleWordEHT ? 1 : 0) | (scope32 ? 0x2 : 0); // Use enum? + + return true; +} +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +template +bool UnwindCursor::getInfoFromFdeCie( + const typename CFI_Parser::FDE_Info &fdeInfo, + const typename CFI_Parser::CIE_Info &cieInfo, pint_t pc, + uintptr_t dso_base) { + typename CFI_Parser::PrologInfo prolog; + if (CFI_Parser::parseFDEInstructions(_addressSpace, fdeInfo, cieInfo, pc, + R::getArch(), &prolog)) { + // Save off parsed FDE info + _info.start_ip = fdeInfo.pcStart; + _info.end_ip = fdeInfo.pcEnd; + _info.lsda = fdeInfo.lsda; + _info.handler = cieInfo.personality; + // Some frameless functions need SP altered when resuming in function, so + // propagate spExtraArgSize. + _info.gp = prolog.spExtraArgSize; + _info.flags = 0; + _info.format = dwarfEncoding(); + _info.unwind_info = fdeInfo.fdeStart; + _info.unwind_info_size = static_cast(fdeInfo.fdeLength); + _info.extra = static_cast(dso_base); + return true; + } + return false; +} + +template +bool UnwindCursor::getInfoFromDwarfSection(pint_t pc, + const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + bool foundFDE = false; + bool foundInCache = false; + // If compact encoding table gave offset into dwarf section, go directly there + if (fdeSectionOffsetHint != 0) { + foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + sects.dwarf_section_length, + sects.dwarf_section + fdeSectionOffsetHint, + &fdeInfo, &cieInfo); + } +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + if (!foundFDE && (sects.dwarf_index_section != 0)) { + foundFDE = EHHeaderParser::findFDE( + _addressSpace, pc, sects.dwarf_index_section, + (uint32_t)sects.dwarf_index_section_length, &fdeInfo, &cieInfo); + } +#endif + if (!foundFDE) { + // otherwise, search cache of previously found FDEs. + pint_t cachedFDE = DwarfFDECache::findFDE(sects.dso_base, pc); + if (cachedFDE != 0) { + foundFDE = + CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + sects.dwarf_section_length, + cachedFDE, &fdeInfo, &cieInfo); + foundInCache = foundFDE; + } + } + if (!foundFDE) { + // Still not found, do full scan of __eh_frame section. + foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + sects.dwarf_section_length, 0, + &fdeInfo, &cieInfo); + } + if (foundFDE) { + if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, sects.dso_base)) { + // Add to cache (to make next lookup faster) if we had no hint + // and there was no index. + if (!foundInCache && (fdeSectionOffsetHint == 0)) { + #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + if (sects.dwarf_index_section == 0) + #endif + DwarfFDECache::add(sects.dso_base, fdeInfo.pcStart, fdeInfo.pcEnd, + fdeInfo.fdeStart); + } + return true; + } + } + //_LIBUNWIND_DEBUG_LOG("can't find/use FDE for pc=0x%llX", (uint64_t)pc); + return false; +} +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + + +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) +template +bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, + const UnwindInfoSections §s) { + const bool log = false; + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n", + (uint64_t)pc, (uint64_t)sects.dso_base); + + const UnwindSectionHeader sectionHeader(_addressSpace, + sects.compact_unwind_section); + if (sectionHeader.version() != UNWIND_SECTION_VERSION) + return false; + + // do a binary search of top level index to find page with unwind info + pint_t targetFunctionOffset = pc - sects.dso_base; + const UnwindSectionIndexArray topIndex(_addressSpace, + sects.compact_unwind_section + + sectionHeader.indexSectionOffset()); + uint32_t low = 0; + uint32_t high = sectionHeader.indexCount(); + uint32_t last = high - 1; + while (low < high) { + uint32_t mid = (low + high) / 2; + //if ( log ) fprintf(stderr, "\tmid=%d, low=%d, high=%d, *mid=0x%08X\n", + //mid, low, high, topIndex.functionOffset(mid)); + if (topIndex.functionOffset(mid) <= targetFunctionOffset) { + if ((mid == last) || + (topIndex.functionOffset(mid + 1) > targetFunctionOffset)) { + low = mid; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + const uint32_t firstLevelFunctionOffset = topIndex.functionOffset(low); + const uint32_t firstLevelNextPageFunctionOffset = + topIndex.functionOffset(low + 1); + const pint_t secondLevelAddr = + sects.compact_unwind_section + topIndex.secondLevelPagesSectionOffset(low); + const pint_t lsdaArrayStartAddr = + sects.compact_unwind_section + topIndex.lsdaIndexArraySectionOffset(low); + const pint_t lsdaArrayEndAddr = + sects.compact_unwind_section + topIndex.lsdaIndexArraySectionOffset(low+1); + if (log) + fprintf(stderr, "\tfirst level search for result index=%d " + "to secondLevelAddr=0x%llX\n", + low, (uint64_t) secondLevelAddr); + // do a binary search of second level page index + uint32_t encoding = 0; + pint_t funcStart = 0; + pint_t funcEnd = 0; + pint_t lsda = 0; + pint_t personality = 0; + uint32_t pageKind = _addressSpace.get32(secondLevelAddr); + if (pageKind == UNWIND_SECOND_LEVEL_REGULAR) { + // regular page + UnwindSectionRegularPageHeader pageHeader(_addressSpace, + secondLevelAddr); + UnwindSectionRegularArray pageIndex( + _addressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + // binary search looks for entry with e where index[e].offset <= pc < + // index[e+1].offset + if (log) + fprintf(stderr, "\tbinary search for targetFunctionOffset=0x%08llX in " + "regular page starting at secondLevelAddr=0x%llX\n", + (uint64_t) targetFunctionOffset, (uint64_t) secondLevelAddr); + low = 0; + high = pageHeader.entryCount(); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (pageIndex.functionOffset(mid) <= targetFunctionOffset) { + if (mid == (uint32_t)(pageHeader.entryCount() - 1)) { + // at end of table + low = mid; + funcEnd = firstLevelNextPageFunctionOffset + sects.dso_base; + break; + } else if (pageIndex.functionOffset(mid + 1) > targetFunctionOffset) { + // next is too big, so we found it + low = mid; + funcEnd = pageIndex.functionOffset(low + 1) + sects.dso_base; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + encoding = pageIndex.encoding(low); + funcStart = pageIndex.functionOffset(low) + sects.dso_base; + if (pc < funcStart) { + if (log) + fprintf( + stderr, + "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", + (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd); + return false; + } + if (pc > funcEnd) { + if (log) + fprintf( + stderr, + "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", + (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd); + return false; + } + } else if (pageKind == UNWIND_SECOND_LEVEL_COMPRESSED) { + // compressed page + UnwindSectionCompressedPageHeader pageHeader(_addressSpace, + secondLevelAddr); + UnwindSectionCompressedArray pageIndex( + _addressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + const uint32_t targetFunctionPageOffset = + (uint32_t)(targetFunctionOffset - firstLevelFunctionOffset); + // binary search looks for entry with e where index[e].offset <= pc < + // index[e+1].offset + if (log) + fprintf(stderr, "\tbinary search of compressed page starting at " + "secondLevelAddr=0x%llX\n", + (uint64_t) secondLevelAddr); + low = 0; + last = pageHeader.entryCount() - 1; + high = pageHeader.entryCount(); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (pageIndex.functionOffset(mid) <= targetFunctionPageOffset) { + if ((mid == last) || + (pageIndex.functionOffset(mid + 1) > targetFunctionPageOffset)) { + low = mid; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + funcStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset + + sects.dso_base; + if (low < last) + funcEnd = + pageIndex.functionOffset(low + 1) + firstLevelFunctionOffset + + sects.dso_base; + else + funcEnd = firstLevelNextPageFunctionOffset + sects.dso_base; + if (pc < funcStart) { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info, pc=0x%llX " + "not in second level compressed unwind table. " + "funcStart=0x%llX", + (uint64_t) pc, (uint64_t) funcStart); + return false; + } + if (pc > funcEnd) { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info, pc=0x%llX " + "not in second level compressed unwind table. " + "funcEnd=0x%llX", + (uint64_t) pc, (uint64_t) funcEnd); + return false; + } + uint16_t encodingIndex = pageIndex.encodingIndex(low); + if (encodingIndex < sectionHeader.commonEncodingsArrayCount()) { + // encoding is in common table in section header + encoding = _addressSpace.get32( + sects.compact_unwind_section + + sectionHeader.commonEncodingsArraySectionOffset() + + encodingIndex * sizeof(uint32_t)); + } else { + // encoding is in page specific table + uint16_t pageEncodingIndex = + encodingIndex - (uint16_t)sectionHeader.commonEncodingsArrayCount(); + encoding = _addressSpace.get32(secondLevelAddr + + pageHeader.encodingsPageOffset() + + pageEncodingIndex * sizeof(uint32_t)); + } + } else { + _LIBUNWIND_DEBUG_LOG( + "malformed __unwind_info at 0x%0llX bad second level page", + (uint64_t)sects.compact_unwind_section); + return false; + } + + // look up LSDA, if encoding says function has one + if (encoding & UNWIND_HAS_LSDA) { + UnwindSectionLsdaArray lsdaIndex(_addressSpace, lsdaArrayStartAddr); + uint32_t funcStartOffset = (uint32_t)(funcStart - sects.dso_base); + low = 0; + high = (uint32_t)(lsdaArrayEndAddr - lsdaArrayStartAddr) / + sizeof(unwind_info_section_header_lsda_index_entry); + // binary search looks for entry with exact match for functionOffset + if (log) + fprintf(stderr, + "\tbinary search of lsda table for targetFunctionOffset=0x%08X\n", + funcStartOffset); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (lsdaIndex.functionOffset(mid) == funcStartOffset) { + lsda = lsdaIndex.lsdaOffset(mid) + sects.dso_base; + break; + } else if (lsdaIndex.functionOffset(mid) < funcStartOffset) { + low = mid + 1; + } else { + high = mid; + } + } + if (lsda == 0) { + _LIBUNWIND_DEBUG_LOG("found encoding 0x%08X with HAS_LSDA bit set for " + "pc=0x%0llX, but lsda table has no entry", + encoding, (uint64_t) pc); + return false; + } + } + + // extract personality routine, if encoding says function has one + uint32_t personalityIndex = (encoding & UNWIND_PERSONALITY_MASK) >> + (__builtin_ctz(UNWIND_PERSONALITY_MASK)); + if (personalityIndex != 0) { + --personalityIndex; // change 1-based to zero-based index + if (personalityIndex >= sectionHeader.personalityArrayCount()) { + _LIBUNWIND_DEBUG_LOG("found encoding 0x%08X with personality index %d, " + "but personality table has only %d entries", + encoding, personalityIndex, + sectionHeader.personalityArrayCount()); + return false; + } + int32_t personalityDelta = (int32_t)_addressSpace.get32( + sects.compact_unwind_section + + sectionHeader.personalityArraySectionOffset() + + personalityIndex * sizeof(uint32_t)); + pint_t personalityPointer = sects.dso_base + (pint_t)personalityDelta; + personality = _addressSpace.getP(personalityPointer); + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " + "personalityDelta=0x%08X, personality=0x%08llX\n", + (uint64_t) pc, personalityDelta, (uint64_t) personality); + } + + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " + "encoding=0x%08X, lsda=0x%08llX for funcStart=0x%llX\n", + (uint64_t) pc, encoding, (uint64_t) lsda, (uint64_t) funcStart); + _info.start_ip = funcStart; + _info.end_ip = funcEnd; + _info.lsda = lsda; + _info.handler = personality; + _info.gp = 0; + _info.flags = 0; + _info.format = encoding; + _info.unwind_info = 0; + _info.unwind_info_size = 0; + _info.extra = sects.dso_base; + return true; +} +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) +template +bool UnwindCursor::getInfoFromSEH(pint_t pc) { + pint_t base; + RUNTIME_FUNCTION *unwindEntry = lookUpSEHUnwindInfo(pc, &base); + if (!unwindEntry) { + _LIBUNWIND_DEBUG_LOG("\tpc not in table, pc=0x%llX", (uint64_t) pc); + return false; + } + _info.gp = 0; + _info.flags = 0; + _info.format = 0; + _info.unwind_info_size = sizeof(RUNTIME_FUNCTION); + _info.unwind_info = reinterpret_cast(unwindEntry); + _info.extra = base; + _info.start_ip = base + unwindEntry->BeginAddress; +#ifdef _LIBUNWIND_TARGET_X86_64 + _info.end_ip = base + unwindEntry->EndAddress; + // Only fill in the handler and LSDA if they're stale. + if (pc != getLastPC()) { + UNWIND_INFO *xdata = reinterpret_cast(base + unwindEntry->UnwindData); + if (xdata->Flags & (UNW_FLAG_EHANDLER|UNW_FLAG_UHANDLER)) { + // The personality is given in the UNWIND_INFO itself. The LSDA immediately + // follows the UNWIND_INFO. (This follows how both Clang and MSVC emit + // these structures.) + // N.B. UNWIND_INFO structs are DWORD-aligned. + uint32_t lastcode = (xdata->CountOfCodes + 1) & ~1; + const uint32_t *handler = reinterpret_cast(&xdata->UnwindCodes[lastcode]); + _info.lsda = reinterpret_cast(handler+1); + _dispContext.HandlerData = reinterpret_cast(_info.lsda); + _dispContext.LanguageHandler = + reinterpret_cast(base + *handler); + if (*handler) { + _info.handler = reinterpret_cast(__libunwind_seh_personality); + } else + _info.handler = 0; + } else { + _info.lsda = 0; + _info.handler = 0; + } + } +#endif + setLastPC(pc); + return true; +} +#endif + +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) +// Masks for traceback table field xtbtable. +enum xTBTableMask : uint8_t { + reservedBit = 0x02, // The traceback table was incorrectly generated if set + // (see comments in function getInfoFromTBTable(). + ehInfoBit = 0x08 // Exception handling info is present if set +}; + +enum frameType : unw_word_t { + frameWithXLEHStateTable = 0, + frameWithEHInfo = 1 +}; + +extern "C" { +typedef _Unwind_Reason_Code __xlcxx_personality_v0_t(int, _Unwind_Action, + uint64_t, + _Unwind_Exception *, + struct _Unwind_Context *); +__attribute__((__weak__)) __xlcxx_personality_v0_t __xlcxx_personality_v0; +} + +static __xlcxx_personality_v0_t *xlcPersonalityV0; +static RWMutex xlcPersonalityV0InitLock; + +template +bool UnwindCursor::getInfoFromTBTable(pint_t pc, R ®isters) { + uint32_t *p = reinterpret_cast(pc); + + // Keep looking forward until a word of 0 is found. The traceback + // table starts at the following word. + while (*p) + ++p; + tbtable *TBTable = reinterpret_cast(p + 1); + + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if (!getFunctionName(functionBuf, sizeof(functionBuf), &offset)) { + functionName = ".anonymous."; + } + _LIBUNWIND_TRACE_UNWINDING("%s: Look up traceback table of func=%s at %p", + __func__, functionName, + reinterpret_cast(TBTable)); + } + + // If the traceback table does not contain necessary info, bypass this frame. + if (!TBTable->tb.has_tboff) + return false; + + // Structure tbtable_ext contains important data we are looking for. + p = reinterpret_cast(&TBTable->tb_ext); + + // Skip field parminfo if it exists. + if (TBTable->tb.fixedparms || TBTable->tb.floatparms) + ++p; + + // p now points to tb_offset, the offset from start of function to TB table. + unw_word_t start_ip = + reinterpret_cast(TBTable) - *p - sizeof(uint32_t); + unw_word_t end_ip = reinterpret_cast(TBTable); + ++p; + + _LIBUNWIND_TRACE_UNWINDING("start_ip=%p, end_ip=%p\n", + reinterpret_cast(start_ip), + reinterpret_cast(end_ip)); + + // Skip field hand_mask if it exists. + if (TBTable->tb.int_hndl) + ++p; + + unw_word_t lsda = 0; + unw_word_t handler = 0; + unw_word_t flags = frameType::frameWithXLEHStateTable; + + if (TBTable->tb.lang == TB_CPLUSPLUS && TBTable->tb.has_ctl) { + // State table info is available. The ctl_info field indicates the + // number of CTL anchors. There should be only one entry for the C++ + // state table. + assert(*p == 1 && "libunwind: there must be only one ctl_info entry"); + ++p; + // p points to the offset of the state table into the stack. + pint_t stateTableOffset = *p++; + + int framePointerReg; + + // Skip fields name_len and name if exist. + if (TBTable->tb.name_present) { + const uint16_t name_len = *(reinterpret_cast(p)); + p = reinterpret_cast(reinterpret_cast(p) + name_len + + sizeof(uint16_t)); + } + + if (TBTable->tb.uses_alloca) + framePointerReg = *(reinterpret_cast(p)); + else + framePointerReg = 1; // default frame pointer == SP + + _LIBUNWIND_TRACE_UNWINDING( + "framePointerReg=%d, framePointer=%p, " + "stateTableOffset=%#lx\n", + framePointerReg, + reinterpret_cast(_registers.getRegister(framePointerReg)), + stateTableOffset); + lsda = _registers.getRegister(framePointerReg) + stateTableOffset; + + // Since the traceback table generated by the legacy XLC++ does not + // provide the location of the personality for the state table, + // function __xlcxx_personality_v0(), which is the personality for the state + // table and is exported from libc++abi, is directly assigned as the + // handler here. When a legacy XLC++ frame is encountered, the symbol + // is resolved dynamically using dlopen() to avoid hard dependency from + // libunwind on libc++abi. + + // Resolve the function pointer to the state table personality if it has + // not already. + if (xlcPersonalityV0 == NULL) { + xlcPersonalityV0InitLock.lock(); + if (xlcPersonalityV0 == NULL) { + // If libc++abi is statically linked in, symbol __xlcxx_personality_v0 + // has been resolved at the link time. + xlcPersonalityV0 = &__xlcxx_personality_v0; + if (xlcPersonalityV0 == NULL) { + // libc++abi is dynamically linked. Resolve __xlcxx_personality_v0 + // using dlopen(). + const char libcxxabi[] = "libc++abi.a(libc++abi.so.1)"; + void *libHandle; + // The AIX dlopen() sets errno to 0 when it is successful, which + // clobbers the value of errno from the user code. This is an AIX + // bug because according to POSIX it should not set errno to 0. To + // workaround before AIX fixes the bug, errno is saved and restored. + int saveErrno = errno; + libHandle = dlopen(libcxxabi, RTLD_MEMBER | RTLD_NOW); + if (libHandle == NULL) { + _LIBUNWIND_TRACE_UNWINDING("dlopen() failed with errno=%d\n", + errno); + assert(0 && "dlopen() failed"); + } + xlcPersonalityV0 = reinterpret_cast<__xlcxx_personality_v0_t *>( + dlsym(libHandle, "__xlcxx_personality_v0")); + if (xlcPersonalityV0 == NULL) { + _LIBUNWIND_TRACE_UNWINDING("dlsym() failed with errno=%d\n", errno); + assert(0 && "dlsym() failed"); + } + dlclose(libHandle); + errno = saveErrno; + } + } + xlcPersonalityV0InitLock.unlock(); + } + handler = reinterpret_cast(xlcPersonalityV0); + _LIBUNWIND_TRACE_UNWINDING("State table: LSDA=%p, Personality=%p\n", + reinterpret_cast(lsda), + reinterpret_cast(handler)); + } else if (TBTable->tb.longtbtable) { + // This frame has the traceback table extension. Possible cases are + // 1) a C++ frame that has the 'eh_info' structure; 2) a C++ frame that + // is not EH aware; or, 3) a frame of other languages. We need to figure out + // if the traceback table extension contains the 'eh_info' structure. + // + // We also need to deal with the complexity arising from some XL compiler + // versions use the wrong ordering of 'longtbtable' and 'has_vec' bits + // where the 'longtbtable' bit is meant to be the 'has_vec' bit and vice + // versa. For frames of code generated by those compilers, the 'longtbtable' + // bit may be set but there isn't really a traceback table extension. + // + // In , there is the following definition of + // 'struct tbtable_ext'. It is not really a structure but a dummy to + // collect the description of optional parts of the traceback table. + // + // struct tbtable_ext { + // ... + // char alloca_reg; /* Register for alloca automatic storage */ + // struct vec_ext vec_ext; /* Vector extension (if has_vec is set) */ + // unsigned char xtbtable; /* More tbtable fields, if longtbtable is set*/ + // }; + // + // Depending on how the 'has_vec'/'longtbtable' bit is interpreted, the data + // following 'alloca_reg' can be treated either as 'struct vec_ext' or + // 'unsigned char xtbtable'. 'xtbtable' bits are defined in + // as flags. The 7th bit '0x02' is currently + // unused and should not be set. 'struct vec_ext' is defined in + // as follows: + // + // struct vec_ext { + // unsigned vr_saved:6; /* Number of non-volatile vector regs saved + // */ + // /* first register saved is assumed to be */ + // /* 32 - vr_saved */ + // unsigned saves_vrsave:1; /* Set if vrsave is saved on the stack */ + // unsigned has_varargs:1; + // ... + // }; + // + // Here, the 7th bit is used as 'saves_vrsave'. To determine whether it + // is 'struct vec_ext' or 'xtbtable' that follows 'alloca_reg', + // we checks if the 7th bit is set or not because 'xtbtable' should + // never have the 7th bit set. The 7th bit of 'xtbtable' will be reserved + // in the future to make sure the mitigation works. This mitigation + // is not 100% bullet proof because 'struct vec_ext' may not always have + // 'saves_vrsave' bit set. + // + // 'reservedBit' is defined in enum 'xTBTableMask' above as the mask for + // checking the 7th bit. + + // p points to field name len. + uint8_t *charPtr = reinterpret_cast(p); + + // Skip fields name_len and name if they exist. + if (TBTable->tb.name_present) { + const uint16_t name_len = *(reinterpret_cast(charPtr)); + charPtr = charPtr + name_len + sizeof(uint16_t); + } + + // Skip field alloc_reg if it exists. + if (TBTable->tb.uses_alloca) + ++charPtr; + + // Check traceback table bit has_vec. Skip struct vec_ext if it exists. + if (TBTable->tb.has_vec) + // Note struct vec_ext does exist at this point because whether the + // ordering of longtbtable and has_vec bits is correct or not, both + // are set. + charPtr += sizeof(struct vec_ext); + + // charPtr points to field 'xtbtable'. Check if the EH info is available. + // Also check if the reserved bit of the extended traceback table field + // 'xtbtable' is set. If it is, the traceback table was incorrectly + // generated by an XL compiler that uses the wrong ordering of 'longtbtable' + // and 'has_vec' bits and this is in fact 'struct vec_ext'. So skip the + // frame. + if ((*charPtr & xTBTableMask::ehInfoBit) && + !(*charPtr & xTBTableMask::reservedBit)) { + // Mark this frame has the new EH info. + flags = frameType::frameWithEHInfo; + + // eh_info is available. + charPtr++; + // The pointer is 4-byte aligned. + if (reinterpret_cast(charPtr) % 4) + charPtr += 4 - reinterpret_cast(charPtr) % 4; + uintptr_t *ehInfo = + reinterpret_cast(*(reinterpret_cast( + registers.getRegister(2) + + *(reinterpret_cast(charPtr))))); + + // ehInfo points to structure en_info. The first member is version. + // Only version 0 is currently supported. + assert(*(reinterpret_cast(ehInfo)) == 0 && + "libunwind: ehInfo version other than 0 is not supported"); + + // Increment ehInfo to point to member lsda. + ++ehInfo; + lsda = *ehInfo++; + + // enInfo now points to member personality. + handler = *ehInfo; + + _LIBUNWIND_TRACE_UNWINDING("Range table: LSDA=%#lx, Personality=%#lx\n", + lsda, handler); + } + } + + _info.start_ip = start_ip; + _info.end_ip = end_ip; + _info.lsda = lsda; + _info.handler = handler; + _info.gp = 0; + _info.flags = flags; + _info.format = 0; + _info.unwind_info = reinterpret_cast(TBTable); + _info.unwind_info_size = 0; + _info.extra = registers.getRegister(2); + + return true; +} + +// Step back up the stack following the frame back link. +template +int UnwindCursor::stepWithTBTable(pint_t pc, tbtable *TBTable, + R ®isters, bool &isSignalFrame) { + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if (!getFunctionName(functionBuf, sizeof(functionBuf), &offset)) { + functionName = ".anonymous."; + } + _LIBUNWIND_TRACE_UNWINDING("%s: Look up traceback table of func=%s at %p", + __func__, functionName, + reinterpret_cast(TBTable)); + } + +#if defined(__powerpc64__) + // Instruction to reload TOC register "l r2,40(r1)" + const uint32_t loadTOCRegInst = 0xe8410028; + const int32_t unwPPCF0Index = UNW_PPC64_F0; + const int32_t unwPPCV0Index = UNW_PPC64_V0; +#else + // Instruction to reload TOC register "l r2,20(r1)" + const uint32_t loadTOCRegInst = 0x80410014; + const int32_t unwPPCF0Index = UNW_PPC_F0; + const int32_t unwPPCV0Index = UNW_PPC_V0; +#endif + + R newRegisters = registers; + + // lastStack points to the stack frame of the next routine up. + pint_t lastStack = *(reinterpret_cast(registers.getSP())); + + // Return address is the address after call site instruction. + pint_t returnAddress; + + if (isSignalFrame) { + _LIBUNWIND_TRACE_UNWINDING("Possible signal handler frame: lastStack=%p", + reinterpret_cast(lastStack)); + + sigcontext *sigContext = reinterpret_cast( + reinterpret_cast(lastStack) + STKMIN); + returnAddress = sigContext->sc_jmpbuf.jmp_context.iar; + + _LIBUNWIND_TRACE_UNWINDING("From sigContext=%p, returnAddress=%p\n", + reinterpret_cast(sigContext), + reinterpret_cast(returnAddress)); + + if (returnAddress < 0x10000000) { + // Try again using STKMINALIGN + sigContext = reinterpret_cast( + reinterpret_cast(lastStack) + STKMINALIGN); + returnAddress = sigContext->sc_jmpbuf.jmp_context.iar; + if (returnAddress < 0x10000000) { + _LIBUNWIND_TRACE_UNWINDING("Bad returnAddress=%p\n", + reinterpret_cast(returnAddress)); + return UNW_EBADFRAME; + } else { + _LIBUNWIND_TRACE_UNWINDING("Tried again using STKMINALIGN: " + "sigContext=%p, returnAddress=%p. " + "Seems to be a valid address\n", + reinterpret_cast(sigContext), + reinterpret_cast(returnAddress)); + } + } + // Restore the condition register from sigcontext. + newRegisters.setCR(sigContext->sc_jmpbuf.jmp_context.cr); + + // Restore GPRs from sigcontext. + for (int i = 0; i < 32; ++i) + newRegisters.setRegister(i, sigContext->sc_jmpbuf.jmp_context.gpr[i]); + + // Restore FPRs from sigcontext. + for (int i = 0; i < 32; ++i) + newRegisters.setFloatRegister(i + unwPPCF0Index, + sigContext->sc_jmpbuf.jmp_context.fpr[i]); + + // Restore vector registers if there is an associated extended context + // structure. + if (sigContext->sc_jmpbuf.jmp_context.msr & __EXTCTX) { + ucontext_t *uContext = reinterpret_cast(sigContext); + if (uContext->__extctx->__extctx_magic == __EXTCTX_MAGIC) { + for (int i = 0; i < 32; ++i) + newRegisters.setVectorRegister( + i + unwPPCV0Index, *(reinterpret_cast( + &(uContext->__extctx->__vmx.__vr[i])))); + } + } + } else { + // Step up a normal frame. + returnAddress = reinterpret_cast(lastStack)[2]; + + _LIBUNWIND_TRACE_UNWINDING("Extract info from lastStack=%p, " + "returnAddress=%p\n", + reinterpret_cast(lastStack), + reinterpret_cast(returnAddress)); + _LIBUNWIND_TRACE_UNWINDING("fpr_regs=%d, gpr_regs=%d, saves_cr=%d\n", + TBTable->tb.fpr_saved, TBTable->tb.gpr_saved, + TBTable->tb.saves_cr); + + // Restore FP registers. + char *ptrToRegs = reinterpret_cast(lastStack); + double *FPRegs = reinterpret_cast( + ptrToRegs - (TBTable->tb.fpr_saved * sizeof(double))); + for (int i = 0; i < TBTable->tb.fpr_saved; ++i) + newRegisters.setFloatRegister( + 32 - TBTable->tb.fpr_saved + i + unwPPCF0Index, FPRegs[i]); + + // Restore GP registers. + ptrToRegs = reinterpret_cast(FPRegs); + uintptr_t *GPRegs = reinterpret_cast( + ptrToRegs - (TBTable->tb.gpr_saved * sizeof(uintptr_t))); + for (int i = 0; i < TBTable->tb.gpr_saved; ++i) + newRegisters.setRegister(32 - TBTable->tb.gpr_saved + i, GPRegs[i]); + + // Restore Vector registers. + ptrToRegs = reinterpret_cast(GPRegs); + + // Restore vector registers only if this is a Clang frame. Also + // check if traceback table bit has_vec is set. If it is, structure + // vec_ext is available. + if (_info.flags == frameType::frameWithEHInfo && TBTable->tb.has_vec) { + + // Get to the vec_ext structure to check if vector registers are saved. + uint32_t *p = reinterpret_cast(&TBTable->tb_ext); + + // Skip field parminfo if exists. + if (TBTable->tb.fixedparms || TBTable->tb.floatparms) + ++p; + + // Skip field tb_offset if exists. + if (TBTable->tb.has_tboff) + ++p; + + // Skip field hand_mask if exists. + if (TBTable->tb.int_hndl) + ++p; + + // Skip fields ctl_info and ctl_info_disp if exist. + if (TBTable->tb.has_ctl) { + // Skip field ctl_info. + ++p; + // Skip field ctl_info_disp. + ++p; + } + + // Skip fields name_len and name if exist. + // p is supposed to point to field name_len now. + uint8_t *charPtr = reinterpret_cast(p); + if (TBTable->tb.name_present) { + const uint16_t name_len = *(reinterpret_cast(charPtr)); + charPtr = charPtr + name_len + sizeof(uint16_t); + } + + // Skip field alloc_reg if it exists. + if (TBTable->tb.uses_alloca) + ++charPtr; + + struct vec_ext *vec_ext = reinterpret_cast(charPtr); + + _LIBUNWIND_TRACE_UNWINDING("vr_saved=%d\n", vec_ext->vr_saved); + + // Restore vector register(s) if saved on the stack. + if (vec_ext->vr_saved) { + // Saved vector registers are 16-byte aligned. + if (reinterpret_cast(ptrToRegs) % 16) + ptrToRegs -= reinterpret_cast(ptrToRegs) % 16; + v128 *VecRegs = reinterpret_cast(ptrToRegs - vec_ext->vr_saved * + sizeof(v128)); + for (int i = 0; i < vec_ext->vr_saved; ++i) { + newRegisters.setVectorRegister( + 32 - vec_ext->vr_saved + i + unwPPCV0Index, VecRegs[i]); + } + } + } + if (TBTable->tb.saves_cr) { + // Get the saved condition register. The condition register is only + // a single word. + newRegisters.setCR( + *(reinterpret_cast(lastStack + sizeof(uintptr_t)))); + } + + // Restore the SP. + newRegisters.setSP(lastStack); + + // The first instruction after return. + uint32_t firstInstruction = *(reinterpret_cast(returnAddress)); + + // Do we need to set the TOC register? + _LIBUNWIND_TRACE_UNWINDING( + "Current gpr2=%p\n", + reinterpret_cast(newRegisters.getRegister(2))); + if (firstInstruction == loadTOCRegInst) { + _LIBUNWIND_TRACE_UNWINDING( + "Set gpr2=%p from frame\n", + reinterpret_cast(reinterpret_cast(lastStack)[5])); + newRegisters.setRegister(2, reinterpret_cast(lastStack)[5]); + } + } + _LIBUNWIND_TRACE_UNWINDING("lastStack=%p, returnAddress=%p, pc=%p\n", + reinterpret_cast(lastStack), + reinterpret_cast(returnAddress), + reinterpret_cast(pc)); + + // The return address is the address after call site instruction, so + // setting IP to that simulates a return. + newRegisters.setIP(reinterpret_cast(returnAddress)); + + // Simulate the step by replacing the register set with the new ones. + registers = newRegisters; + + // Check if the next frame is a signal frame. + pint_t nextStack = *(reinterpret_cast(registers.getSP())); + + // Return address is the address after call site instruction. + pint_t nextReturnAddress = reinterpret_cast(nextStack)[2]; + + if (nextReturnAddress > 0x01 && nextReturnAddress < 0x10000) { + _LIBUNWIND_TRACE_UNWINDING("The next is a signal handler frame: " + "nextStack=%p, next return address=%p\n", + reinterpret_cast(nextStack), + reinterpret_cast(nextReturnAddress)); + isSignalFrame = true; + } else { + isSignalFrame = false; + } + + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + +template +void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) + _isSigReturn = false; +#endif + + pint_t pc = static_cast(this->getReg(UNW_REG_IP)); +#if defined(_LIBUNWIND_ARM_EHABI) + // Remove the thumb bit so the IP represents the actual instruction address. + // This matches the behaviour of _Unwind_GetIP on arm. + pc &= (pint_t)~0x1; +#endif + + // Exit early if at the top of the stack. + if (pc == 0) { + _unwindInfoMissing = true; + return; + } + + // If the last line of a function is a "throw" the compiler sometimes + // emits no instructions after the call to __cxa_throw. This means + // the return address is actually the start of the next function. + // To disambiguate this, back up the pc when we know it is a return + // address. + if (isReturnAddress) +#if defined(_AIX) + // PC needs to be a 4-byte aligned address to be able to look for a + // word of 0 that indicates the start of the traceback table at the end + // of a function on AIX. + pc -= 4; +#else + --pc; +#endif + + // Ask address space object to find unwind sections for this pc. + UnwindInfoSections sects; + if (_addressSpace.findUnwindSections(pc, sects)) { +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + // If there is a compact unwind encoding table, look there first. + if (sects.compact_unwind_section != 0) { + if (this->getInfoFromCompactEncodingSection(pc, sects)) { + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + // Found info in table, done unless encoding says to use dwarf. + uint32_t dwarfOffset; + if ((sects.dwarf_section != 0) && compactSaysUseDwarf(&dwarfOffset)) { + if (this->getInfoFromDwarfSection(pc, sects, dwarfOffset)) { + // found info in dwarf, done + return; + } + } + #endif + // If unwind table has entry, but entry says there is no unwind info, + // record that we have no unwind info. + if (_info.format == 0) + _unwindInfoMissing = true; + return; + } + } +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + // If there is SEH unwind info, look there next. + if (this->getInfoFromSEH(pc)) + return; +#endif + +#if defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + // If there is unwind info in the traceback table, look there next. + if (this->getInfoFromTBTable(pc, _registers)) + return; +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + // If there is dwarf unwind info, look there next. + if (sects.dwarf_section != 0) { + if (this->getInfoFromDwarfSection(pc, sects)) { + // found info in dwarf, done + return; + } + } +#endif + +#if defined(_LIBUNWIND_ARM_EHABI) + // If there is ARM EHABI unwind info, look there next. + if (sects.arm_section != 0 && this->getInfoFromEHABISection(pc, sects)) + return; +#endif + } + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + // There is no static unwind info for this pc. Look to see if an FDE was + // dynamically registered for it. + pint_t cachedFDE = DwarfFDECache::findFDE(DwarfFDECache::kSearchAll, + pc); + if (cachedFDE != 0) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + if (!CFI_Parser::decodeFDE(_addressSpace, cachedFDE, &fdeInfo, &cieInfo)) + if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, 0)) + return; + } + + // Lastly, ask AddressSpace object about platform specific ways to locate + // other FDEs. + pint_t fde; + if (_addressSpace.findOtherFDE(pc, fde)) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + if (!CFI_Parser::decodeFDE(_addressSpace, fde, &fdeInfo, &cieInfo)) { + // Double check this FDE is for a function that includes the pc. + if ((fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd)) + if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, 0)) + return; + } + } +#endif // #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) + if (setInfoForSigReturn()) + return; +#endif + + // no unwind info, flag that we can't reliably unwind + _unwindInfoMissing = true; +} + +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ + defined(_LIBUNWIND_TARGET_AARCH64) +template +bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { + // Look for the sigreturn trampoline. The trampoline's body is two + // specific instructions (see below). Typically the trampoline comes from the + // vDSO[1] (i.e. the __kernel_rt_sigreturn function). A libc might provide its + // own restorer function, though, or user-mode QEMU might write a trampoline + // onto the stack. + // + // This special code path is a fallback that is only used if the trampoline + // lacks proper (e.g. DWARF) unwind info. On AArch64, a new DWARF register + // constant for the PC needs to be defined before DWARF can handle a signal + // trampoline. This code may segfault if the target PC is unreadable, e.g.: + // - The PC points at a function compiled without unwind info, and which is + // part of an execute-only mapping (e.g. using -Wl,--execute-only). + // - The PC is invalid and happens to point to unreadable or unmapped memory. + // + // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S + const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + // The PC might contain an invalid address if the unwind info is bad, so + // directly accessing it could cause a segfault. Use process_vm_readv to read + // the memory safely instead. process_vm_readv was added in Linux 3.2, and + // AArch64 supported was added in Linux 3.7, so the syscall is guaranteed to + // be present. Unfortunately, there are Linux AArch64 environments where the + // libc wrapper for the syscall might not be present (e.g. Android 5), so call + // the syscall directly instead. + uint32_t instructions[2]; + struct iovec local_iov = {&instructions, sizeof instructions}; + struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; + long bytesRead = + syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0); + // Look for instructions: mov x8, #0x8b; svc #0x0 + if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 || + instructions[1] != 0xd4000001) + return false; + + _info = {}; + _info.start_ip = pc; + _info.end_ip = pc + 4; + _isSigReturn = true; + return true; +} + +template +int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { + // In the signal trampoline frame, sp points to an rt_sigframe[1], which is: + // - 128-byte siginfo struct + // - ucontext struct: + // - 8-byte long (uc_flags) + // - 8-byte pointer (uc_link) + // - 24-byte stack_t + // - 128-byte signal set + // - 8 bytes of padding because sigcontext has 16-byte alignment + // - sigcontext/mcontext_t + // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/signal.c + const pint_t kOffsetSpToSigcontext = (128 + 8 + 8 + 24 + 128 + 8); // 304 + + // Offsets from sigcontext to each register. + const pint_t kOffsetGprs = 8; // offset to "__u64 regs[31]" field + const pint_t kOffsetSp = 256; // offset to "__u64 sp" field + const pint_t kOffsetPc = 264; // offset to "__u64 pc" field + + pint_t sigctx = _registers.getSP() + kOffsetSpToSigcontext; + + for (int i = 0; i <= 30; ++i) { + uint64_t value = _addressSpace.get64(sigctx + kOffsetGprs + + static_cast(i * 8)); + _registers.setRegister(UNW_AARCH64_X0 + i, value); + } + _registers.setSP(_addressSpace.get64(sigctx + kOffsetSp)); + _registers.setIP(_addressSpace.get64(sigctx + kOffsetPc)); + _isSignalFrame = true; + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && + // defined(_LIBUNWIND_TARGET_AARCH64) + +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ + defined(_LIBUNWIND_TARGET_RISCV) +template +bool UnwindCursor::setInfoForSigReturn(Registers_riscv &) { + const pint_t pc = static_cast(getReg(UNW_REG_IP)); + uint32_t instructions[2]; + struct iovec local_iov = {&instructions, sizeof instructions}; + struct iovec remote_iov = {reinterpret_cast(pc), sizeof instructions}; + long bytesRead = + syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0); + // Look for the two instructions used in the sigreturn trampoline + // __vdso_rt_sigreturn: + // + // 0x08b00893 li a7,0x8b + // 0x00000073 ecall + if (bytesRead != sizeof instructions || instructions[0] != 0x08b00893 || + instructions[1] != 0x00000073) + return false; + + _info = {}; + _info.start_ip = pc; + _info.end_ip = pc + 4; + _isSigReturn = true; + return true; +} + +template +int UnwindCursor::stepThroughSigReturn(Registers_riscv &) { + // In the signal trampoline frame, sp points to an rt_sigframe[1], which is: + // - 128-byte siginfo struct + // - ucontext_t struct: + // - 8-byte long (__uc_flags) + // - 8-byte pointer (*uc_link) + // - 24-byte uc_stack + // - 8-byte uc_sigmask + // - 120-byte of padding to allow sigset_t to be expanded in the future + // - 8 bytes of padding because sigcontext has 16-byte alignment + // - struct sigcontext uc_mcontext + // [1] + // https://github.com/torvalds/linux/blob/master/arch/riscv/kernel/signal.c + const pint_t kOffsetSpToSigcontext = 128 + 8 + 8 + 24 + 8 + 128; + + const pint_t sigctx = _registers.getSP() + kOffsetSpToSigcontext; + _registers.setIP(_addressSpace.get64(sigctx)); + for (int i = UNW_RISCV_X1; i <= UNW_RISCV_X31; ++i) { + uint64_t value = _addressSpace.get64(sigctx + static_cast(i * 8)); + _registers.setRegister(i, value); + } + _isSignalFrame = true; + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && + // defined(_LIBUNWIND_TARGET_RISCV) + +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && \ + defined(_LIBUNWIND_TARGET_S390X) +template +bool UnwindCursor::setInfoForSigReturn(Registers_s390x &) { + // Look for the sigreturn trampoline. The trampoline's body is a + // specific instruction (see below). Typically the trampoline comes from the + // vDSO (i.e. the __kernel_[rt_]sigreturn function). A libc might provide its + // own restorer function, though, or user-mode QEMU might write a trampoline + // onto the stack. + const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + // The PC might contain an invalid address if the unwind info is bad, so + // directly accessing it could cause a segfault. Use process_vm_readv to + // read the memory safely instead. + uint16_t inst; + struct iovec local_iov = {&inst, sizeof inst}; + struct iovec remote_iov = {reinterpret_cast(pc), sizeof inst}; + long bytesRead = process_vm_readv(getpid(), &local_iov, 1, &remote_iov, 1, 0); + if (bytesRead == sizeof inst && (inst == 0x0a77 || inst == 0x0aad)) { + _info = {}; + _info.start_ip = pc; + _info.end_ip = pc + 2; + _isSigReturn = true; + return true; + } + return false; +} + +template +int UnwindCursor::stepThroughSigReturn(Registers_s390x &) { + // Determine current SP. + const pint_t sp = static_cast(this->getReg(UNW_REG_SP)); + // According to the s390x ABI, the CFA is at (incoming) SP + 160. + const pint_t cfa = sp + 160; + + // Determine current PC and instruction there (this must be either + // a "svc __NR_sigreturn" or "svc __NR_rt_sigreturn"). + const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + const uint16_t inst = _addressSpace.get16(pc); + + // Find the addresses of the signo and sigcontext in the frame. + pint_t pSigctx = 0; + pint_t pSigno = 0; + + // "svc __NR_sigreturn" uses a non-RT signal trampoline frame. + if (inst == 0x0a77) { + // Layout of a non-RT signal trampoline frame, starting at the CFA: + // - 8-byte signal mask + // - 8-byte pointer to sigcontext, followed by signo + // - 4-byte signo + pSigctx = _addressSpace.get64(cfa + 8); + pSigno = pSigctx + 344; + } + + // "svc __NR_rt_sigreturn" uses a RT signal trampoline frame. + if (inst == 0x0aad) { + // Layout of a RT signal trampoline frame, starting at the CFA: + // - 8-byte retcode (+ alignment) + // - 128-byte siginfo struct (starts with signo) + // - ucontext struct: + // - 8-byte long (uc_flags) + // - 8-byte pointer (uc_link) + // - 24-byte stack_t + // - 8 bytes of padding because sigcontext has 16-byte alignment + // - sigcontext/mcontext_t + pSigctx = cfa + 8 + 128 + 8 + 8 + 24 + 8; + pSigno = cfa + 8; + } + + assert(pSigctx != 0); + assert(pSigno != 0); + + // Offsets from sigcontext to each register. + const pint_t kOffsetPc = 8; + const pint_t kOffsetGprs = 16; + const pint_t kOffsetFprs = 216; + + // Restore all registers. + for (int i = 0; i < 16; ++i) { + uint64_t value = _addressSpace.get64(pSigctx + kOffsetGprs + + static_cast(i * 8)); + _registers.setRegister(UNW_S390X_R0 + i, value); + } + for (int i = 0; i < 16; ++i) { + static const int fpr[16] = { + UNW_S390X_F0, UNW_S390X_F1, UNW_S390X_F2, UNW_S390X_F3, + UNW_S390X_F4, UNW_S390X_F5, UNW_S390X_F6, UNW_S390X_F7, + UNW_S390X_F8, UNW_S390X_F9, UNW_S390X_F10, UNW_S390X_F11, + UNW_S390X_F12, UNW_S390X_F13, UNW_S390X_F14, UNW_S390X_F15 + }; + double value = _addressSpace.getDouble(pSigctx + kOffsetFprs + + static_cast(i * 8)); + _registers.setFloatRegister(fpr[i], value); + } + _registers.setIP(_addressSpace.get64(pSigctx + kOffsetPc)); + + // SIGILL, SIGFPE and SIGTRAP are delivered with psw_addr + // after the faulting instruction rather than before it. + // Do not set _isSignalFrame in that case. + uint32_t signo = _addressSpace.get32(pSigno); + _isSignalFrame = (signo != 4 && signo != 5 && signo != 8); + + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) && + // defined(_LIBUNWIND_TARGET_S390X) + +template int UnwindCursor::step(bool stage2) { + (void)stage2; + // Bottom of stack is defined is when unwind info cannot be found. + if (_unwindInfoMissing) + return UNW_STEP_END; + + // Use unwinding info to modify register set as if function returned. + int result; +#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN) + if (_isSigReturn) { + result = this->stepThroughSigReturn(); + } else +#endif + { +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + result = this->stepWithCompactEncoding(stage2); +#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + result = this->stepWithSEHData(); +#elif defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + result = this->stepWithTBTableData(); +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + result = this->stepWithDwarfFDE(stage2); +#elif defined(_LIBUNWIND_ARM_EHABI) + result = this->stepWithEHABI(); +#else + #error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or \ + _LIBUNWIND_SUPPORT_SEH_UNWIND or \ + _LIBUNWIND_SUPPORT_DWARF_UNWIND or \ + _LIBUNWIND_ARM_EHABI +#endif + } + + // update info based on new PC + if (result == UNW_STEP_SUCCESS) { + this->setInfoBasedOnIPRegister(true); + if (_unwindInfoMissing) + return UNW_STEP_END; + } + + return result; +} + +template +void UnwindCursor::getInfo(unw_proc_info_t *info) { + if (_unwindInfoMissing) + memset(info, 0, sizeof(*info)); + else + *info = _info; +} + +template +bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, + unw_word_t *offset) { + return _addressSpace.findFunctionName((pint_t)this->getReg(UNW_REG_IP), + buf, bufLen, offset); +} + +#if defined(_LIBUNWIND_USE_CET) +extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) { + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->get_registers(); +} +#endif +} // namespace libunwind + +#endif // __UNWINDCURSOR_HPP__ diff --git a/third_party/libunwind/UnwindLevel1-gcc-ext.c b/third_party/libunwind/UnwindLevel1-gcc-ext.c new file mode 100644 index 000000000..98ab95842 --- /dev/null +++ b/third_party/libunwind/UnwindLevel1-gcc-ext.c @@ -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 +#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) diff --git a/third_party/libunwind/UnwindLevel1.c b/third_party/libunwind/UnwindLevel1.c new file mode 100644 index 000000000..e59c10432 --- /dev/null +++ b/third_party/libunwind/UnwindLevel1.c @@ -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 ), 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__) diff --git a/third_party/libunwind/cet_unwind.h b/third_party/libunwind/cet_unwind.h new file mode 100644 index 000000000..75d24a040 --- /dev/null +++ b/third_party/libunwind/cet_unwind.h @@ -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 +#include + +#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 diff --git a/third_party/libunwind/config.h b/third_party/libunwind/config.h new file mode 100644 index 000000000..ecf375bdf --- /dev/null +++ b/third_party/libunwind/config.h @@ -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 +struct check_fit { + template + 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 diff --git a/third_party/libunwind/dwarf2.h b/third_party/libunwind/dwarf2.h new file mode 100644 index 000000000..174277d5a --- /dev/null +++ b/third_party/libunwind/dwarf2.h @@ -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 diff --git a/third_party/libunwind/include/__libunwind_config.h b/third_party/libunwind/include/__libunwind_config.h new file mode 100644 index 000000000..a317a3667 --- /dev/null +++ b/third_party/libunwind/include/__libunwind_config.h @@ -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__ diff --git a/third_party/libunwind/include/libunwind.h b/third_party/libunwind/include/libunwind.h new file mode 100644 index 000000000..27c65a022 --- /dev/null +++ b/third_party/libunwind/include/libunwind.h @@ -0,0 +1,1300 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +// +// Compatible with libunwind API documented at: +// http://www.nongnu.org/libunwind/man/libunwind(3).html +// +//===----------------------------------------------------------------------===// + +#ifndef __LIBUNWIND__ +#define __LIBUNWIND__ + +#include "third_party/libunwind/include/__libunwind_config.h" + +#include "libc/isystem/stdint.h" +#include "libc/isystem/stddef.h" + +#ifdef __APPLE__ + #if __clang__ + #if __has_include() + #include + #endif + #elif __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + #include + #endif + + #ifdef __arm__ + #define LIBUNWIND_AVAIL __attribute__((unavailable)) + #elif defined(__OSX_AVAILABLE_STARTING) + #define LIBUNWIND_AVAIL __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_5_0) + #else + #include + #ifdef AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #define LIBUNWIND_AVAIL AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #else + #define LIBUNWIND_AVAIL __attribute__((unavailable)) + #endif + #endif +#else + #define LIBUNWIND_AVAIL +#endif + +#if defined(_WIN32) && defined(__SEH__) + #define LIBUNWIND_CURSOR_ALIGNMENT_ATTR __attribute__((__aligned__(16))) +#else + #define LIBUNWIND_CURSOR_ALIGNMENT_ATTR +#endif + +/* error codes */ +enum { + UNW_ESUCCESS = 0, /* no error */ + UNW_EUNSPEC = -6540, /* unspecified (general) error */ + UNW_ENOMEM = -6541, /* out of memory */ + UNW_EBADREG = -6542, /* bad register number */ + UNW_EREADONLYREG = -6543, /* attempt to write read-only register */ + UNW_ESTOPUNWIND = -6544, /* stop unwinding */ + UNW_EINVALIDIP = -6545, /* invalid IP */ + UNW_EBADFRAME = -6546, /* bad frame */ + UNW_EINVAL = -6547, /* unsupported operation or bad value */ + UNW_EBADVERSION = -6548, /* unwind info has unsupported version */ + UNW_ENOINFO = -6549 /* no unwind info found */ +#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY) + , UNW_ECROSSRASIGNING = -6550 /* cross unwind with return address signing */ +#endif +}; + +struct unw_context_t { + uint64_t data[_LIBUNWIND_CONTEXT_SIZE]; +}; +typedef struct unw_context_t unw_context_t; + +struct unw_cursor_t { + uint64_t data[_LIBUNWIND_CURSOR_SIZE]; +} LIBUNWIND_CURSOR_ALIGNMENT_ATTR; +typedef struct unw_cursor_t unw_cursor_t; + +typedef struct unw_addr_space *unw_addr_space_t; + +typedef int unw_regnum_t; +typedef uintptr_t unw_word_t; +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) && !defined(__SEH__) +typedef uint64_t unw_fpreg_t; +#else +typedef double unw_fpreg_t; +#endif + +struct unw_proc_info_t { + unw_word_t start_ip; /* start address of function */ + unw_word_t end_ip; /* address after end of function */ + unw_word_t lsda; /* address of language specific data area, */ + /* or zero if not used */ + unw_word_t handler; /* personality routine, or zero if not used */ + unw_word_t gp; /* not used */ + unw_word_t flags; /* not used */ + uint32_t format; /* compact unwind encoding, or zero if none */ + uint32_t unwind_info_size; /* size of DWARF unwind info, or zero if none */ + unw_word_t unwind_info; /* address of DWARF unwind info, or zero */ + unw_word_t extra; /* mach_header of mach-o image containing func */ +}; +typedef struct unw_proc_info_t unw_proc_info_t; + +#ifdef __cplusplus +extern "C" { +#endif + +extern int unw_getcontext(unw_context_t *) LIBUNWIND_AVAIL; +extern int unw_init_local(unw_cursor_t *, unw_context_t *) LIBUNWIND_AVAIL; +extern int unw_step(unw_cursor_t *) LIBUNWIND_AVAIL; +extern int unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *) LIBUNWIND_AVAIL; +extern int unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *) LIBUNWIND_AVAIL; +extern int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t) LIBUNWIND_AVAIL; +extern int unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t) LIBUNWIND_AVAIL; +extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL; + +#ifdef __arm__ +/* Save VFP registers in FSTMX format (instead of FSTMD). */ +extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL; +#endif + +#ifdef _AIX +extern uintptr_t unw_get_data_rel_base(unw_cursor_t *) LIBUNWIND_AVAIL; +#endif + +extern const char *unw_regname(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; +extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *) LIBUNWIND_AVAIL; +extern int unw_is_fpreg(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; +extern int unw_is_signal_frame(unw_cursor_t *) LIBUNWIND_AVAIL; +extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *) LIBUNWIND_AVAIL; +//extern int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*); + +extern unw_addr_space_t unw_local_addr_space; + +#ifdef __cplusplus +} +#endif + +// architecture independent register numbers +enum { + UNW_REG_IP = -1, // instruction pointer + UNW_REG_SP = -2, // stack pointer +}; + +// 32-bit x86 registers +enum { + UNW_X86_EAX = 0, + UNW_X86_ECX = 1, + UNW_X86_EDX = 2, + UNW_X86_EBX = 3, + UNW_X86_EBP = 4, + UNW_X86_ESP = 5, + UNW_X86_ESI = 6, + UNW_X86_EDI = 7 +}; + +// 64-bit x86_64 registers +enum { + UNW_X86_64_RAX = 0, + UNW_X86_64_RDX = 1, + UNW_X86_64_RCX = 2, + UNW_X86_64_RBX = 3, + UNW_X86_64_RSI = 4, + UNW_X86_64_RDI = 5, + UNW_X86_64_RBP = 6, + UNW_X86_64_RSP = 7, + UNW_X86_64_R8 = 8, + UNW_X86_64_R9 = 9, + UNW_X86_64_R10 = 10, + UNW_X86_64_R11 = 11, + UNW_X86_64_R12 = 12, + UNW_X86_64_R13 = 13, + UNW_X86_64_R14 = 14, + UNW_X86_64_R15 = 15, + UNW_X86_64_RIP = 16, + UNW_X86_64_XMM0 = 17, + UNW_X86_64_XMM1 = 18, + UNW_X86_64_XMM2 = 19, + UNW_X86_64_XMM3 = 20, + UNW_X86_64_XMM4 = 21, + UNW_X86_64_XMM5 = 22, + UNW_X86_64_XMM6 = 23, + UNW_X86_64_XMM7 = 24, + UNW_X86_64_XMM8 = 25, + UNW_X86_64_XMM9 = 26, + UNW_X86_64_XMM10 = 27, + UNW_X86_64_XMM11 = 28, + UNW_X86_64_XMM12 = 29, + UNW_X86_64_XMM13 = 30, + UNW_X86_64_XMM14 = 31, + UNW_X86_64_XMM15 = 32, +}; + + +// 32-bit ppc register numbers +enum { + UNW_PPC_R0 = 0, + UNW_PPC_R1 = 1, + UNW_PPC_R2 = 2, + UNW_PPC_R3 = 3, + UNW_PPC_R4 = 4, + UNW_PPC_R5 = 5, + UNW_PPC_R6 = 6, + UNW_PPC_R7 = 7, + UNW_PPC_R8 = 8, + UNW_PPC_R9 = 9, + UNW_PPC_R10 = 10, + UNW_PPC_R11 = 11, + UNW_PPC_R12 = 12, + UNW_PPC_R13 = 13, + UNW_PPC_R14 = 14, + UNW_PPC_R15 = 15, + UNW_PPC_R16 = 16, + UNW_PPC_R17 = 17, + UNW_PPC_R18 = 18, + UNW_PPC_R19 = 19, + UNW_PPC_R20 = 20, + UNW_PPC_R21 = 21, + UNW_PPC_R22 = 22, + UNW_PPC_R23 = 23, + UNW_PPC_R24 = 24, + UNW_PPC_R25 = 25, + UNW_PPC_R26 = 26, + UNW_PPC_R27 = 27, + UNW_PPC_R28 = 28, + UNW_PPC_R29 = 29, + UNW_PPC_R30 = 30, + UNW_PPC_R31 = 31, + UNW_PPC_F0 = 32, + UNW_PPC_F1 = 33, + UNW_PPC_F2 = 34, + UNW_PPC_F3 = 35, + UNW_PPC_F4 = 36, + UNW_PPC_F5 = 37, + UNW_PPC_F6 = 38, + UNW_PPC_F7 = 39, + UNW_PPC_F8 = 40, + UNW_PPC_F9 = 41, + UNW_PPC_F10 = 42, + UNW_PPC_F11 = 43, + UNW_PPC_F12 = 44, + UNW_PPC_F13 = 45, + UNW_PPC_F14 = 46, + UNW_PPC_F15 = 47, + UNW_PPC_F16 = 48, + UNW_PPC_F17 = 49, + UNW_PPC_F18 = 50, + UNW_PPC_F19 = 51, + UNW_PPC_F20 = 52, + UNW_PPC_F21 = 53, + UNW_PPC_F22 = 54, + UNW_PPC_F23 = 55, + UNW_PPC_F24 = 56, + UNW_PPC_F25 = 57, + UNW_PPC_F26 = 58, + UNW_PPC_F27 = 59, + UNW_PPC_F28 = 60, + UNW_PPC_F29 = 61, + UNW_PPC_F30 = 62, + UNW_PPC_F31 = 63, + UNW_PPC_MQ = 64, + UNW_PPC_LR = 65, + UNW_PPC_CTR = 66, + UNW_PPC_AP = 67, + UNW_PPC_CR0 = 68, + UNW_PPC_CR1 = 69, + UNW_PPC_CR2 = 70, + UNW_PPC_CR3 = 71, + UNW_PPC_CR4 = 72, + UNW_PPC_CR5 = 73, + UNW_PPC_CR6 = 74, + UNW_PPC_CR7 = 75, + UNW_PPC_XER = 76, + UNW_PPC_V0 = 77, + UNW_PPC_V1 = 78, + UNW_PPC_V2 = 79, + UNW_PPC_V3 = 80, + UNW_PPC_V4 = 81, + UNW_PPC_V5 = 82, + UNW_PPC_V6 = 83, + UNW_PPC_V7 = 84, + UNW_PPC_V8 = 85, + UNW_PPC_V9 = 86, + UNW_PPC_V10 = 87, + UNW_PPC_V11 = 88, + UNW_PPC_V12 = 89, + UNW_PPC_V13 = 90, + UNW_PPC_V14 = 91, + UNW_PPC_V15 = 92, + UNW_PPC_V16 = 93, + UNW_PPC_V17 = 94, + UNW_PPC_V18 = 95, + UNW_PPC_V19 = 96, + UNW_PPC_V20 = 97, + UNW_PPC_V21 = 98, + UNW_PPC_V22 = 99, + UNW_PPC_V23 = 100, + UNW_PPC_V24 = 101, + UNW_PPC_V25 = 102, + UNW_PPC_V26 = 103, + UNW_PPC_V27 = 104, + UNW_PPC_V28 = 105, + UNW_PPC_V29 = 106, + UNW_PPC_V30 = 107, + UNW_PPC_V31 = 108, + UNW_PPC_VRSAVE = 109, + UNW_PPC_VSCR = 110, + UNW_PPC_SPE_ACC = 111, + UNW_PPC_SPEFSCR = 112 +}; + +// 64-bit ppc register numbers +enum { + UNW_PPC64_R0 = 0, + UNW_PPC64_R1 = 1, + UNW_PPC64_R2 = 2, + UNW_PPC64_R3 = 3, + UNW_PPC64_R4 = 4, + UNW_PPC64_R5 = 5, + UNW_PPC64_R6 = 6, + UNW_PPC64_R7 = 7, + UNW_PPC64_R8 = 8, + UNW_PPC64_R9 = 9, + UNW_PPC64_R10 = 10, + UNW_PPC64_R11 = 11, + UNW_PPC64_R12 = 12, + UNW_PPC64_R13 = 13, + UNW_PPC64_R14 = 14, + UNW_PPC64_R15 = 15, + UNW_PPC64_R16 = 16, + UNW_PPC64_R17 = 17, + UNW_PPC64_R18 = 18, + UNW_PPC64_R19 = 19, + UNW_PPC64_R20 = 20, + UNW_PPC64_R21 = 21, + UNW_PPC64_R22 = 22, + UNW_PPC64_R23 = 23, + UNW_PPC64_R24 = 24, + UNW_PPC64_R25 = 25, + UNW_PPC64_R26 = 26, + UNW_PPC64_R27 = 27, + UNW_PPC64_R28 = 28, + UNW_PPC64_R29 = 29, + UNW_PPC64_R30 = 30, + UNW_PPC64_R31 = 31, + UNW_PPC64_F0 = 32, + UNW_PPC64_F1 = 33, + UNW_PPC64_F2 = 34, + UNW_PPC64_F3 = 35, + UNW_PPC64_F4 = 36, + UNW_PPC64_F5 = 37, + UNW_PPC64_F6 = 38, + UNW_PPC64_F7 = 39, + UNW_PPC64_F8 = 40, + UNW_PPC64_F9 = 41, + UNW_PPC64_F10 = 42, + UNW_PPC64_F11 = 43, + UNW_PPC64_F12 = 44, + UNW_PPC64_F13 = 45, + UNW_PPC64_F14 = 46, + UNW_PPC64_F15 = 47, + UNW_PPC64_F16 = 48, + UNW_PPC64_F17 = 49, + UNW_PPC64_F18 = 50, + UNW_PPC64_F19 = 51, + UNW_PPC64_F20 = 52, + UNW_PPC64_F21 = 53, + UNW_PPC64_F22 = 54, + UNW_PPC64_F23 = 55, + UNW_PPC64_F24 = 56, + UNW_PPC64_F25 = 57, + UNW_PPC64_F26 = 58, + UNW_PPC64_F27 = 59, + UNW_PPC64_F28 = 60, + UNW_PPC64_F29 = 61, + UNW_PPC64_F30 = 62, + UNW_PPC64_F31 = 63, + // 64: reserved + UNW_PPC64_LR = 65, + UNW_PPC64_CTR = 66, + // 67: reserved + UNW_PPC64_CR0 = 68, + UNW_PPC64_CR1 = 69, + UNW_PPC64_CR2 = 70, + UNW_PPC64_CR3 = 71, + UNW_PPC64_CR4 = 72, + UNW_PPC64_CR5 = 73, + UNW_PPC64_CR6 = 74, + UNW_PPC64_CR7 = 75, + UNW_PPC64_XER = 76, + UNW_PPC64_V0 = 77, + UNW_PPC64_V1 = 78, + UNW_PPC64_V2 = 79, + UNW_PPC64_V3 = 80, + UNW_PPC64_V4 = 81, + UNW_PPC64_V5 = 82, + UNW_PPC64_V6 = 83, + UNW_PPC64_V7 = 84, + UNW_PPC64_V8 = 85, + UNW_PPC64_V9 = 86, + UNW_PPC64_V10 = 87, + UNW_PPC64_V11 = 88, + UNW_PPC64_V12 = 89, + UNW_PPC64_V13 = 90, + UNW_PPC64_V14 = 91, + UNW_PPC64_V15 = 92, + UNW_PPC64_V16 = 93, + UNW_PPC64_V17 = 94, + UNW_PPC64_V18 = 95, + UNW_PPC64_V19 = 96, + UNW_PPC64_V20 = 97, + UNW_PPC64_V21 = 98, + UNW_PPC64_V22 = 99, + UNW_PPC64_V23 = 100, + UNW_PPC64_V24 = 101, + UNW_PPC64_V25 = 102, + UNW_PPC64_V26 = 103, + UNW_PPC64_V27 = 104, + UNW_PPC64_V28 = 105, + UNW_PPC64_V29 = 106, + UNW_PPC64_V30 = 107, + UNW_PPC64_V31 = 108, + // 109, 111-113: OpenPOWER ELF V2 ABI: reserved + // Borrowing VRSAVE number from PPC32. + UNW_PPC64_VRSAVE = 109, + UNW_PPC64_VSCR = 110, + UNW_PPC64_TFHAR = 114, + UNW_PPC64_TFIAR = 115, + UNW_PPC64_TEXASR = 116, + UNW_PPC64_VS0 = UNW_PPC64_F0, + UNW_PPC64_VS1 = UNW_PPC64_F1, + UNW_PPC64_VS2 = UNW_PPC64_F2, + UNW_PPC64_VS3 = UNW_PPC64_F3, + UNW_PPC64_VS4 = UNW_PPC64_F4, + UNW_PPC64_VS5 = UNW_PPC64_F5, + UNW_PPC64_VS6 = UNW_PPC64_F6, + UNW_PPC64_VS7 = UNW_PPC64_F7, + UNW_PPC64_VS8 = UNW_PPC64_F8, + UNW_PPC64_VS9 = UNW_PPC64_F9, + UNW_PPC64_VS10 = UNW_PPC64_F10, + UNW_PPC64_VS11 = UNW_PPC64_F11, + UNW_PPC64_VS12 = UNW_PPC64_F12, + UNW_PPC64_VS13 = UNW_PPC64_F13, + UNW_PPC64_VS14 = UNW_PPC64_F14, + UNW_PPC64_VS15 = UNW_PPC64_F15, + UNW_PPC64_VS16 = UNW_PPC64_F16, + UNW_PPC64_VS17 = UNW_PPC64_F17, + UNW_PPC64_VS18 = UNW_PPC64_F18, + UNW_PPC64_VS19 = UNW_PPC64_F19, + UNW_PPC64_VS20 = UNW_PPC64_F20, + UNW_PPC64_VS21 = UNW_PPC64_F21, + UNW_PPC64_VS22 = UNW_PPC64_F22, + UNW_PPC64_VS23 = UNW_PPC64_F23, + UNW_PPC64_VS24 = UNW_PPC64_F24, + UNW_PPC64_VS25 = UNW_PPC64_F25, + UNW_PPC64_VS26 = UNW_PPC64_F26, + UNW_PPC64_VS27 = UNW_PPC64_F27, + UNW_PPC64_VS28 = UNW_PPC64_F28, + UNW_PPC64_VS29 = UNW_PPC64_F29, + UNW_PPC64_VS30 = UNW_PPC64_F30, + UNW_PPC64_VS31 = UNW_PPC64_F31, + UNW_PPC64_VS32 = UNW_PPC64_V0, + UNW_PPC64_VS33 = UNW_PPC64_V1, + UNW_PPC64_VS34 = UNW_PPC64_V2, + UNW_PPC64_VS35 = UNW_PPC64_V3, + UNW_PPC64_VS36 = UNW_PPC64_V4, + UNW_PPC64_VS37 = UNW_PPC64_V5, + UNW_PPC64_VS38 = UNW_PPC64_V6, + UNW_PPC64_VS39 = UNW_PPC64_V7, + UNW_PPC64_VS40 = UNW_PPC64_V8, + UNW_PPC64_VS41 = UNW_PPC64_V9, + UNW_PPC64_VS42 = UNW_PPC64_V10, + UNW_PPC64_VS43 = UNW_PPC64_V11, + UNW_PPC64_VS44 = UNW_PPC64_V12, + UNW_PPC64_VS45 = UNW_PPC64_V13, + UNW_PPC64_VS46 = UNW_PPC64_V14, + UNW_PPC64_VS47 = UNW_PPC64_V15, + UNW_PPC64_VS48 = UNW_PPC64_V16, + UNW_PPC64_VS49 = UNW_PPC64_V17, + UNW_PPC64_VS50 = UNW_PPC64_V18, + UNW_PPC64_VS51 = UNW_PPC64_V19, + UNW_PPC64_VS52 = UNW_PPC64_V20, + UNW_PPC64_VS53 = UNW_PPC64_V21, + UNW_PPC64_VS54 = UNW_PPC64_V22, + UNW_PPC64_VS55 = UNW_PPC64_V23, + UNW_PPC64_VS56 = UNW_PPC64_V24, + UNW_PPC64_VS57 = UNW_PPC64_V25, + UNW_PPC64_VS58 = UNW_PPC64_V26, + UNW_PPC64_VS59 = UNW_PPC64_V27, + UNW_PPC64_VS60 = UNW_PPC64_V28, + UNW_PPC64_VS61 = UNW_PPC64_V29, + UNW_PPC64_VS62 = UNW_PPC64_V30, + UNW_PPC64_VS63 = UNW_PPC64_V31 +}; + +// 64-bit ARM64 registers +enum { + UNW_AARCH64_X0 = 0, + UNW_AARCH64_X1 = 1, + UNW_AARCH64_X2 = 2, + UNW_AARCH64_X3 = 3, + UNW_AARCH64_X4 = 4, + UNW_AARCH64_X5 = 5, + UNW_AARCH64_X6 = 6, + UNW_AARCH64_X7 = 7, + UNW_AARCH64_X8 = 8, + UNW_AARCH64_X9 = 9, + UNW_AARCH64_X10 = 10, + UNW_AARCH64_X11 = 11, + UNW_AARCH64_X12 = 12, + UNW_AARCH64_X13 = 13, + UNW_AARCH64_X14 = 14, + UNW_AARCH64_X15 = 15, + UNW_AARCH64_X16 = 16, + UNW_AARCH64_X17 = 17, + UNW_AARCH64_X18 = 18, + UNW_AARCH64_X19 = 19, + UNW_AARCH64_X20 = 20, + UNW_AARCH64_X21 = 21, + UNW_AARCH64_X22 = 22, + UNW_AARCH64_X23 = 23, + UNW_AARCH64_X24 = 24, + UNW_AARCH64_X25 = 25, + UNW_AARCH64_X26 = 26, + UNW_AARCH64_X27 = 27, + UNW_AARCH64_X28 = 28, + UNW_AARCH64_X29 = 29, + UNW_AARCH64_FP = 29, + UNW_AARCH64_X30 = 30, + UNW_AARCH64_LR = 30, + UNW_AARCH64_X31 = 31, + UNW_AARCH64_SP = 31, + UNW_AARCH64_PC = 32, + + // reserved block + UNW_AARCH64_RA_SIGN_STATE = 34, + + // FP/vector registers + UNW_AARCH64_V0 = 64, + UNW_AARCH64_V1 = 65, + UNW_AARCH64_V2 = 66, + UNW_AARCH64_V3 = 67, + UNW_AARCH64_V4 = 68, + UNW_AARCH64_V5 = 69, + UNW_AARCH64_V6 = 70, + UNW_AARCH64_V7 = 71, + UNW_AARCH64_V8 = 72, + UNW_AARCH64_V9 = 73, + UNW_AARCH64_V10 = 74, + UNW_AARCH64_V11 = 75, + UNW_AARCH64_V12 = 76, + UNW_AARCH64_V13 = 77, + UNW_AARCH64_V14 = 78, + UNW_AARCH64_V15 = 79, + UNW_AARCH64_V16 = 80, + UNW_AARCH64_V17 = 81, + UNW_AARCH64_V18 = 82, + UNW_AARCH64_V19 = 83, + UNW_AARCH64_V20 = 84, + UNW_AARCH64_V21 = 85, + UNW_AARCH64_V22 = 86, + UNW_AARCH64_V23 = 87, + UNW_AARCH64_V24 = 88, + UNW_AARCH64_V25 = 89, + UNW_AARCH64_V26 = 90, + UNW_AARCH64_V27 = 91, + UNW_AARCH64_V28 = 92, + UNW_AARCH64_V29 = 93, + UNW_AARCH64_V30 = 94, + UNW_AARCH64_V31 = 95, + + // Compatibility aliases + UNW_ARM64_X0 = UNW_AARCH64_X0, + UNW_ARM64_X1 = UNW_AARCH64_X1, + UNW_ARM64_X2 = UNW_AARCH64_X2, + UNW_ARM64_X3 = UNW_AARCH64_X3, + UNW_ARM64_X4 = UNW_AARCH64_X4, + UNW_ARM64_X5 = UNW_AARCH64_X5, + UNW_ARM64_X6 = UNW_AARCH64_X6, + UNW_ARM64_X7 = UNW_AARCH64_X7, + UNW_ARM64_X8 = UNW_AARCH64_X8, + UNW_ARM64_X9 = UNW_AARCH64_X9, + UNW_ARM64_X10 = UNW_AARCH64_X10, + UNW_ARM64_X11 = UNW_AARCH64_X11, + UNW_ARM64_X12 = UNW_AARCH64_X12, + UNW_ARM64_X13 = UNW_AARCH64_X13, + UNW_ARM64_X14 = UNW_AARCH64_X14, + UNW_ARM64_X15 = UNW_AARCH64_X15, + UNW_ARM64_X16 = UNW_AARCH64_X16, + UNW_ARM64_X17 = UNW_AARCH64_X17, + UNW_ARM64_X18 = UNW_AARCH64_X18, + UNW_ARM64_X19 = UNW_AARCH64_X19, + UNW_ARM64_X20 = UNW_AARCH64_X20, + UNW_ARM64_X21 = UNW_AARCH64_X21, + UNW_ARM64_X22 = UNW_AARCH64_X22, + UNW_ARM64_X23 = UNW_AARCH64_X23, + UNW_ARM64_X24 = UNW_AARCH64_X24, + UNW_ARM64_X25 = UNW_AARCH64_X25, + UNW_ARM64_X26 = UNW_AARCH64_X26, + UNW_ARM64_X27 = UNW_AARCH64_X27, + UNW_ARM64_X28 = UNW_AARCH64_X28, + UNW_ARM64_X29 = UNW_AARCH64_X29, + UNW_ARM64_FP = UNW_AARCH64_FP, + UNW_ARM64_X30 = UNW_AARCH64_X30, + UNW_ARM64_LR = UNW_AARCH64_LR, + UNW_ARM64_X31 = UNW_AARCH64_X31, + UNW_ARM64_SP = UNW_AARCH64_SP, + UNW_ARM64_PC = UNW_AARCH64_PC, + UNW_ARM64_RA_SIGN_STATE = UNW_AARCH64_RA_SIGN_STATE, + UNW_ARM64_D0 = UNW_AARCH64_V0, + UNW_ARM64_D1 = UNW_AARCH64_V1, + UNW_ARM64_D2 = UNW_AARCH64_V2, + UNW_ARM64_D3 = UNW_AARCH64_V3, + UNW_ARM64_D4 = UNW_AARCH64_V4, + UNW_ARM64_D5 = UNW_AARCH64_V5, + UNW_ARM64_D6 = UNW_AARCH64_V6, + UNW_ARM64_D7 = UNW_AARCH64_V7, + UNW_ARM64_D8 = UNW_AARCH64_V8, + UNW_ARM64_D9 = UNW_AARCH64_V9, + UNW_ARM64_D10 = UNW_AARCH64_V10, + UNW_ARM64_D11 = UNW_AARCH64_V11, + UNW_ARM64_D12 = UNW_AARCH64_V12, + UNW_ARM64_D13 = UNW_AARCH64_V13, + UNW_ARM64_D14 = UNW_AARCH64_V14, + UNW_ARM64_D15 = UNW_AARCH64_V15, + UNW_ARM64_D16 = UNW_AARCH64_V16, + UNW_ARM64_D17 = UNW_AARCH64_V17, + UNW_ARM64_D18 = UNW_AARCH64_V18, + UNW_ARM64_D19 = UNW_AARCH64_V19, + UNW_ARM64_D20 = UNW_AARCH64_V20, + UNW_ARM64_D21 = UNW_AARCH64_V21, + UNW_ARM64_D22 = UNW_AARCH64_V22, + UNW_ARM64_D23 = UNW_AARCH64_V23, + UNW_ARM64_D24 = UNW_AARCH64_V24, + UNW_ARM64_D25 = UNW_AARCH64_V25, + UNW_ARM64_D26 = UNW_AARCH64_V26, + UNW_ARM64_D27 = UNW_AARCH64_V27, + UNW_ARM64_D28 = UNW_AARCH64_V28, + UNW_ARM64_D29 = UNW_AARCH64_V29, + UNW_ARM64_D30 = UNW_AARCH64_V30, + UNW_ARM64_D31 = UNW_AARCH64_V31, +}; + +// 32-bit ARM registers. Numbers match DWARF for ARM spec #3.1 Table 1. +// Naming scheme uses recommendations given in Note 4 for VFP-v2 and VFP-v3. +// In this scheme, even though the 64-bit floating point registers D0-D31 +// overlap physically with the 32-bit floating pointer registers S0-S31, +// they are given a non-overlapping range of register numbers. +// +// Commented out ranges are not preserved during unwinding. +enum { + UNW_ARM_R0 = 0, + UNW_ARM_R1 = 1, + UNW_ARM_R2 = 2, + UNW_ARM_R3 = 3, + UNW_ARM_R4 = 4, + UNW_ARM_R5 = 5, + UNW_ARM_R6 = 6, + UNW_ARM_R7 = 7, + UNW_ARM_R8 = 8, + UNW_ARM_R9 = 9, + UNW_ARM_R10 = 10, + UNW_ARM_R11 = 11, + UNW_ARM_R12 = 12, + UNW_ARM_SP = 13, // Logical alias for UNW_REG_SP + UNW_ARM_R13 = 13, + UNW_ARM_LR = 14, + UNW_ARM_R14 = 14, + UNW_ARM_IP = 15, // Logical alias for UNW_REG_IP + UNW_ARM_R15 = 15, + // 16-63 -- OBSOLETE. Used in VFP1 to represent both S0-S31 and D0-D31. + UNW_ARM_S0 = 64, + UNW_ARM_S1 = 65, + UNW_ARM_S2 = 66, + UNW_ARM_S3 = 67, + UNW_ARM_S4 = 68, + UNW_ARM_S5 = 69, + UNW_ARM_S6 = 70, + UNW_ARM_S7 = 71, + UNW_ARM_S8 = 72, + UNW_ARM_S9 = 73, + UNW_ARM_S10 = 74, + UNW_ARM_S11 = 75, + UNW_ARM_S12 = 76, + UNW_ARM_S13 = 77, + UNW_ARM_S14 = 78, + UNW_ARM_S15 = 79, + UNW_ARM_S16 = 80, + UNW_ARM_S17 = 81, + UNW_ARM_S18 = 82, + UNW_ARM_S19 = 83, + UNW_ARM_S20 = 84, + UNW_ARM_S21 = 85, + UNW_ARM_S22 = 86, + UNW_ARM_S23 = 87, + UNW_ARM_S24 = 88, + UNW_ARM_S25 = 89, + UNW_ARM_S26 = 90, + UNW_ARM_S27 = 91, + UNW_ARM_S28 = 92, + UNW_ARM_S29 = 93, + UNW_ARM_S30 = 94, + UNW_ARM_S31 = 95, + // 96-103 -- OBSOLETE. F0-F7. Used by the FPA system. Superseded by VFP. + // 104-111 -- wCGR0-wCGR7, ACC0-ACC7 (Intel wireless MMX) + UNW_ARM_WR0 = 112, + UNW_ARM_WR1 = 113, + UNW_ARM_WR2 = 114, + UNW_ARM_WR3 = 115, + UNW_ARM_WR4 = 116, + UNW_ARM_WR5 = 117, + UNW_ARM_WR6 = 118, + UNW_ARM_WR7 = 119, + UNW_ARM_WR8 = 120, + UNW_ARM_WR9 = 121, + UNW_ARM_WR10 = 122, + UNW_ARM_WR11 = 123, + UNW_ARM_WR12 = 124, + UNW_ARM_WR13 = 125, + UNW_ARM_WR14 = 126, + UNW_ARM_WR15 = 127, + // 128-133 -- SPSR, SPSR_{FIQ|IRQ|ABT|UND|SVC} + // 134-142 -- Reserved + UNW_ARM_RA_AUTH_CODE = 143, + // 144-150 -- R8_USR-R14_USR + // 151-157 -- R8_FIQ-R14_FIQ + // 158-159 -- R13_IRQ-R14_IRQ + // 160-161 -- R13_ABT-R14_ABT + // 162-163 -- R13_UND-R14_UND + // 164-165 -- R13_SVC-R14_SVC + // 166-191 -- Reserved + UNW_ARM_WC0 = 192, + UNW_ARM_WC1 = 193, + UNW_ARM_WC2 = 194, + UNW_ARM_WC3 = 195, + // 196-199 -- wC4-wC7 (Intel wireless MMX control) + // 200-255 -- Reserved + UNW_ARM_D0 = 256, + UNW_ARM_D1 = 257, + UNW_ARM_D2 = 258, + UNW_ARM_D3 = 259, + UNW_ARM_D4 = 260, + UNW_ARM_D5 = 261, + UNW_ARM_D6 = 262, + UNW_ARM_D7 = 263, + UNW_ARM_D8 = 264, + UNW_ARM_D9 = 265, + UNW_ARM_D10 = 266, + UNW_ARM_D11 = 267, + UNW_ARM_D12 = 268, + UNW_ARM_D13 = 269, + UNW_ARM_D14 = 270, + UNW_ARM_D15 = 271, + UNW_ARM_D16 = 272, + UNW_ARM_D17 = 273, + UNW_ARM_D18 = 274, + UNW_ARM_D19 = 275, + UNW_ARM_D20 = 276, + UNW_ARM_D21 = 277, + UNW_ARM_D22 = 278, + UNW_ARM_D23 = 279, + UNW_ARM_D24 = 280, + UNW_ARM_D25 = 281, + UNW_ARM_D26 = 282, + UNW_ARM_D27 = 283, + UNW_ARM_D28 = 284, + UNW_ARM_D29 = 285, + UNW_ARM_D30 = 286, + UNW_ARM_D31 = 287, + // 288-319 -- Reserved for VFP/Neon + // 320-8191 -- Reserved + // 8192-16383 -- Unspecified vendor co-processor register. +}; + +// OpenRISC1000 register numbers +enum { + UNW_OR1K_R0 = 0, + UNW_OR1K_R1 = 1, + UNW_OR1K_R2 = 2, + UNW_OR1K_R3 = 3, + UNW_OR1K_R4 = 4, + UNW_OR1K_R5 = 5, + UNW_OR1K_R6 = 6, + UNW_OR1K_R7 = 7, + UNW_OR1K_R8 = 8, + UNW_OR1K_R9 = 9, + UNW_OR1K_R10 = 10, + UNW_OR1K_R11 = 11, + UNW_OR1K_R12 = 12, + UNW_OR1K_R13 = 13, + UNW_OR1K_R14 = 14, + UNW_OR1K_R15 = 15, + UNW_OR1K_R16 = 16, + UNW_OR1K_R17 = 17, + UNW_OR1K_R18 = 18, + UNW_OR1K_R19 = 19, + UNW_OR1K_R20 = 20, + UNW_OR1K_R21 = 21, + UNW_OR1K_R22 = 22, + UNW_OR1K_R23 = 23, + UNW_OR1K_R24 = 24, + UNW_OR1K_R25 = 25, + UNW_OR1K_R26 = 26, + UNW_OR1K_R27 = 27, + UNW_OR1K_R28 = 28, + UNW_OR1K_R29 = 29, + UNW_OR1K_R30 = 30, + UNW_OR1K_R31 = 31, + UNW_OR1K_EPCR = 32, +}; + +// MIPS registers +enum { + UNW_MIPS_R0 = 0, + UNW_MIPS_R1 = 1, + UNW_MIPS_R2 = 2, + UNW_MIPS_R3 = 3, + UNW_MIPS_R4 = 4, + UNW_MIPS_R5 = 5, + UNW_MIPS_R6 = 6, + UNW_MIPS_R7 = 7, + UNW_MIPS_R8 = 8, + UNW_MIPS_R9 = 9, + UNW_MIPS_R10 = 10, + UNW_MIPS_R11 = 11, + UNW_MIPS_R12 = 12, + UNW_MIPS_R13 = 13, + UNW_MIPS_R14 = 14, + UNW_MIPS_R15 = 15, + UNW_MIPS_R16 = 16, + UNW_MIPS_R17 = 17, + UNW_MIPS_R18 = 18, + UNW_MIPS_R19 = 19, + UNW_MIPS_R20 = 20, + UNW_MIPS_R21 = 21, + UNW_MIPS_R22 = 22, + UNW_MIPS_R23 = 23, + UNW_MIPS_R24 = 24, + UNW_MIPS_R25 = 25, + UNW_MIPS_R26 = 26, + UNW_MIPS_R27 = 27, + UNW_MIPS_R28 = 28, + UNW_MIPS_R29 = 29, + UNW_MIPS_R30 = 30, + UNW_MIPS_R31 = 31, + UNW_MIPS_F0 = 32, + UNW_MIPS_F1 = 33, + UNW_MIPS_F2 = 34, + UNW_MIPS_F3 = 35, + UNW_MIPS_F4 = 36, + UNW_MIPS_F5 = 37, + UNW_MIPS_F6 = 38, + UNW_MIPS_F7 = 39, + UNW_MIPS_F8 = 40, + UNW_MIPS_F9 = 41, + UNW_MIPS_F10 = 42, + UNW_MIPS_F11 = 43, + UNW_MIPS_F12 = 44, + UNW_MIPS_F13 = 45, + UNW_MIPS_F14 = 46, + UNW_MIPS_F15 = 47, + UNW_MIPS_F16 = 48, + UNW_MIPS_F17 = 49, + UNW_MIPS_F18 = 50, + UNW_MIPS_F19 = 51, + UNW_MIPS_F20 = 52, + UNW_MIPS_F21 = 53, + UNW_MIPS_F22 = 54, + UNW_MIPS_F23 = 55, + UNW_MIPS_F24 = 56, + UNW_MIPS_F25 = 57, + UNW_MIPS_F26 = 58, + UNW_MIPS_F27 = 59, + UNW_MIPS_F28 = 60, + UNW_MIPS_F29 = 61, + UNW_MIPS_F30 = 62, + UNW_MIPS_F31 = 63, + UNW_MIPS_HI = 64, + UNW_MIPS_LO = 65, +}; + +// SPARC registers +enum { + UNW_SPARC_G0 = 0, + UNW_SPARC_G1 = 1, + UNW_SPARC_G2 = 2, + UNW_SPARC_G3 = 3, + UNW_SPARC_G4 = 4, + UNW_SPARC_G5 = 5, + UNW_SPARC_G6 = 6, + UNW_SPARC_G7 = 7, + UNW_SPARC_O0 = 8, + UNW_SPARC_O1 = 9, + UNW_SPARC_O2 = 10, + UNW_SPARC_O3 = 11, + UNW_SPARC_O4 = 12, + UNW_SPARC_O5 = 13, + UNW_SPARC_O6 = 14, + UNW_SPARC_O7 = 15, + UNW_SPARC_L0 = 16, + UNW_SPARC_L1 = 17, + UNW_SPARC_L2 = 18, + UNW_SPARC_L3 = 19, + UNW_SPARC_L4 = 20, + UNW_SPARC_L5 = 21, + UNW_SPARC_L6 = 22, + UNW_SPARC_L7 = 23, + UNW_SPARC_I0 = 24, + UNW_SPARC_I1 = 25, + UNW_SPARC_I2 = 26, + UNW_SPARC_I3 = 27, + UNW_SPARC_I4 = 28, + UNW_SPARC_I5 = 29, + UNW_SPARC_I6 = 30, + UNW_SPARC_I7 = 31, +}; + +// Hexagon register numbers +enum { + UNW_HEXAGON_R0, + UNW_HEXAGON_R1, + UNW_HEXAGON_R2, + UNW_HEXAGON_R3, + UNW_HEXAGON_R4, + UNW_HEXAGON_R5, + UNW_HEXAGON_R6, + UNW_HEXAGON_R7, + UNW_HEXAGON_R8, + UNW_HEXAGON_R9, + UNW_HEXAGON_R10, + UNW_HEXAGON_R11, + UNW_HEXAGON_R12, + UNW_HEXAGON_R13, + UNW_HEXAGON_R14, + UNW_HEXAGON_R15, + UNW_HEXAGON_R16, + UNW_HEXAGON_R17, + UNW_HEXAGON_R18, + UNW_HEXAGON_R19, + UNW_HEXAGON_R20, + UNW_HEXAGON_R21, + UNW_HEXAGON_R22, + UNW_HEXAGON_R23, + UNW_HEXAGON_R24, + UNW_HEXAGON_R25, + UNW_HEXAGON_R26, + UNW_HEXAGON_R27, + UNW_HEXAGON_R28, + UNW_HEXAGON_R29, + UNW_HEXAGON_R30, + UNW_HEXAGON_R31, + UNW_HEXAGON_P3_0, + UNW_HEXAGON_PC, +}; + +// RISC-V registers. These match the DWARF register numbers defined by section +// 4 of the RISC-V ELF psABI specification, which can be found at: +// +// https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md +enum { + UNW_RISCV_X0 = 0, + UNW_RISCV_X1 = 1, + UNW_RISCV_X2 = 2, + UNW_RISCV_X3 = 3, + UNW_RISCV_X4 = 4, + UNW_RISCV_X5 = 5, + UNW_RISCV_X6 = 6, + UNW_RISCV_X7 = 7, + UNW_RISCV_X8 = 8, + UNW_RISCV_X9 = 9, + UNW_RISCV_X10 = 10, + UNW_RISCV_X11 = 11, + UNW_RISCV_X12 = 12, + UNW_RISCV_X13 = 13, + UNW_RISCV_X14 = 14, + UNW_RISCV_X15 = 15, + UNW_RISCV_X16 = 16, + UNW_RISCV_X17 = 17, + UNW_RISCV_X18 = 18, + UNW_RISCV_X19 = 19, + UNW_RISCV_X20 = 20, + UNW_RISCV_X21 = 21, + UNW_RISCV_X22 = 22, + UNW_RISCV_X23 = 23, + UNW_RISCV_X24 = 24, + UNW_RISCV_X25 = 25, + UNW_RISCV_X26 = 26, + UNW_RISCV_X27 = 27, + UNW_RISCV_X28 = 28, + UNW_RISCV_X29 = 29, + UNW_RISCV_X30 = 30, + UNW_RISCV_X31 = 31, + UNW_RISCV_F0 = 32, + UNW_RISCV_F1 = 33, + UNW_RISCV_F2 = 34, + UNW_RISCV_F3 = 35, + UNW_RISCV_F4 = 36, + UNW_RISCV_F5 = 37, + UNW_RISCV_F6 = 38, + UNW_RISCV_F7 = 39, + UNW_RISCV_F8 = 40, + UNW_RISCV_F9 = 41, + UNW_RISCV_F10 = 42, + UNW_RISCV_F11 = 43, + UNW_RISCV_F12 = 44, + UNW_RISCV_F13 = 45, + UNW_RISCV_F14 = 46, + UNW_RISCV_F15 = 47, + UNW_RISCV_F16 = 48, + UNW_RISCV_F17 = 49, + UNW_RISCV_F18 = 50, + UNW_RISCV_F19 = 51, + UNW_RISCV_F20 = 52, + UNW_RISCV_F21 = 53, + UNW_RISCV_F22 = 54, + UNW_RISCV_F23 = 55, + UNW_RISCV_F24 = 56, + UNW_RISCV_F25 = 57, + UNW_RISCV_F26 = 58, + UNW_RISCV_F27 = 59, + UNW_RISCV_F28 = 60, + UNW_RISCV_F29 = 61, + UNW_RISCV_F30 = 62, + UNW_RISCV_F31 = 63, + // 65-95 -- Reserved for future standard extensions + // 96-127 -- v0-v31 (Vector registers) + // 128-3071 -- Reserved for future standard extensions + // 3072-4095 -- Reserved for custom extensions + // 4096-8191 -- CSRs + // + // VLENB CSR number: 0xC22 -- defined by section 3 of v-spec: + // https://github.com/riscv/riscv-v-spec/blob/master/v-spec.adoc#3-vector-extension-programmers-model + // VLENB DWARF number: 0x1000 + 0xC22 + UNW_RISCV_VLENB = 0x1C22, +}; + +// VE register numbers +enum { + UNW_VE_S0 = 0, + UNW_VE_S1 = 1, + UNW_VE_S2 = 2, + UNW_VE_S3 = 3, + UNW_VE_S4 = 4, + UNW_VE_S5 = 5, + UNW_VE_S6 = 6, + UNW_VE_S7 = 7, + UNW_VE_S8 = 8, + UNW_VE_S9 = 9, + UNW_VE_S10 = 10, + UNW_VE_S11 = 11, + UNW_VE_S12 = 12, + UNW_VE_S13 = 13, + UNW_VE_S14 = 14, + UNW_VE_S15 = 15, + UNW_VE_S16 = 16, + UNW_VE_S17 = 17, + UNW_VE_S18 = 18, + UNW_VE_S19 = 19, + UNW_VE_S20 = 20, + UNW_VE_S21 = 21, + UNW_VE_S22 = 22, + UNW_VE_S23 = 23, + UNW_VE_S24 = 24, + UNW_VE_S25 = 25, + UNW_VE_S26 = 26, + UNW_VE_S27 = 27, + UNW_VE_S28 = 28, + UNW_VE_S29 = 29, + UNW_VE_S30 = 30, + UNW_VE_S31 = 31, + UNW_VE_S32 = 32, + UNW_VE_S33 = 33, + UNW_VE_S34 = 34, + UNW_VE_S35 = 35, + UNW_VE_S36 = 36, + UNW_VE_S37 = 37, + UNW_VE_S38 = 38, + UNW_VE_S39 = 39, + UNW_VE_S40 = 40, + UNW_VE_S41 = 41, + UNW_VE_S42 = 42, + UNW_VE_S43 = 43, + UNW_VE_S44 = 44, + UNW_VE_S45 = 45, + UNW_VE_S46 = 46, + UNW_VE_S47 = 47, + UNW_VE_S48 = 48, + UNW_VE_S49 = 49, + UNW_VE_S50 = 50, + UNW_VE_S51 = 51, + UNW_VE_S52 = 52, + UNW_VE_S53 = 53, + UNW_VE_S54 = 54, + UNW_VE_S55 = 55, + UNW_VE_S56 = 56, + UNW_VE_S57 = 57, + UNW_VE_S58 = 58, + UNW_VE_S59 = 59, + UNW_VE_S60 = 60, + UNW_VE_S61 = 61, + UNW_VE_S62 = 62, + UNW_VE_S63 = 63, + UNW_VE_V0 = 64 + 0, + UNW_VE_V1 = 64 + 1, + UNW_VE_V2 = 64 + 2, + UNW_VE_V3 = 64 + 3, + UNW_VE_V4 = 64 + 4, + UNW_VE_V5 = 64 + 5, + UNW_VE_V6 = 64 + 6, + UNW_VE_V7 = 64 + 7, + UNW_VE_V8 = 64 + 8, + UNW_VE_V9 = 64 + 9, + UNW_VE_V10 = 64 + 10, + UNW_VE_V11 = 64 + 11, + UNW_VE_V12 = 64 + 12, + UNW_VE_V13 = 64 + 13, + UNW_VE_V14 = 64 + 14, + UNW_VE_V15 = 64 + 15, + UNW_VE_V16 = 64 + 16, + UNW_VE_V17 = 64 + 17, + UNW_VE_V18 = 64 + 18, + UNW_VE_V19 = 64 + 19, + UNW_VE_V20 = 64 + 20, + UNW_VE_V21 = 64 + 21, + UNW_VE_V22 = 64 + 22, + UNW_VE_V23 = 64 + 23, + UNW_VE_V24 = 64 + 24, + UNW_VE_V25 = 64 + 25, + UNW_VE_V26 = 64 + 26, + UNW_VE_V27 = 64 + 27, + UNW_VE_V28 = 64 + 28, + UNW_VE_V29 = 64 + 29, + UNW_VE_V30 = 64 + 30, + UNW_VE_V31 = 64 + 31, + UNW_VE_V32 = 64 + 32, + UNW_VE_V33 = 64 + 33, + UNW_VE_V34 = 64 + 34, + UNW_VE_V35 = 64 + 35, + UNW_VE_V36 = 64 + 36, + UNW_VE_V37 = 64 + 37, + UNW_VE_V38 = 64 + 38, + UNW_VE_V39 = 64 + 39, + UNW_VE_V40 = 64 + 40, + UNW_VE_V41 = 64 + 41, + UNW_VE_V42 = 64 + 42, + UNW_VE_V43 = 64 + 43, + UNW_VE_V44 = 64 + 44, + UNW_VE_V45 = 64 + 45, + UNW_VE_V46 = 64 + 46, + UNW_VE_V47 = 64 + 47, + UNW_VE_V48 = 64 + 48, + UNW_VE_V49 = 64 + 49, + UNW_VE_V50 = 64 + 50, + UNW_VE_V51 = 64 + 51, + UNW_VE_V52 = 64 + 52, + UNW_VE_V53 = 64 + 53, + UNW_VE_V54 = 64 + 54, + UNW_VE_V55 = 64 + 55, + UNW_VE_V56 = 64 + 56, + UNW_VE_V57 = 64 + 57, + UNW_VE_V58 = 64 + 58, + UNW_VE_V59 = 64 + 59, + UNW_VE_V60 = 64 + 60, + UNW_VE_V61 = 64 + 61, + UNW_VE_V62 = 64 + 62, + UNW_VE_V63 = 64 + 63, + UNW_VE_VM0 = 128 + 0, + UNW_VE_VM1 = 128 + 1, + UNW_VE_VM2 = 128 + 2, + UNW_VE_VM3 = 128 + 3, + UNW_VE_VM4 = 128 + 4, + UNW_VE_VM5 = 128 + 5, + UNW_VE_VM6 = 128 + 6, + UNW_VE_VM7 = 128 + 7, + UNW_VE_VM8 = 128 + 8, + UNW_VE_VM9 = 128 + 9, + UNW_VE_VM10 = 128 + 10, + UNW_VE_VM11 = 128 + 11, + UNW_VE_VM12 = 128 + 12, + UNW_VE_VM13 = 128 + 13, + UNW_VE_VM14 = 128 + 14, + UNW_VE_VM15 = 128 + 15, // = 143 + + // Following registers don't have DWARF register numbers. + UNW_VE_VIXR = 144, + UNW_VE_VL = 145, +}; + +// s390x register numbers +enum { + UNW_S390X_R0 = 0, + UNW_S390X_R1 = 1, + UNW_S390X_R2 = 2, + UNW_S390X_R3 = 3, + UNW_S390X_R4 = 4, + UNW_S390X_R5 = 5, + UNW_S390X_R6 = 6, + UNW_S390X_R7 = 7, + UNW_S390X_R8 = 8, + UNW_S390X_R9 = 9, + UNW_S390X_R10 = 10, + UNW_S390X_R11 = 11, + UNW_S390X_R12 = 12, + UNW_S390X_R13 = 13, + UNW_S390X_R14 = 14, + UNW_S390X_R15 = 15, + UNW_S390X_F0 = 16, + UNW_S390X_F2 = 17, + UNW_S390X_F4 = 18, + UNW_S390X_F6 = 19, + UNW_S390X_F1 = 20, + UNW_S390X_F3 = 21, + UNW_S390X_F5 = 22, + UNW_S390X_F7 = 23, + UNW_S390X_F8 = 24, + UNW_S390X_F10 = 25, + UNW_S390X_F12 = 26, + UNW_S390X_F14 = 27, + UNW_S390X_F9 = 28, + UNW_S390X_F11 = 29, + UNW_S390X_F13 = 30, + UNW_S390X_F15 = 31, + // 32-47 Control Registers + // 48-63 Access Registers + UNW_S390X_PSWM = 64, + UNW_S390X_PSWA = 65, + // 66-67 Reserved + // 68-83 Vector Registers %v16-%v31 +}; + +// LoongArch registers. +enum { + UNW_LOONGARCH_R0 = 0, + UNW_LOONGARCH_R1 = 1, + UNW_LOONGARCH_R2 = 2, + UNW_LOONGARCH_R3 = 3, + UNW_LOONGARCH_R4 = 4, + UNW_LOONGARCH_R5 = 5, + UNW_LOONGARCH_R6 = 6, + UNW_LOONGARCH_R7 = 7, + UNW_LOONGARCH_R8 = 8, + UNW_LOONGARCH_R9 = 9, + UNW_LOONGARCH_R10 = 10, + UNW_LOONGARCH_R11 = 11, + UNW_LOONGARCH_R12 = 12, + UNW_LOONGARCH_R13 = 13, + UNW_LOONGARCH_R14 = 14, + UNW_LOONGARCH_R15 = 15, + UNW_LOONGARCH_R16 = 16, + UNW_LOONGARCH_R17 = 17, + UNW_LOONGARCH_R18 = 18, + UNW_LOONGARCH_R19 = 19, + UNW_LOONGARCH_R20 = 20, + UNW_LOONGARCH_R21 = 21, + UNW_LOONGARCH_R22 = 22, + UNW_LOONGARCH_R23 = 23, + UNW_LOONGARCH_R24 = 24, + UNW_LOONGARCH_R25 = 25, + UNW_LOONGARCH_R26 = 26, + UNW_LOONGARCH_R27 = 27, + UNW_LOONGARCH_R28 = 28, + UNW_LOONGARCH_R29 = 29, + UNW_LOONGARCH_R30 = 30, + UNW_LOONGARCH_R31 = 31, + UNW_LOONGARCH_F0 = 32, + UNW_LOONGARCH_F1 = 33, + UNW_LOONGARCH_F2 = 34, + UNW_LOONGARCH_F3 = 35, + UNW_LOONGARCH_F4 = 36, + UNW_LOONGARCH_F5 = 37, + UNW_LOONGARCH_F6 = 38, + UNW_LOONGARCH_F7 = 39, + UNW_LOONGARCH_F8 = 40, + UNW_LOONGARCH_F9 = 41, + UNW_LOONGARCH_F10 = 42, + UNW_LOONGARCH_F11 = 43, + UNW_LOONGARCH_F12 = 44, + UNW_LOONGARCH_F13 = 45, + UNW_LOONGARCH_F14 = 46, + UNW_LOONGARCH_F15 = 47, + UNW_LOONGARCH_F16 = 48, + UNW_LOONGARCH_F17 = 49, + UNW_LOONGARCH_F18 = 50, + UNW_LOONGARCH_F19 = 51, + UNW_LOONGARCH_F20 = 52, + UNW_LOONGARCH_F21 = 53, + UNW_LOONGARCH_F22 = 54, + UNW_LOONGARCH_F23 = 55, + UNW_LOONGARCH_F24 = 56, + UNW_LOONGARCH_F25 = 57, + UNW_LOONGARCH_F26 = 58, + UNW_LOONGARCH_F27 = 59, + UNW_LOONGARCH_F28 = 60, + UNW_LOONGARCH_F29 = 61, + UNW_LOONGARCH_F30 = 62, + UNW_LOONGARCH_F31 = 63, +}; + +#endif diff --git a/third_party/libunwind/include/mach-o/compact_unwind_encoding.h b/third_party/libunwind/include/mach-o/compact_unwind_encoding.h new file mode 100644 index 000000000..34979a9ab --- /dev/null +++ b/third_party/libunwind/include/mach-o/compact_unwind_encoding.h @@ -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 + diff --git a/third_party/libunwind/include/unwind.h b/third_party/libunwind/include/unwind.h new file mode 100644 index 000000000..051d44a73 --- /dev/null +++ b/third_party/libunwind/include/unwind.h @@ -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 +#include +#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__ diff --git a/third_party/libunwind/include/unwind_arm_ehabi.h b/third_party/libunwind/include/unwind_arm_ehabi.h new file mode 100644 index 000000000..6277a1457 --- /dev/null +++ b/third_party/libunwind/include/unwind_arm_ehabi.h @@ -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 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__ diff --git a/third_party/libunwind/include/unwind_itanium.h b/third_party/libunwind/include/unwind_itanium.h new file mode 100644 index 000000000..d94a6183b --- /dev/null +++ b/third_party/libunwind/include/unwind_itanium.h @@ -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__ diff --git a/third_party/libunwind/libunwind.cc b/third_party/libunwind/libunwind.cc new file mode 100644 index 000000000..e116c1115 --- /dev/null +++ b/third_party/libunwind/libunwind.cc @@ -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 +// +//===----------------------------------------------------------------------===// + +#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 +#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(cursor), + static_cast(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 *>(cursor)) + UnwindCursor( + 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(cursor), regNum, + static_cast(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(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(cursor), regNum, + static_cast(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(cursor), regNum, value); +#else + _LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%g)", + static_cast(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(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(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(cursor), static_cast(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(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(cursor), static_cast(buf), + static_cast(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(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(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(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(cursor)); + AbstractUnwindCursor *co = reinterpret_cast(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(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(func)); + DwarfFDECache::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::FDE_Info fdeInfo; + CFI_Parser::CIE_Info cieInfo; + const char *message = CFI_Parser::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::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::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::CIE_Info cieInfo; + CFI_Parser::FDE_Info fdeInfo; + auto p = (LocalAddressSpace::pint_t)eh_frame_start; + while (true) { + if (CFI_Parser::decodeFDE( + LocalAddressSpace::sThisAddressSpace, p, &fdeInfo, &cieInfo, + true) == NULL) { + DwarfFDECache::add((LocalAddressSpace::pint_t)mh_group, + fdeInfo.pcStart, fdeInfo.pcEnd, + fdeInfo.fdeStart); + p += fdeInfo.fdeLength; + } else if (CFI_Parser::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::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 + diff --git a/third_party/libunwind/libunwind_ext.h b/third_party/libunwind/libunwind_ext.h new file mode 100644 index 000000000..bbc3e501f --- /dev/null +++ b/third_party/libunwind/libunwind_ext.h @@ -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__