diff options
Diffstat (limited to 'src/pal/src/exception/seh-unwind.cpp')
-rw-r--r-- | src/pal/src/exception/seh-unwind.cpp | 231 |
1 files changed, 228 insertions, 3 deletions
diff --git a/src/pal/src/exception/seh-unwind.cpp b/src/pal/src/exception/seh-unwind.cpp index 465c459dda..cf899f7f7f 100644 --- a/src/pal/src/exception/seh-unwind.cpp +++ b/src/pal/src/exception/seh-unwind.cpp @@ -26,12 +26,20 @@ Abstract: #endif // !FEATURE_PAL_SXS #include "pal/context.h" +#include "pal.h" #include <dlfcn.h> #include <exception> + #if HAVE_LIBUNWIND_H +#ifndef __LINUX__ #define UNW_LOCAL_ONLY +#endif // !__LINUX__ #include <libunwind.h> -#endif +#ifdef __LINUX__ +#include <libunwind-ptrace.h> +#endif // __LINUX__ +#endif // HAVE_LIBUNWIND_H + //---------------------------------------------------------------------- // Virtual Unwinding @@ -226,6 +234,7 @@ BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextP int st; unw_context_t unwContext; unw_cursor_t cursor; + #if defined(__APPLE__) || defined(__FreeBSD__) || defined(_ARM64_) DWORD64 curPc; #endif @@ -251,7 +260,6 @@ BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextP return FALSE; } #endif - st = unw_init_local(&cursor, &unwContext); if (st < 0) { @@ -308,13 +316,230 @@ BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextP { GetContextPointers(&cursor, &unwContext, contextPointers); } - return TRUE; } + #else #error don't know how to unwind on this platform #endif + +#ifdef __LINUX__ + + +static struct LibunwindCallbacksInfoType +{ + CONTEXT *Context; + ReadMemoryWordCallback readMemCallback; +} LibunwindCallbacksInfo; + +static int get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dilap, void *arg) +{ + return -UNW_ENOINFO; +} + +static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg) +{ + if (write) + { + ASSERT("Memory write must never be called by libunwind during stackwalk"); + return -UNW_EINVAL; + } + + // access_mem sometimes gets called by _UPT_find_proc_info, in such cases arg has a pointer to libunwind internal data + // returned by _UPT_create. It makes it impossible to use arg for passing readMemCallback. That's why we have to use global variable. + if (LibunwindCallbacksInfo.readMemCallback((SIZE_T)addr, (SIZE_T *)valp)) + { + return UNW_ESUCCESS; + } + else + { + return -UNW_EUNSPEC; + } +} + +static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg) +{ + if (write) + { + ASSERT("Register write must never be called by libunwind during stackwalk"); + return -UNW_EREADONLYREG; + } + + CONTEXT *winContext = LibunwindCallbacksInfo.Context; + + switch (regnum) + { +#if defined(_AMD64_) + case UNW_REG_IP: *valp = (unw_word_t) winContext->Rip; break; + case UNW_REG_SP: *valp = (unw_word_t) winContext->Rsp; break; + case UNW_X86_64_RBP: *valp = (unw_word_t) winContext->Rbp; break; + case UNW_X86_64_RBX: *valp = (unw_word_t) winContext->Rbx; break; + case UNW_X86_64_R12: *valp = (unw_word_t) winContext->R12; break; + case UNW_X86_64_R13: *valp = (unw_word_t) winContext->R13; break; + case UNW_X86_64_R14: *valp = (unw_word_t) winContext->R14; break; + case UNW_X86_64_R15: *valp = (unw_word_t) winContext->R15; break; +#elif defined(_ARM_) + case UNW_ARM_R13: *valp = (unw_word_t) winContext->Sp; break; + case UNW_ARM_R14: *valp = (unw_word_t) winContext->Lr; break; + case UNW_ARM_R15: *valp = (unw_word_t) winContext->Pc; break; + case UNW_ARM_R4: *valp = (unw_word_t) winContext->R4; break; + case UNW_ARM_R5: *valp = (unw_word_t) winContext->R5; break; + case UNW_ARM_R6: *valp = (unw_word_t) winContext->R6; break; + case UNW_ARM_R7: *valp = (unw_word_t) winContext->R7; break; + case UNW_ARM_R8: *valp = (unw_word_t) winContext->R8; break; + case UNW_ARM_R9: *valp = (unw_word_t) winContext->R9; break; + case UNW_ARM_R10: *valp = (unw_word_t) winContext->R10; break; + case UNW_ARM_R11: *valp = (unw_word_t) winContext->R11; break; +#elif defined(_ARM64_) + case UNW_REG_IP: *valp = (unw_word_t) winContext->Pc; break; + case UNW_REG_SP: *valp = (unw_word_t) winContext->Sp; break; + case UNW_AARCH64_X29: *valp = (unw_word_t) winContext->Fp; break; + case UNW_AARCH64_X30: *valp = (unw_word_t) winContext->Lr; break; + case UNW_AARCH64_X19: *valp = (unw_word_t) winContext->X19; break; + case UNW_AARCH64_X20: *valp = (unw_word_t) winContext->X20; break; + case UNW_AARCH64_X21: *valp = (unw_word_t) winContext->X21; break; + case UNW_AARCH64_X22: *valp = (unw_word_t) winContext->X22; break; + case UNW_AARCH64_X23: *valp = (unw_word_t) winContext->X23; break; + case UNW_AARCH64_X24: *valp = (unw_word_t) winContext->X24; break; + case UNW_AARCH64_X25: *valp = (unw_word_t) winContext->X25; break; + case UNW_AARCH64_X26: *valp = (unw_word_t) winContext->X26; break; + case UNW_AARCH64_X27: *valp = (unw_word_t) winContext->X27; break; + case UNW_AARCH64_X28: *valp = (unw_word_t) winContext->X28; break; +#else +#error unsupported architecture +#endif + default: + ASSERT("Attempt to read an unknown register."); + return -UNW_EBADREG; + } + return UNW_ESUCCESS; +} + +static int access_fpreg(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *fpvalp, int write, void *arg) +{ + ASSERT("Not supposed to be ever called"); + return -UNW_EINVAL; +} + +static int resume(unw_addr_space_t as, unw_cursor_t *cp, void *arg) +{ + ASSERT("Not supposed to be ever called"); + return -UNW_EINVAL; +} + +static int get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg) +{ + ASSERT("Not supposed to be ever called"); + return -UNW_EINVAL; +} + +static unw_accessors_t unwind_accessors = +{ + .find_proc_info = _UPT_find_proc_info, + .put_unwind_info = _UPT_put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name +}; + +BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context, + KNONVOLATILE_CONTEXT_POINTERS *contextPointers, + DWORD pid, + ReadMemoryWordCallback readMemCallback) +{ + // This function can be executed only by one thread at a time. + // The reason for this is that we need to pass context and read mem function to libunwind callbacks + // but "arg" is already used by the pointer returned from _UPT_create(). + // So we resort to using global variables and a lock. + struct Lock + { + CRITICAL_SECTION cs; + Lock() + { + // ctor of a static variable is a thread-safe way to initialize critical section exactly once (clang,gcc) + InitializeCriticalSection(&cs); + } + }; + struct LockHolder + { + CRITICAL_SECTION *cs; + LockHolder(CRITICAL_SECTION *cs) + { + this->cs = cs; + EnterCriticalSection(cs); + } + + ~LockHolder() + { + LeaveCriticalSection(cs); + cs = NULL; + } + }; + static Lock lock; + LockHolder lockHolder(&lock.cs); + + int st; + unw_context_t unwContext; + unw_cursor_t cursor; + unw_addr_space_t addrSpace = 0; + void *libunwindUptPtr = NULL; + BOOL result = FALSE; + + LibunwindCallbacksInfo.Context = context; + LibunwindCallbacksInfo.readMemCallback = readMemCallback; + WinContextToUnwindContext(context, &unwContext); + addrSpace = unw_create_addr_space(&unwind_accessors, 0); + libunwindUptPtr = _UPT_create(pid); + st = unw_init_remote(&cursor, addrSpace, libunwindUptPtr); + if (st < 0) + { + result = FALSE; + goto Exit; + } + + st = unw_step(&cursor); + if (st < 0) + { + result = FALSE; + goto Exit; + } + + UnwindContextToWinContext(&cursor, context); + + if (contextPointers != NULL) + { + GetContextPointers(&cursor, &unwContext, contextPointers); + } + result = TRUE; + +Exit: + if (libunwindUptPtr != nullptr) + { + _UPT_destroy(libunwindUptPtr); + } + if (addrSpace != 0) + { + unw_destroy_addr_space(addrSpace); + } + return result; +} +#else // __LINUX__ + +BOOL PAL_VirtualUnwindOutOfProc(CONTEXT *context, + KNONVOLATILE_CONTEXT_POINTERS *contextPointers, + DWORD pid, + ReadMemoryWordCallback readMemCallback) +{ + //UNIXTODO: Implement for Mac flavor of libunwind + return FALSE; +} + +#endif // !__LINUX__ + /*++ Function: RtlpRaiseException |