llvm-project
508 строк · 21.5 Кб
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Implements SEH-based Itanium C++ exceptions.
10//
11//===----------------------------------------------------------------------===//
12
13#include "config.h"14
15#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)16
17#include <unwind.h>18
19#include <stdint.h>20#include <stdbool.h>21#include <stdlib.h>22
23#include <windef.h>24#include <excpt.h>25#include <winnt.h>26#include <ntstatus.h>27
28#include "libunwind_ext.h"29#include "UnwindCursor.hpp"30
31using namespace libunwind;32
33#define STATUS_USER_DEFINED (1u << 29)34
35#define STATUS_GCC_MAGIC (('G' << 16) | ('C' << 8) | 'C')36
37#define MAKE_CUSTOM_STATUS(s, c) \38((NTSTATUS)(((s) << 30) | STATUS_USER_DEFINED | (c)))39#define MAKE_GCC_EXCEPTION(c) \40MAKE_CUSTOM_STATUS(STATUS_SEVERITY_SUCCESS, STATUS_GCC_MAGIC | ((c) << 24))41
42/// SEH exception raised by libunwind when the program calls
43/// \c _Unwind_RaiseException.
44#define STATUS_GCC_THROW MAKE_GCC_EXCEPTION(0) // 0x2047434345/// SEH exception raised by libunwind to initiate phase 2 of exception
46/// handling.
47#define STATUS_GCC_UNWIND MAKE_GCC_EXCEPTION(1) // 0x2147434348
49static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *ctx);50static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor);51static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor,52DISPATCHER_CONTEXT *disp);53
54/// Common implementation of SEH-style handler functions used by Itanium-
55/// style frames. Depending on how and why it was called, it may do one of:
56/// a) Delegate to the given Itanium-style personality function; or
57/// b) Initiate a collided unwind to halt unwinding.
58_LIBUNWIND_EXPORT EXCEPTION_DISPOSITION
59_GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx,60DISPATCHER_CONTEXT *disp, _Unwind_Personality_Fn pers) {61unw_cursor_t cursor;62_Unwind_Exception *exc;63_Unwind_Action action;64struct _Unwind_Context *ctx = nullptr;65_Unwind_Reason_Code urc;66uintptr_t retval, target;67bool ours = false;68
69_LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler(%#010lx(%lx), %p)",70ms_exc->ExceptionCode, ms_exc->ExceptionFlags,71(void *)frame);72if (ms_exc->ExceptionCode == STATUS_GCC_UNWIND) {73if (IS_TARGET_UNWIND(ms_exc->ExceptionFlags)) {74// Set up the upper return value (the lower one and the target PC75// were set in the call to RtlUnwindEx()) for the landing pad.76#ifdef __x86_64__77disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3];78#elif defined(__arm__)79disp->ContextRecord->R1 = ms_exc->ExceptionInformation[3];80#elif defined(__aarch64__)81disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3];82#endif83}84// This is the collided unwind to the landing pad. Nothing to do.85return ExceptionContinueSearch;86}87
88if (ms_exc->ExceptionCode == STATUS_GCC_THROW) {89// This is (probably) a libunwind-controlled exception/unwind. Recover the90// parameters which we set below, and pass them to the personality function.91ours = true;92exc = (_Unwind_Exception *)ms_exc->ExceptionInformation[0];93if (!IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) {94ctx = (struct _Unwind_Context *)ms_exc->ExceptionInformation[1];95action = (_Unwind_Action)ms_exc->ExceptionInformation[2];96}97} else {98// Foreign exception.99// We can't interact with them (we don't know the original target frame100// that we should pass on to RtlUnwindEx in _Unwind_Resume), so just101// pass without calling our destructors here.102return ExceptionContinueSearch;103}104if (!ctx) {105__unw_init_seh(&cursor, disp->ContextRecord);106__unw_seh_set_disp_ctx(&cursor, disp);107__unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc);108ctx = (struct _Unwind_Context *)&cursor;109
110if (!IS_UNWINDING(ms_exc->ExceptionFlags)) {111if (ours && ms_exc->NumberParameters > 1)112action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_FORCE_UNWIND);113else114action = _UA_SEARCH_PHASE;115} else {116if (ours && ms_exc->ExceptionInformation[1] == (ULONG_PTR)frame)117action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME);118else119action = _UA_CLEANUP_PHASE;120}121}122
123_LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() calling personality "124"function %p(1, %d, %llx, %p, %p)",125(void *)pers, action, exc->exception_class,126(void *)exc, (void *)ctx);127urc = pers(1, action, exc->exception_class, exc, ctx);128_LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() personality returned %d", urc);129switch (urc) {130case _URC_CONTINUE_UNWIND:131// If we're in phase 2, and the personality routine said to continue132// at the target frame, we're in real trouble.133if (action & _UA_HANDLER_FRAME)134_LIBUNWIND_ABORT("Personality continued unwind at the target frame!");135return ExceptionContinueSearch;136case _URC_HANDLER_FOUND:137// If we were called by __libunwind_seh_personality(), indicate that138// a handler was found; otherwise, initiate phase 2 by unwinding.139if (ours && ms_exc->NumberParameters > 1)140return 4 /* ExceptionExecuteHandler in mingw */;141// This should never happen in phase 2.142if (IS_UNWINDING(ms_exc->ExceptionFlags))143_LIBUNWIND_ABORT("Personality indicated exception handler in phase 2!");144exc->private_[1] = (ULONG_PTR)frame;145if (ours) {146ms_exc->NumberParameters = 4;147ms_exc->ExceptionInformation[1] = (ULONG_PTR)frame;148}149// FIXME: Indicate target frame in foreign case!150// phase 2: the clean up phase151RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, ms_ctx, disp->HistoryTable);152_LIBUNWIND_ABORT("RtlUnwindEx() failed");153case _URC_INSTALL_CONTEXT: {154// If we were called by __libunwind_seh_personality(), indicate that155// a handler was found; otherwise, it's time to initiate a collided156// unwind to the target.157if (ours && !IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1)158return 4 /* ExceptionExecuteHandler in mingw */;159// This should never happen in phase 1.160if (!IS_UNWINDING(ms_exc->ExceptionFlags))161_LIBUNWIND_ABORT("Personality installed context during phase 1!");162#ifdef __x86_64__163exc->private_[2] = disp->TargetIp;164__unw_get_reg(&cursor, UNW_X86_64_RAX, &retval);165__unw_get_reg(&cursor, UNW_X86_64_RDX, &exc->private_[3]);166#elif defined(__arm__)167exc->private_[2] = disp->TargetPc;168__unw_get_reg(&cursor, UNW_ARM_R0, &retval);169__unw_get_reg(&cursor, UNW_ARM_R1, &exc->private_[3]);170#elif defined(__aarch64__)171exc->private_[2] = disp->TargetPc;172__unw_get_reg(&cursor, UNW_AARCH64_X0, &retval);173__unw_get_reg(&cursor, UNW_AARCH64_X1, &exc->private_[3]);174#endif175__unw_get_reg(&cursor, UNW_REG_IP, &target);176ms_exc->ExceptionCode = STATUS_GCC_UNWIND;177#ifdef __x86_64__178ms_exc->ExceptionInformation[2] = disp->TargetIp;179#elif defined(__arm__) || defined(__aarch64__)180ms_exc->ExceptionInformation[2] = disp->TargetPc;181#endif182ms_exc->ExceptionInformation[3] = exc->private_[3];183// Give NTRTL some scratch space to keep track of the collided unwind.184// Don't use the one that was passed in; we don't want to overwrite the185// context in the DISPATCHER_CONTEXT.186CONTEXT new_ctx;187RtlUnwindEx(frame, (PVOID)target, ms_exc, (PVOID)retval, &new_ctx, disp->HistoryTable);188_LIBUNWIND_ABORT("RtlUnwindEx() failed");189}190// Anything else indicates a serious problem.191default: return ExceptionContinueExecution;192}193}
194
195/// Personality function returned by \c __unw_get_proc_info() in SEH contexts.
196/// This is a wrapper that calls the real SEH handler function, which in
197/// turn (at least, for Itanium-style frames) calls the real Itanium
198/// personality function (see \c _GCC_specific_handler()).
199extern "C" _Unwind_Reason_Code200__libunwind_seh_personality(int version, _Unwind_Action state,201uint64_t klass, _Unwind_Exception *exc,202struct _Unwind_Context *context) {203(void)version;204(void)klass;205EXCEPTION_RECORD ms_exc;206bool phase2 = (state & (_UA_SEARCH_PHASE|_UA_CLEANUP_PHASE)) == _UA_CLEANUP_PHASE;207ms_exc.ExceptionCode = STATUS_GCC_THROW;208ms_exc.ExceptionFlags = 0;209ms_exc.NumberParameters = 3;210ms_exc.ExceptionInformation[0] = (ULONG_PTR)exc;211ms_exc.ExceptionInformation[1] = (ULONG_PTR)context;212ms_exc.ExceptionInformation[2] = state;213DISPATCHER_CONTEXT *disp_ctx =214__unw_seh_get_disp_ctx((unw_cursor_t *)context);215_LIBUNWIND_TRACE_UNWINDING("__libunwind_seh_personality() calling "216"LanguageHandler %p(%p, %p, %p, %p)",217(void *)disp_ctx->LanguageHandler, (void *)&ms_exc,218(void *)disp_ctx->EstablisherFrame,219(void *)disp_ctx->ContextRecord, (void *)disp_ctx);220EXCEPTION_DISPOSITION ms_act = disp_ctx->LanguageHandler(&ms_exc,221(PVOID)disp_ctx->EstablisherFrame,222disp_ctx->ContextRecord,223disp_ctx);224_LIBUNWIND_TRACE_UNWINDING("__libunwind_seh_personality() LanguageHandler "225"returned %d",226(int)ms_act);227switch (ms_act) {228case ExceptionContinueExecution: return _URC_END_OF_STACK;229case ExceptionContinueSearch: return _URC_CONTINUE_UNWIND;230case 4 /*ExceptionExecuteHandler*/:231return phase2 ? _URC_INSTALL_CONTEXT : _URC_HANDLER_FOUND;232default:233return phase2 ? _URC_FATAL_PHASE2_ERROR : _URC_FATAL_PHASE1_ERROR;234}235}
236
237static _Unwind_Reason_Code238unwind_phase2_forced(unw_context_t *uc,239_Unwind_Exception *exception_object,240_Unwind_Stop_Fn stop, void *stop_parameter) {241unw_cursor_t cursor2;242__unw_init_local(&cursor2, uc);243
244// Walk each frame until we reach where search phase said to stop245while (__unw_step(&cursor2) > 0) {246
247// Update info about this frame.248unw_proc_info_t frameInfo;249if (__unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) {250_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_get_proc_info "251"failed => _URC_END_OF_STACK",252(void *)exception_object);253return _URC_FATAL_PHASE2_ERROR;254}255
256#ifndef NDEBUG257// When tracing, print state information.258if (_LIBUNWIND_TRACING_UNWINDING) {259char functionBuf[512];260const char *functionName = functionBuf;261unw_word_t offset;262if ((__unw_get_proc_name(&cursor2, functionBuf, sizeof(functionBuf),263&offset) != UNW_ESUCCESS) ||264(frameInfo.start_ip + offset > frameInfo.end_ip))265functionName = ".anonymous.";266_LIBUNWIND_TRACE_UNWINDING(267"unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR268", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR,269(void *)exception_object, frameInfo.start_ip, functionName,270frameInfo.lsda, frameInfo.handler);271}272#endif273
274// Call stop function at each frame.275_Unwind_Action action =276(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);277_Unwind_Reason_Code stopResult =278(*stop)(1, action, exception_object->exception_class, exception_object,279(struct _Unwind_Context *)(&cursor2), stop_parameter);280_LIBUNWIND_TRACE_UNWINDING(281"unwind_phase2_forced(ex_ojb=%p): stop function returned %d",282(void *)exception_object, stopResult);283if (stopResult != _URC_NO_REASON) {284_LIBUNWIND_TRACE_UNWINDING(285"unwind_phase2_forced(ex_ojb=%p): stopped by stop function",286(void *)exception_object);287return _URC_FATAL_PHASE2_ERROR;288}289
290// If there is a personality routine, tell it we are unwinding.291if (frameInfo.handler != 0) {292_Unwind_Personality_Fn p =293(_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler);294_LIBUNWIND_TRACE_UNWINDING(295"unwind_phase2_forced(ex_ojb=%p): calling personality function %p",296(void *)exception_object, (void *)(uintptr_t)p);297_Unwind_Reason_Code personalityResult =298(*p)(1, action, exception_object->exception_class, exception_object,299(struct _Unwind_Context *)(&cursor2));300switch (personalityResult) {301case _URC_CONTINUE_UNWIND:302_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "303"personality returned "304"_URC_CONTINUE_UNWIND",305(void *)exception_object);306// Destructors called, continue unwinding307break;308case _URC_INSTALL_CONTEXT:309_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "310"personality returned "311"_URC_INSTALL_CONTEXT",312(void *)exception_object);313// We may get control back if landing pad calls _Unwind_Resume().314__unw_resume(&cursor2);315break;316case _URC_END_OF_STACK:317_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "318"personality returned "319"_URC_END_OF_STACK",320(void *)exception_object);321break;322default:323// Personality routine returned an unknown result code.324_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "325"personality returned %d, "326"_URC_FATAL_PHASE2_ERROR",327(void *)exception_object, personalityResult);328return _URC_FATAL_PHASE2_ERROR;329}330if (personalityResult == _URC_END_OF_STACK)331break;332}333}334
335// Call stop function one last time and tell it we've reached the end336// of the stack.337_LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "338"function with _UA_END_OF_STACK",339(void *)exception_object);340_Unwind_Action lastAction =341(_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);342(*stop)(1, lastAction, exception_object->exception_class, exception_object,343(struct _Unwind_Context *)(&cursor2), stop_parameter);344
345// Clean up phase did not resume at the frame that the search phase said it346// would.347return _URC_FATAL_PHASE2_ERROR;348}
349
350/// Called by \c __cxa_throw(). Only returns if there is a fatal error.
351_LIBUNWIND_EXPORT _Unwind_Reason_Code
352_Unwind_RaiseException(_Unwind_Exception *exception_object) {353_LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)",354(void *)exception_object);355
356// Mark that this is a non-forced unwind, so _Unwind_Resume()357// can do the right thing.358memset(exception_object->private_, 0, sizeof(exception_object->private_));359
360// phase 1: the search phase361// We'll let the system do that for us.362RaiseException(STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exception_object);363
364// If we get here, either something went horribly wrong or we reached the365// top of the stack. Either way, let libc++abi call std::terminate().366return _URC_END_OF_STACK;367}
368
369/// When \c _Unwind_RaiseException() is in phase2, it hands control
370/// to the personality function at each frame. The personality
371/// may force a jump to a landing pad in that function; the landing
372/// pad code may then call \c _Unwind_Resume() to continue with the
373/// unwinding. Note: the call to \c _Unwind_Resume() is from compiler
374/// generated user code. All other \c _Unwind_* routines are called
375/// by the C++ runtime \c __cxa_* routines.
376///
377/// Note: re-throwing an exception (as opposed to continuing the unwind)
378/// is implemented by having the code call \c __cxa_rethrow() which
379/// in turn calls \c _Unwind_Resume_or_Rethrow().
380_LIBUNWIND_EXPORT void381_Unwind_Resume(_Unwind_Exception *exception_object) {382_LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object);383
384if (exception_object->private_[0] != 0) {385unw_context_t uc;386
387__unw_getcontext(&uc);388unwind_phase2_forced(&uc, exception_object,389(_Unwind_Stop_Fn) exception_object->private_[0],390(void *)exception_object->private_[4]);391} else {392// Recover the parameters for the unwind from the exception object393// so we can start unwinding again.394EXCEPTION_RECORD ms_exc;395CONTEXT ms_ctx;396UNWIND_HISTORY_TABLE hist;397
398memset(&ms_exc, 0, sizeof(ms_exc));399memset(&hist, 0, sizeof(hist));400ms_exc.ExceptionCode = STATUS_GCC_THROW;401ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE;402ms_exc.NumberParameters = 4;403ms_exc.ExceptionInformation[0] = (ULONG_PTR)exception_object;404ms_exc.ExceptionInformation[1] = exception_object->private_[1];405ms_exc.ExceptionInformation[2] = exception_object->private_[2];406ms_exc.ExceptionInformation[3] = exception_object->private_[3];407RtlUnwindEx((PVOID)exception_object->private_[1],408(PVOID)exception_object->private_[2], &ms_exc,409exception_object, &ms_ctx, &hist);410}411
412// Clients assume _Unwind_Resume() does not return, so all we can do is abort.413_LIBUNWIND_ABORT("_Unwind_Resume() can't return");414}
415
416/// Not used by C++.
417/// Unwinds stack, calling "stop" function at each frame.
418/// Could be used to implement \c longjmp().
419_LIBUNWIND_EXPORT _Unwind_Reason_Code
420_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,421_Unwind_Stop_Fn stop, void *stop_parameter) {422_LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)",423(void *)exception_object, (void *)(uintptr_t)stop);424unw_context_t uc;425__unw_getcontext(&uc);426
427// Mark that this is a forced unwind, so _Unwind_Resume() can do428// the right thing.429exception_object->private_[0] = (uintptr_t) stop;430exception_object->private_[4] = (uintptr_t) stop_parameter;431
432// do it433return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter);434}
435
436/// Called by personality handler during phase 2 to get LSDA for current frame.
437_LIBUNWIND_EXPORT uintptr_t
438_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {439uintptr_t result =440(uintptr_t)__unw_seh_get_disp_ctx((unw_cursor_t *)context)->HandlerData;441_LIBUNWIND_TRACE_API(442"_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR,443(void *)context, result);444return result;445}
446
447/// Called by personality handler during phase 2 to find the start of the
448/// function.
449_LIBUNWIND_EXPORT uintptr_t
450_Unwind_GetRegionStart(struct _Unwind_Context *context) {451DISPATCHER_CONTEXT *disp = __unw_seh_get_disp_ctx((unw_cursor_t *)context);452uintptr_t result = (uintptr_t)disp->FunctionEntry->BeginAddress + disp->ImageBase;453_LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR,454(void *)context, result);455return result;456}
457
458static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *context) {459#ifdef _LIBUNWIND_TARGET_X86_64460new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor))461UnwindCursor<LocalAddressSpace, Registers_x86_64>(462context, LocalAddressSpace::sThisAddressSpace);463auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);464co->setInfoBasedOnIPRegister();465return UNW_ESUCCESS;466#elif defined(_LIBUNWIND_TARGET_ARM)467new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor))468UnwindCursor<LocalAddressSpace, Registers_arm>(469context, LocalAddressSpace::sThisAddressSpace);470auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);471co->setInfoBasedOnIPRegister();472return UNW_ESUCCESS;473#elif defined(_LIBUNWIND_TARGET_AARCH64)474new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor))475UnwindCursor<LocalAddressSpace, Registers_arm64>(476context, LocalAddressSpace::sThisAddressSpace);477auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);478co->setInfoBasedOnIPRegister();479return UNW_ESUCCESS;480#else481return UNW_EINVAL;482#endif483}
484
485static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor) {486#ifdef _LIBUNWIND_TARGET_X86_64487return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->getDispatcherContext();488#elif defined(_LIBUNWIND_TARGET_ARM)489return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->getDispatcherContext();490#elif defined(_LIBUNWIND_TARGET_AARCH64)491return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->getDispatcherContext();492#else493return nullptr;494#endif495}
496
497static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor,498DISPATCHER_CONTEXT *disp) {499#ifdef _LIBUNWIND_TARGET_X86_64500reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->setDispatcherContext(disp);501#elif defined(_LIBUNWIND_TARGET_ARM)502reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->setDispatcherContext(disp);503#elif defined(_LIBUNWIND_TARGET_AARCH64)504reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->setDispatcherContext(disp);505#endif506}
507
508#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)509