Commit 6295415da7
Changed files (17)
lib
libunwind
lib/libunwind/include/mach-o/compact_unwind_encoding.h
@@ -108,7 +108,7 @@ enum {
// 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
+// 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.
@@ -119,16 +119,16 @@ enum {
// 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
+// 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
+// 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_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
+// 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.
//
@@ -233,36 +233,36 @@ enum {
// 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
+// 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
+// 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.
+// 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
+// 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.
+// saved and their order.
// UNWIND_X86_64_MODE_STACK_IND:
-// A "frameless" (RBP not used as frame pointer) function large constant
+// 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
+// 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_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
+// 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.
//
@@ -307,20 +307,20 @@ enum {
// 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
+// 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.
+// number order.
// UNWIND_ARM64_MODE_FRAMELESS:
-// A "frameless" leaf function, where FP/LR are not saved. The return address
+// 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
+// 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
+// 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.
//
@@ -337,19 +337,19 @@ enum {
//
// 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
+// 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
+// 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.
+// The length and encoding fields are 32-bits. The other are all pointer sized.
//
// In x86_64 assembly, these entry would look like:
//
@@ -372,23 +372,23 @@ enum {
// .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
+// Notes: There is no need for any labels in 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
+// 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
+// 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.
+// 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.
+// 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.
@@ -402,7 +402,7 @@ enum {
//
// 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.
+// to the page (4096 byte block) containing the unwind info for that function.
//
#define UNWIND_SECTION_VERSION 1
lib/libunwind/include/__libunwind_config.h
@@ -36,6 +36,9 @@
# if defined(__linux__)
# define _LIBUNWIND_TARGET_LINUX 1
# endif
+# if defined(__HAIKU__)
+# define _LIBUNWIND_TARGET_HAIKU 1
+# endif
# if defined(__i386__)
# define _LIBUNWIND_TARGET_I386
# define _LIBUNWIND_CONTEXT_SIZE 8
@@ -196,7 +199,7 @@
# define _LIBUNWIND_TARGET_RISCV 1
# define _LIBUNWIND_TARGET_VE 1
# define _LIBUNWIND_TARGET_S390X 1
- #define _LIBUNWIND_TARGET_LOONGARCH 1
+# define _LIBUNWIND_TARGET_LOONGARCH 1
# define _LIBUNWIND_CONTEXT_SIZE 167
# define _LIBUNWIND_CURSOR_SIZE 204
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287
lib/libunwind/include/libunwind.h
@@ -876,6 +876,9 @@ enum {
UNW_MIPS_F29 = 61,
UNW_MIPS_F30 = 62,
UNW_MIPS_F31 = 63,
+ // HI,LO have been dropped since r6, we keep them here.
+ // So, when we add DSP/MSA etc, we can use the same register indexes
+ // for r6 and pre-r6.
UNW_MIPS_HI = 64,
UNW_MIPS_LO = 65,
};
lib/libunwind/src/AddressSpace.hpp
@@ -414,8 +414,8 @@ static bool checkForUnwindInfoSegment(const Elf_Phdr *phdr, size_t image_base,
cbdata->sects->dwarf_index_section = eh_frame_hdr_start;
cbdata->sects->dwarf_index_section_length = phdr->p_memsz;
if (EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
- *cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz,
- hdrInfo)) {
+ *cbdata->addressSpace, eh_frame_hdr_start,
+ 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;
@@ -638,7 +638,8 @@ inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr,
info.dwarf_index_section_length = SIZE_MAX;
EHHeaderParser<LocalAddressSpace>::EHHeaderInfo hdrInfo;
if (!EHHeaderParser<LocalAddressSpace>::decodeEHHdr(
- *this, info.dwarf_index_section, info.dwarf_index_section_length,
+ *this, info.dwarf_index_section,
+ info.dwarf_index_section + info.dwarf_index_section_length,
hdrInfo)) {
return false;
}
lib/libunwind/src/config.h
@@ -46,6 +46,12 @@
#elif defined(_AIX)
// The traceback table at the end of each function is used for unwinding.
#define _LIBUNWIND_SUPPORT_TBTAB_UNWIND 1
+#elif defined(__HAIKU__)
+ #if defined(_LIBUNWIND_USE_HAIKU_BSD_LIB)
+ #define _LIBUNWIND_USE_DL_ITERATE_PHDR 1
+ #endif
+ #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1
+ #define _LIBUNWIND_SUPPORT_DWARF_INDEX 1
#else
// Assume an ELF system with a dl_iterate_phdr function.
#define _LIBUNWIND_USE_DL_ITERATE_PHDR 1
@@ -83,7 +89,7 @@
__asm__(".globl " SYMBOL_NAME(aliasname)); \
__asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name)); \
_LIBUNWIND_ALIAS_VISIBILITY(SYMBOL_NAME(aliasname))
-#elif defined(__ELF__) || defined(_AIX)
+#elif defined(__ELF__) || defined(_AIX) || defined(__wasm__)
#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \
extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \
__attribute__((weak, alias(#name)));
@@ -108,10 +114,6 @@
#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__) || \
@@ -125,7 +127,7 @@
#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_ALLOC(_size) __builtin_alloca(_size)
#define _LIBUNWIND_REMEMBER_FREE(_ptr) \
do { \
} while (0)
lib/libunwind/src/DwarfInstructions.hpp
@@ -68,7 +68,7 @@ private:
return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) +
prolog.cfaRegisterOffset);
if (prolog.cfaExpression != 0)
- return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace,
+ return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace,
registers, 0);
assert(0 && "getCFA(): unknown location");
__builtin_unreachable();
lib/libunwind/src/EHHeaderParser.hpp
@@ -55,6 +55,19 @@ template <typename A>
bool EHHeaderParser<A>::decodeEHHdr(A &addressSpace, pint_t ehHdrStart,
pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) {
pint_t p = ehHdrStart;
+
+ // Ensure that we don't read data beyond the end of .eh_frame_hdr
+ if (ehHdrEnd - ehHdrStart < 4) {
+ // Don't print a message for an empty .eh_frame_hdr (this can happen if
+ // the linker script defines symbols for it even in the empty case).
+ if (ehHdrEnd == ehHdrStart)
+ return false;
+ _LIBUNWIND_LOG("unsupported .eh_frame_hdr at %" PRIx64
+ ": need at least 4 bytes of data but only got %zd",
+ static_cast<uint64_t>(ehHdrStart),
+ static_cast<size_t>(ehHdrEnd - ehHdrStart));
+ return false;
+ }
uint8_t version = addressSpace.get8(p++);
if (version != 1) {
_LIBUNWIND_LOG("unsupported .eh_frame_hdr version: %" PRIu8 " at %" PRIx64,
lib/libunwind/src/FrameHeaderCache.hpp
@@ -31,8 +31,8 @@
class _LIBUNWIND_HIDDEN FrameHeaderCache {
struct CacheEntry {
- uintptr_t LowPC() { return Info.dso_base; };
- uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; };
+ uintptr_t LowPC() { return Info.dso_base; }
+ uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; }
UnwindInfoSections Info;
CacheEntry *Next;
};
@@ -41,7 +41,7 @@ class _LIBUNWIND_HIDDEN FrameHeaderCache {
// 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
+ // used entries. FIXME: Would 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
lib/libunwind/src/libunwind.cpp
@@ -26,7 +26,7 @@
#include <sanitizer/asan_interface.h>
#endif
-#if !defined(__USING_SJLJ_EXCEPTIONS__)
+#if !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__USING_WASM_EXCEPTIONS__)
#include "AddressSpace.hpp"
#include "UnwindCursor.hpp"
@@ -324,7 +324,7 @@ void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start) {
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
auto p = (LocalAddressSpace::pint_t)eh_frame_start;
- while (true) {
+ while (LocalAddressSpace::sThisAddressSpace.get32(p)) {
if (CFI_Parser<LocalAddressSpace>::decodeFDE(
LocalAddressSpace::sThisAddressSpace, p, &fdeInfo, &cieInfo,
true) == NULL) {
@@ -347,7 +347,8 @@ void __unw_remove_dynamic_eh_frame_section(unw_word_t eh_frame_start) {
}
#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
-#endif // !defined(__USING_SJLJ_EXCEPTIONS__)
+#endif // !defined(__USING_SJLJ_EXCEPTIONS__) &&
+ // !defined(__USING_WASM_EXCEPTIONS__)
#ifdef __APPLE__
lib/libunwind/src/Registers.hpp
@@ -619,6 +619,8 @@ public:
void setIP(uint32_t value) { _registers.__srr0 = value; }
uint64_t getCR() const { return _registers.__cr; }
void setCR(uint32_t value) { _registers.__cr = value; }
+ uint64_t getLR() const { return _registers.__lr; }
+ void setLR(uint32_t value) { _registers.__lr = value; }
private:
struct ppc_thread_state_t {
@@ -1189,6 +1191,8 @@ public:
void setIP(uint64_t value) { _registers.__srr0 = value; }
uint64_t getCR() const { return _registers.__cr; }
void setCR(uint64_t value) { _registers.__cr = value; }
+ uint64_t getLR() const { return _registers.__lr; }
+ void setLR(uint64_t value) { _registers.__lr = value; }
private:
struct ppc64_thread_state_t {
@@ -2869,7 +2873,7 @@ inline bool Registers_mips_o32::validRegister(int regNum) const {
return false;
if (regNum <= UNW_MIPS_R31)
return true;
-#if __mips_isa_rev != 6
+#if __mips_isa_rev < 6
if (regNum == UNW_MIPS_HI)
return true;
if (regNum == UNW_MIPS_LO)
@@ -2903,10 +2907,12 @@ inline uint32_t Registers_mips_o32::getRegister(int regNum) const {
return _registers.__pc;
case UNW_REG_SP:
return _registers.__r[29];
+#if __mips_isa_rev < 6
case UNW_MIPS_HI:
return _registers.__hi;
case UNW_MIPS_LO:
return _registers.__lo;
+#endif
}
_LIBUNWIND_ABORT("unsupported mips_o32 register");
}
@@ -2936,11 +2942,13 @@ inline void Registers_mips_o32::setRegister(int regNum, uint32_t value) {
case UNW_REG_SP:
_registers.__r[29] = value;
return;
+#if __mips_isa_rev < 6
case UNW_MIPS_HI:
_registers.__hi = value;
return;
case UNW_MIPS_LO:
_registers.__lo = value;
+#endif
return;
}
_LIBUNWIND_ABORT("unsupported mips_o32 register");
@@ -3120,10 +3128,12 @@ inline const char *Registers_mips_o32::getRegisterName(int regNum) {
return "$f30";
case UNW_MIPS_F31:
return "$f31";
+#if __mips_isa_rev < 6
case UNW_MIPS_HI:
return "$hi";
case UNW_MIPS_LO:
return "$lo";
+#endif
default:
return "unknown register";
}
@@ -3193,7 +3203,7 @@ inline bool Registers_mips_newabi::validRegister(int regNum) const {
return false;
if (regNum <= UNW_MIPS_R31)
return true;
-#if __mips_isa_rev != 6
+#if __mips_isa_rev < 6
if (regNum == UNW_MIPS_HI)
return true;
if (regNum == UNW_MIPS_LO)
@@ -3212,10 +3222,12 @@ inline uint64_t Registers_mips_newabi::getRegister(int regNum) const {
return _registers.__pc;
case UNW_REG_SP:
return _registers.__r[29];
+#if __mips_isa_rev < 6
case UNW_MIPS_HI:
return _registers.__hi;
case UNW_MIPS_LO:
return _registers.__lo;
+#endif
}
_LIBUNWIND_ABORT("unsupported mips_newabi register");
}
@@ -3233,12 +3245,14 @@ inline void Registers_mips_newabi::setRegister(int regNum, uint64_t value) {
case UNW_REG_SP:
_registers.__r[29] = value;
return;
+#if __mips_isa_rev < 6
case UNW_MIPS_HI:
_registers.__hi = value;
return;
case UNW_MIPS_LO:
_registers.__lo = value;
return;
+#endif
}
_LIBUNWIND_ABORT("unsupported mips_newabi register");
}
@@ -3417,10 +3431,12 @@ inline const char *Registers_mips_newabi::getRegisterName(int regNum) {
return "$f30";
case UNW_MIPS_F31:
return "$f31";
+#if __mips_isa_rev < 6
case UNW_MIPS_HI:
return "$hi";
case UNW_MIPS_LO:
return "$lo";
+#endif
default:
return "unknown register";
}
lib/libunwind/src/Unwind-sjlj.c
@@ -82,7 +82,8 @@ struct _Unwind_FunctionContext {
static _LIBUNWIND_THREAD_LOCAL struct _Unwind_FunctionContext *stack = NULL;
#endif
-static struct _Unwind_FunctionContext *__Unwind_SjLj_GetTopOfFunctionStack() {
+static struct _Unwind_FunctionContext *
+__Unwind_SjLj_GetTopOfFunctionStack(void) {
#if defined(__APPLE__)
return _pthread_getspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key);
#else
@@ -426,7 +427,7 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context *context,
/// 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
+ _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%" PRIxPTR
")",
(void *)context, index, new_value);
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
@@ -437,7 +438,7 @@ _LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index,
/// 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,
+ _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIxPTR,
(void *)context, ufc->resumeLocation + 1);
return ufc->resumeLocation + 1;
}
@@ -450,7 +451,7 @@ _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,
+ _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%" PRIxPTR,
(void *)context, (void *)ipBefore,
ufc->resumeLocation + 1);
return ufc->resumeLocation + 1;
@@ -460,7 +461,7 @@ _LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context,
/// 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 ")",
+ _LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%" PRIxPTR ")",
(void *)context, new_value);
_Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context;
ufc->resumeLocation = new_value - 1;
lib/libunwind/src/Unwind-wasm.c
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 Wasm exception handling proposal
+// (https://github.com/WebAssembly/exception-handling) based C++ exceptions
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdbool.h>
+
+#include "config.h"
+
+#ifdef __USING_WASM_EXCEPTIONS__
+
+#include "unwind.h"
+#include <threads.h>
+
+_Unwind_Reason_Code __gxx_personality_wasm0(int version, _Unwind_Action actions,
+ uint64_t exceptionClass,
+ _Unwind_Exception *unwind_exception,
+ _Unwind_Context *context);
+
+struct _Unwind_LandingPadContext {
+ // Input information to personality function
+ uintptr_t lpad_index; // landing pad index
+ uintptr_t lsda; // LSDA address
+
+ // Output information computed by personality function
+ uintptr_t selector; // selector value
+};
+
+// Communication channel between compiler-generated user code and personality
+// function
+thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
+
+/// Calls to this function is in landing pads in compiler-generated user code.
+/// In other EH schemes, stack unwinding is done by libunwind library, which
+/// calls the personality function for each each frame it lands. On the other
+/// hand, WebAssembly stack unwinding process is performed by a VM, and the
+/// personality function cannot be called from there. So the compiler inserts
+/// a call to this function in landing pads in the user code, which in turn
+/// calls the personality function.
+_Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
+ struct _Unwind_Exception *exception_object =
+ (struct _Unwind_Exception *)exception_ptr;
+ _LIBUNWIND_TRACE_API("_Unwind_CallPersonality(exception_object=%p)",
+ (void *)exception_object);
+
+ // Reset the selector.
+ __wasm_lpad_context.selector = 0;
+
+ // Call personality function. Wasm does not have two-phase unwinding, so we
+ // only do the cleanup phase.
+ return __gxx_personality_wasm0(
+ 1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
+ (struct _Unwind_Context *)&__wasm_lpad_context);
+}
+
+/// Called by __cxa_throw.
+_LIBUNWIND_EXPORT _Unwind_Reason_Code
+_Unwind_RaiseException(_Unwind_Exception *exception_object) {
+ _LIBUNWIND_TRACE_API("_Unwind_RaiseException(exception_object=%p)",
+ (void *)exception_object);
+ // Use Wasm EH's 'throw' instruction.
+ __builtin_wasm_throw(0, exception_object);
+}
+
+/// Called by __cxa_end_catch.
+_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 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, index=%d, value=%lu)",
+ (void *)context, index, value);
+ // We only use this function to set __wasm_lpad_context.selector field, which
+ // is index 1 in the personality function.
+ if (index == 1)
+ ((struct _Unwind_LandingPadContext *)context)->selector = value;
+}
+
+/// Called by personality handler to get instruction pointer.
+_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) {
+ // The result will be used as an 1-based index after decrementing 1, so we
+ // increment 2 here
+ uintptr_t result =
+ ((struct _Unwind_LandingPadContext *)context)->lpad_index + 2;
+ _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => %lu", (void *)context,
+ result);
+ return result;
+}
+
+/// Not used in Wasm.
+_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context,
+ uintptr_t value) {}
+
+/// Called by personality handler to get LSDA for current frame.
+_LIBUNWIND_EXPORT uintptr_t
+_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
+ uintptr_t result = ((struct _Unwind_LandingPadContext *)context)->lsda;
+ _LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%lx",
+ (void *)context, result);
+ return result;
+}
+
+/// Not used in Wasm.
+_LIBUNWIND_EXPORT uintptr_t
+_Unwind_GetRegionStart(struct _Unwind_Context *context) {
+ return 0;
+}
+
+#endif // defined(__USING_WASM_EXCEPTIONS__)
lib/libunwind/src/UnwindCursor.hpp
@@ -33,6 +33,8 @@
#if defined(_LIBUNWIND_TARGET_LINUX) && \
(defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_RISCV) || \
defined(_LIBUNWIND_TARGET_S390X))
+#include <errno.h>
+#include <signal.h>
#include <sys/syscall.h>
#include <sys/uio.h>
#include <unistd.h>
@@ -990,6 +992,7 @@ private:
R dummy;
return stepThroughSigReturn(dummy);
}
+ bool isReadableAddr(const pint_t addr) const;
#if defined(_LIBUNWIND_TARGET_AARCH64)
bool setInfoForSigReturn(Registers_arm64 &);
int stepThroughSigReturn(Registers_arm64 &);
@@ -2301,27 +2304,39 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
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<void *>(TBTable));
+ _LIBUNWIND_TRACE_UNWINDING(
+ "%s: Look up traceback table of func=%s at %p, pc=%p, "
+ "SP=%p, saves_lr=%d, stores_bc=%d",
+ __func__, functionName, reinterpret_cast<void *>(TBTable),
+ reinterpret_cast<void *>(pc),
+ reinterpret_cast<void *>(registers.getSP()), TBTable->tb.saves_lr,
+ TBTable->tb.stores_bc);
}
#if defined(__powerpc64__)
- // Instruction to reload TOC register "l r2,40(r1)"
+ // Instruction to reload TOC register "ld 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)"
+ // Instruction to reload TOC register "lwz r2,20(r1)"
const uint32_t loadTOCRegInst = 0x80410014;
const int32_t unwPPCF0Index = UNW_PPC_F0;
const int32_t unwPPCV0Index = UNW_PPC_V0;
#endif
+ // lastStack points to the stack frame of the next routine up.
+ pint_t curStack = static_cast<pint_t>(registers.getSP());
+ pint_t lastStack = *reinterpret_cast<pint_t *>(curStack);
+
+ if (lastStack == 0)
+ return UNW_STEP_END;
+
R newRegisters = registers;
- // lastStack points to the stack frame of the next routine up.
- pint_t lastStack = *(reinterpret_cast<pint_t *>(registers.getSP()));
+ // If backchain is not stored, use the current stack frame.
+ if (!TBTable->tb.stores_bc)
+ lastStack = curStack;
// Return address is the address after call site instruction.
pint_t returnAddress;
@@ -2331,33 +2346,41 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
reinterpret_cast<void *>(lastStack));
sigcontext *sigContext = reinterpret_cast<sigcontext *>(
- reinterpret_cast<char *>(lastStack) + STKMIN);
+ reinterpret_cast<char *>(lastStack) + STKMINALIGN);
returnAddress = sigContext->sc_jmpbuf.jmp_context.iar;
- _LIBUNWIND_TRACE_UNWINDING("From sigContext=%p, returnAddress=%p\n",
- reinterpret_cast<void *>(sigContext),
- reinterpret_cast<void *>(returnAddress));
-
+ bool useSTKMIN = false;
if (returnAddress < 0x10000000) {
- // Try again using STKMINALIGN
+ // Try again using STKMIN.
sigContext = reinterpret_cast<sigcontext *>(
- reinterpret_cast<char *>(lastStack) + STKMINALIGN);
+ reinterpret_cast<char *>(lastStack) + STKMIN);
returnAddress = sigContext->sc_jmpbuf.jmp_context.iar;
if (returnAddress < 0x10000000) {
- _LIBUNWIND_TRACE_UNWINDING("Bad returnAddress=%p\n",
- reinterpret_cast<void *>(returnAddress));
+ _LIBUNWIND_TRACE_UNWINDING("Bad returnAddress=%p from sigcontext=%p",
+ reinterpret_cast<void *>(returnAddress),
+ reinterpret_cast<void *>(sigContext));
return UNW_EBADFRAME;
- } else {
- _LIBUNWIND_TRACE_UNWINDING("Tried again using STKMINALIGN: "
- "sigContext=%p, returnAddress=%p. "
- "Seems to be a valid address\n",
- reinterpret_cast<void *>(sigContext),
- reinterpret_cast<void *>(returnAddress));
}
+ useSTKMIN = true;
}
+ _LIBUNWIND_TRACE_UNWINDING("Returning from a signal handler %s: "
+ "sigContext=%p, returnAddress=%p. "
+ "Seems to be a valid address",
+ useSTKMIN ? "STKMIN" : "STKMINALIGN",
+ reinterpret_cast<void *>(sigContext),
+ reinterpret_cast<void *>(returnAddress));
+
// Restore the condition register from sigcontext.
newRegisters.setCR(sigContext->sc_jmpbuf.jmp_context.cr);
+ // Save the LR in sigcontext for stepping up when the function that
+ // raised the signal is a leaf function. This LR has the return address
+ // to the caller of the leaf function.
+ newRegisters.setLR(sigContext->sc_jmpbuf.jmp_context.lr);
+ _LIBUNWIND_TRACE_UNWINDING(
+ "Save LR=%p from sigcontext",
+ reinterpret_cast<void *>(sigContext->sc_jmpbuf.jmp_context.lr));
+
// Restore GPRs from sigcontext.
for (int i = 0; i < 32; ++i)
newRegisters.setRegister(i, sigContext->sc_jmpbuf.jmp_context.gpr[i]);
@@ -2380,13 +2403,26 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
}
} else {
// Step up a normal frame.
- returnAddress = reinterpret_cast<pint_t *>(lastStack)[2];
- _LIBUNWIND_TRACE_UNWINDING("Extract info from lastStack=%p, "
- "returnAddress=%p\n",
- reinterpret_cast<void *>(lastStack),
- reinterpret_cast<void *>(returnAddress));
- _LIBUNWIND_TRACE_UNWINDING("fpr_regs=%d, gpr_regs=%d, saves_cr=%d\n",
+ if (!TBTable->tb.saves_lr && registers.getLR()) {
+ // This case should only occur if we were called from a signal handler
+ // and the signal occurred in a function that doesn't save the LR.
+ returnAddress = static_cast<pint_t>(registers.getLR());
+ _LIBUNWIND_TRACE_UNWINDING("Use saved LR=%p",
+ reinterpret_cast<void *>(returnAddress));
+ } else {
+ // Otherwise, use the LR value in the stack link area.
+ returnAddress = reinterpret_cast<pint_t *>(lastStack)[2];
+ }
+
+ // Reset LR in the current context.
+ newRegisters.setLR(NULL);
+
+ _LIBUNWIND_TRACE_UNWINDING(
+ "Extract info from lastStack=%p, returnAddress=%p",
+ reinterpret_cast<void *>(lastStack),
+ reinterpret_cast<void *>(returnAddress));
+ _LIBUNWIND_TRACE_UNWINDING("fpr_regs=%d, gpr_regs=%d, saves_cr=%d",
TBTable->tb.fpr_saved, TBTable->tb.gpr_saved,
TBTable->tb.saves_cr);
@@ -2450,7 +2486,7 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
struct vec_ext *vec_ext = reinterpret_cast<struct vec_ext *>(charPtr);
- _LIBUNWIND_TRACE_UNWINDING("vr_saved=%d\n", vec_ext->vr_saved);
+ _LIBUNWIND_TRACE_UNWINDING("vr_saved=%d", vec_ext->vr_saved);
// Restore vector register(s) if saved on the stack.
if (vec_ext->vr_saved) {
@@ -2480,11 +2516,11 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
// Do we need to set the TOC register?
_LIBUNWIND_TRACE_UNWINDING(
- "Current gpr2=%p\n",
+ "Current gpr2=%p",
reinterpret_cast<void *>(newRegisters.getRegister(2)));
if (firstInstruction == loadTOCRegInst) {
_LIBUNWIND_TRACE_UNWINDING(
- "Set gpr2=%p from frame\n",
+ "Set gpr2=%p from frame",
reinterpret_cast<void *>(reinterpret_cast<pint_t *>(lastStack)[5]));
newRegisters.setRegister(2, reinterpret_cast<pint_t *>(lastStack)[5]);
}
@@ -2516,7 +2552,6 @@ int UnwindCursor<A, R>::stepWithTBTable(pint_t pc, tbtable *TBTable,
} else {
isSignalFrame = false;
}
-
return UNW_STEP_SUCCESS;
}
#endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND)
@@ -2668,20 +2703,12 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_arm64 &) {
// [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S
const pint_t pc = static_cast<pint_t>(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<void *>(pc), sizeof instructions};
- long bytesRead =
- syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0);
+ // directly accessing it could cause a SIGSEGV.
+ if (!isReadableAddr(pc))
+ return false;
+ auto *instructions = reinterpret_cast<const uint32_t *>(pc);
// Look for instructions: mov x8, #0x8b; svc #0x0
- if (bytesRead != sizeof instructions || instructions[0] != 0xd2801168 ||
- instructions[1] != 0xd4000001)
+ if (instructions[0] != 0xd2801168 || instructions[1] != 0xd4000001)
return false;
_info = {};
@@ -2730,18 +2757,17 @@ int UnwindCursor<A, R>::stepThroughSigReturn(Registers_arm64 &) {
template <typename A, typename R>
bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_riscv &) {
const pint_t pc = static_cast<pint_t>(getReg(UNW_REG_IP));
- uint32_t instructions[2];
- struct iovec local_iov = {&instructions, sizeof instructions};
- struct iovec remote_iov = {reinterpret_cast<void *>(pc), sizeof instructions};
- long bytesRead =
- syscall(SYS_process_vm_readv, getpid(), &local_iov, 1, &remote_iov, 1, 0);
+ // The PC might contain an invalid address if the unwind info is bad, so
+ // directly accessing it could cause a SIGSEGV.
+ if (!isReadableAddr(pc))
+ return false;
+ const auto *instructions = reinterpret_cast<const uint32_t *>(pc);
// 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)
+ if (instructions[0] != 0x08b00893 || instructions[1] != 0x00000073)
return false;
_info = {};
@@ -2790,13 +2816,11 @@ bool UnwindCursor<A, R>::setInfoForSigReturn(Registers_s390x &) {
// onto the stack.
const pint_t pc = static_cast<pint_t>(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<void *>(pc), sizeof inst};
- long bytesRead = process_vm_readv(getpid(), &local_iov, 1, &remote_iov, 1, 0);
- if (bytesRead == sizeof inst && (inst == 0x0a77 || inst == 0x0aad)) {
+ // directly accessing it could cause a SIGSEGV.
+ if (!isReadableAddr(pc))
+ return false;
+ const auto inst = *reinterpret_cast<const uint16_t *>(pc);
+ if (inst == 0x0a77 || inst == 0x0aad) {
_info = {};
_info.start_ip = pc;
_info.end_ip = pc + 2;
@@ -2942,6 +2966,37 @@ bool UnwindCursor<A, R>::getFunctionName(char *buf, size_t bufLen,
buf, bufLen, offset);
}
+#if defined(_LIBUNWIND_CHECK_LINUX_SIGRETURN)
+template <typename A, typename R>
+bool UnwindCursor<A, R>::isReadableAddr(const pint_t addr) const {
+ // We use SYS_rt_sigprocmask, inspired by Abseil's AddressIsReadable.
+
+ const auto sigsetAddr = reinterpret_cast<sigset_t *>(addr);
+ // We have to check that addr is nullptr because sigprocmask allows that
+ // as an argument without failure.
+ if (!sigsetAddr)
+ return false;
+ const auto saveErrno = errno;
+ // We MUST use a raw syscall here, as wrappers may try to access
+ // sigsetAddr which may cause a SIGSEGV. A raw syscall however is
+ // safe. Additionally, we need to pass the kernel_sigset_size, which is
+ // different from libc sizeof(sigset_t). For the majority of architectures,
+ // it's 64 bits (_NSIG), and libc NSIG is _NSIG + 1.
+ const auto kernelSigsetSize = NSIG / 8;
+ [[maybe_unused]] const int Result = syscall(
+ SYS_rt_sigprocmask, /*how=*/~0, sigsetAddr, nullptr, kernelSigsetSize);
+ // Because our "how" is invalid, this syscall should always fail, and our
+ // errno should always be EINVAL or an EFAULT. This relies on the Linux
+ // kernel to check copy_from_user before checking if the "how" argument is
+ // invalid.
+ assert(Result == -1);
+ assert(errno == EFAULT || errno == EINVAL);
+ const auto readable = errno != EFAULT;
+ errno = saveErrno;
+ return readable;
+}
+#endif
+
#if defined(_LIBUNWIND_USE_CET)
extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) {
AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor;
lib/libunwind/src/UnwindLevel1-gcc-ext.c
@@ -143,7 +143,7 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) {
// Create a mock exception object for force unwinding.
_Unwind_Exception ex;
memset(&ex, '\0', sizeof(ex));
- strcpy((char *)&ex.exception_class, "CLNGUNW");
+ memcpy(&ex.exception_class, "CLNGUNW", sizeof(ex.exception_class));
#endif
// walk each frame
lib/libunwind/src/UnwindRegistersRestore.S
@@ -673,7 +673,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
ldr d30, [x0, #0x200]
ldr d31, [x0, #0x208]
- // Finally, restore sp. This must be done after the the last read from the
+ // Finally, restore sp. This must be done after the last read from the
// context struct, because it is allocated on the stack, and an exception
// could clobber the de-allocated portion of the stack after sp has been
// restored.
@@ -993,11 +993,13 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind18Registers_mips_o326jumptoEv)
ldc1 $f31, (4 * 36 + 8 * 31)($4)
#endif
#endif
+#if __mips_isa_rev < 6
// restore hi and lo
lw $8, (4 * 33)($4)
mthi $8
lw $8, (4 * 34)($4)
mtlo $8
+#endif
// r0 is zero
lw $1, (4 * 1)($4)
lw $2, (4 * 2)($4)
@@ -1054,11 +1056,13 @@ DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind21Registers_mips_newabi6jumptoEv)
ldc1 $f\i, (280+8*\i)($4)
.endr
#endif
+#if __mips_isa_rev < 6
// restore hi and lo
ld $8, (8 * 33)($4)
mthi $8
ld $8, (8 * 34)($4)
mtlo $8
+#endif
// r0 is zero
ld $1, (8 * 1)($4)
ld $2, (8 * 2)($4)
lib/libunwind/src/UnwindRegistersSave.S
@@ -174,11 +174,13 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
sw $31, (4 * 31)($4)
# Store return address to pc
sw $31, (4 * 32)($4)
+#if __mips_isa_rev < 6
# hi and lo
mfhi $8
sw $8, (4 * 33)($4)
mflo $8
sw $8, (4 * 34)($4)
+#endif
#ifdef __mips_hard_float
#if __mips_fpr != 64
sdc1 $f0, (4 * 36 + 8 * 0)($4)
@@ -255,11 +257,13 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
.endr
# Store return address to pc
sd $31, (8 * 32)($4)
+#if __mips_isa_rev < 6
# hi and lo
mfhi $8
sd $8, (8 * 33)($4)
mflo $8
sd $8, (8 * 34)($4)
+#endif
#ifdef __mips_hard_float
.irp i,FROM_0_TO_31
sdc1 $f\i, (280+8*\i)($4)
@@ -301,9 +305,21 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
mflr 0
std 0, PPC64_OFFS_SRR0(3) // store lr as ssr0
PPC64_STR(1)
+ PPC64_STR(4) // Save r4 first since it will be used for fixing r2.
+#if defined(_AIX)
+ // The TOC register (r2) was changed by the glue code if unw_getcontext
+ // is called from a different module. Save the original TOC register
+ // in the context if this is the case.
+ mflr 4
+ lwz 4, 0(4) // Get the first instruction at the return address.
+ xoris 0, 4, 0xe841 // Is it reloading the TOC register "ld 2,40(1)"?
+ cmplwi 0, 0x28
+ bne 0, LnoR2Fix // No need to fix up r2 if it is not.
+ ld 2, 40(1) // Use the saved TOC register in the stack.
+LnoR2Fix:
+#endif
PPC64_STR(2)
PPC64_STR(3)
- PPC64_STR(4)
PPC64_STR(5)
PPC64_STR(6)
PPC64_STR(7)
@@ -336,7 +352,12 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
std 0, PPC64_OFFS_CR(3)
mfxer 0
std 0, PPC64_OFFS_XER(3)
+#if defined(_AIX)
+ // LR value saved from the register is not used, initialize it to 0.
+ li 0, 0
+#else
mflr 0
+#endif
std 0, PPC64_OFFS_LR(3)
mfctr 0
std 0, PPC64_OFFS_CTR(3)
@@ -543,9 +564,21 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
mflr 0
stw 0, 0(3) // store lr as ssr0
stw 1, 12(3)
+ stw 4, 24(3) // Save r4 first since it will be used for fixing r2.
+#if defined(_AIX)
+ // The TOC register (r2) was changed by the glue code if unw_getcontext
+ // is called from a different module. Save the original TOC register
+ // in the context if this is the case.
+ mflr 4
+ lwz 4, 0(4) // Get the instruction at the return address.
+ xoris 0, 4, 0x8041 // Is it reloading the TOC register "lwz 2,20(1)"?
+ cmplwi 0, 0x14
+ bne 0, LnoR2Fix // No need to fix up r2 if it is not.
+ lwz 2, 20(1) // Use the saved TOC register in the stack.
+LnoR2Fix:
+#endif
stw 2, 16(3)
stw 3, 20(3)
- stw 4, 24(3)
stw 5, 28(3)
stw 6, 32(3)
stw 7, 36(3)
@@ -582,6 +615,11 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
// save CR registers
mfcr 0
stw 0, 136(3)
+#if defined(_AIX)
+ // LR value from the register is not used, initialize it to 0.
+ li 0, 0
+ stw 0, 144(3)
+#endif
// save CTR register
mfctr 0
stw 0, 148(3)
@@ -742,7 +780,7 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
@
@ On entry:
@ thread_state pointer is in r0
-@
+@
@ Per EHABI #4.7 this only saves the core integer registers.
@ EHABI #7.4.5 notes that in general all VRS registers should be restored
@ however this is very hard to do for VFP registers because it is unknown
src/libunwind.zig
@@ -164,6 +164,7 @@ const unwind_src_list = [_][]const u8{
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c",
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c",
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c",
+ "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-wasm.c",
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S",
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S",
"libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind_AIXExtras.cpp",