| 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 | |
| 31 | using 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) \ |
| 40 | MAKE_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) // 0x20474343 |
| 45 | /// SEH exception raised by libunwind to initiate phase 2 of exception |
| 46 | /// handling. |
| 47 | #define STATUS_GCC_UNWIND MAKE_GCC_EXCEPTION(1) // 0x21474343 |
| 48 | |
| 49 | static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *ctx); |
| 50 | static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor); |
| 51 | static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor, |
| 52 | DISPATCHER_CONTEXT *disp); |
| 53 | |
| 54 | #pragma clang diagnostic push |
| 55 | #pragma clang diagnostic ignored "-Wgnu-anonymous-struct" |
| 56 | // Local redefinition of this type; mingw-w64 headers lack the |
| 57 | // DISPATCHER_CONTEXT_NONVOLREG_ARM64 type as of May 2025, so locally redefine |
| 58 | // it and use that definition, to avoid needing to test/guess whether the real |
| 59 | // type is available of not. |
| 60 | union LOCAL_DISPATCHER_CONTEXT_NONVOLREG_ARM64 { |
| 61 | BYTE Buffer[11 * sizeof(DWORD64) + 8 * sizeof(double)]; |
| 62 | |
| 63 | struct { |
| 64 | DWORD64 GpNvRegs[11]; |
| 65 | double FpNvRegs[8]; |
| 66 | }; |
| 67 | }; |
| 68 | |
| 69 | // Custom data type definition; this type is not defined in WinSDK. |
| 70 | union LOCAL_DISPATCHER_CONTEXT_NONVOLREG_ARM { |
| 71 | BYTE Buffer[8 * sizeof(DWORD) + 8 * sizeof(double)]; |
| 72 | |
| 73 | struct { |
| 74 | DWORD GpNvRegs[8]; |
| 75 | double FpNvRegs[8]; |
| 76 | }; |
| 77 | }; |
| 78 | #pragma clang diagnostic pop |
| 79 | |
| 80 | /// Common implementation of SEH-style handler functions used by Itanium- |
| 81 | /// style frames. Depending on how and why it was called, it may do one of: |
| 82 | /// a) Delegate to the given Itanium-style personality function; or |
| 83 | /// b) Initiate a collided unwind to halt unwinding. |
| 84 | _LIBUNWIND_EXPORT EXCEPTION_DISPOSITION |
| 85 | _GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx, |
| 86 | DISPATCHER_CONTEXT *disp, _Unwind_Personality_Fn pers) { |
| 87 | unw_cursor_t cursor; |
| 88 | _Unwind_Exception *exc; |
| 89 | _Unwind_Action action; |
| 90 | struct _Unwind_Context *ctx = nullptr; |
| 91 | _Unwind_Reason_Code urc; |
| 92 | uintptr_t retval, target; |
| 93 | bool ours = false; |
| 94 | |
| 95 | _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler(%#010lx(%lx), %p)" , |
| 96 | ms_exc->ExceptionCode, ms_exc->ExceptionFlags, |
| 97 | (void *)frame); |
| 98 | if (ms_exc->ExceptionCode == STATUS_GCC_UNWIND) { |
| 99 | if (IS_TARGET_UNWIND(ms_exc->ExceptionFlags)) { |
| 100 | // Set up the upper return value (the lower one and the target PC |
| 101 | // were set in the call to RtlUnwindEx()) for the landing pad. |
| 102 | #ifdef __x86_64__ |
| 103 | disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3]; |
| 104 | #elif defined(__arm__) |
| 105 | disp->ContextRecord->R1 = ms_exc->ExceptionInformation[3]; |
| 106 | #elif defined(__aarch64__) |
| 107 | disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3]; |
| 108 | #endif |
| 109 | } |
| 110 | // This is the collided unwind to the landing pad. Nothing to do. |
| 111 | return ExceptionContinueSearch; |
| 112 | } |
| 113 | |
| 114 | if (ms_exc->ExceptionCode == STATUS_GCC_THROW) { |
| 115 | // This is (probably) a libunwind-controlled exception/unwind. Recover the |
| 116 | // parameters which we set below, and pass them to the personality function. |
| 117 | ours = true; |
| 118 | exc = (_Unwind_Exception *)ms_exc->ExceptionInformation[0]; |
| 119 | if (!IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) { |
| 120 | ctx = (struct _Unwind_Context *)ms_exc->ExceptionInformation[1]; |
| 121 | action = (_Unwind_Action)ms_exc->ExceptionInformation[2]; |
| 122 | } |
| 123 | } else { |
| 124 | // Foreign exception. |
| 125 | // We can't interact with them (we don't know the original target frame |
| 126 | // that we should pass on to RtlUnwindEx in _Unwind_Resume), so just |
| 127 | // pass without calling our destructors here. |
| 128 | return ExceptionContinueSearch; |
| 129 | } |
| 130 | if (!ctx) { |
| 131 | __unw_init_seh(&cursor, disp->ContextRecord); |
| 132 | __unw_seh_set_disp_ctx(&cursor, disp); |
| 133 | __unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc); |
| 134 | ctx = (struct _Unwind_Context *)&cursor; |
| 135 | |
| 136 | if (!IS_UNWINDING(ms_exc->ExceptionFlags)) { |
| 137 | if (ours && ms_exc->NumberParameters > 1) |
| 138 | action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_FORCE_UNWIND); |
| 139 | else |
| 140 | action = _UA_SEARCH_PHASE; |
| 141 | } else { |
| 142 | if (ours && ms_exc->ExceptionInformation[1] == (ULONG_PTR)frame) |
| 143 | action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); |
| 144 | else |
| 145 | action = _UA_CLEANUP_PHASE; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() calling personality " |
| 150 | "function %p(1, %d, %llx, %p, %p)" , |
| 151 | (void *)pers, action, exc->exception_class, |
| 152 | (void *)exc, (void *)ctx); |
| 153 | urc = pers(1, action, exc->exception_class, exc, ctx); |
| 154 | _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() personality returned %d" , urc); |
| 155 | switch (urc) { |
| 156 | case _URC_CONTINUE_UNWIND: |
| 157 | // If we're in phase 2, and the personality routine said to continue |
| 158 | // at the target frame, we're in real trouble. |
| 159 | if (action & _UA_HANDLER_FRAME) |
| 160 | _LIBUNWIND_ABORT("Personality continued unwind at the target frame!" ); |
| 161 | return ExceptionContinueSearch; |
| 162 | case _URC_HANDLER_FOUND: |
| 163 | // If we were called by __libunwind_seh_personality(), indicate that |
| 164 | // a handler was found; otherwise, initiate phase 2 by unwinding. |
| 165 | if (ours && ms_exc->NumberParameters > 1) |
| 166 | return 4 /* ExceptionExecuteHandler in mingw */; |
| 167 | // This should never happen in phase 2. |
| 168 | if (IS_UNWINDING(ms_exc->ExceptionFlags)) |
| 169 | _LIBUNWIND_ABORT("Personality indicated exception handler in phase 2!" ); |
| 170 | exc->private_[1] = (ULONG_PTR)frame; |
| 171 | if (ours) { |
| 172 | ms_exc->NumberParameters = 4; |
| 173 | ms_exc->ExceptionInformation[1] = (ULONG_PTR)frame; |
| 174 | } |
| 175 | // FIXME: Indicate target frame in foreign case! |
| 176 | // phase 2: the clean up phase |
| 177 | RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, ms_ctx, disp->HistoryTable); |
| 178 | _LIBUNWIND_ABORT("RtlUnwindEx() failed" ); |
| 179 | case _URC_INSTALL_CONTEXT: { |
| 180 | // If we were called by __libunwind_seh_personality(), indicate that |
| 181 | // a handler was found; otherwise, it's time to initiate a collided |
| 182 | // unwind to the target. |
| 183 | if (ours && !IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) |
| 184 | return 4 /* ExceptionExecuteHandler in mingw */; |
| 185 | // This should never happen in phase 1. |
| 186 | if (!IS_UNWINDING(ms_exc->ExceptionFlags)) |
| 187 | _LIBUNWIND_ABORT("Personality installed context during phase 1!" ); |
| 188 | #ifdef __x86_64__ |
| 189 | exc->private_[2] = disp->TargetIp; |
| 190 | __unw_get_reg(&cursor, UNW_X86_64_RAX, &retval); |
| 191 | __unw_get_reg(&cursor, UNW_X86_64_RDX, &exc->private_[3]); |
| 192 | #elif defined(__arm__) |
| 193 | exc->private_[2] = disp->TargetPc; |
| 194 | __unw_get_reg(&cursor, UNW_ARM_R0, &retval); |
| 195 | __unw_get_reg(&cursor, UNW_ARM_R1, &exc->private_[3]); |
| 196 | #elif defined(__aarch64__) |
| 197 | exc->private_[2] = disp->TargetPc; |
| 198 | __unw_get_reg(&cursor, UNW_AARCH64_X0, &retval); |
| 199 | __unw_get_reg(&cursor, UNW_AARCH64_X1, &exc->private_[3]); |
| 200 | #endif |
| 201 | __unw_get_reg(&cursor, UNW_REG_IP, &target); |
| 202 | ms_exc->ExceptionCode = STATUS_GCC_UNWIND; |
| 203 | #ifdef __x86_64__ |
| 204 | ms_exc->ExceptionInformation[2] = disp->TargetIp; |
| 205 | #elif defined(__arm__) || defined(__aarch64__) |
| 206 | ms_exc->ExceptionInformation[2] = disp->TargetPc; |
| 207 | #endif |
| 208 | ms_exc->ExceptionInformation[3] = exc->private_[3]; |
| 209 | // Give NTRTL some scratch space to keep track of the collided unwind. |
| 210 | // Don't use the one that was passed in; we don't want to overwrite the |
| 211 | // context in the DISPATCHER_CONTEXT. |
| 212 | CONTEXT new_ctx; |
| 213 | RtlUnwindEx(frame, (PVOID)target, ms_exc, (PVOID)retval, &new_ctx, disp->HistoryTable); |
| 214 | _LIBUNWIND_ABORT("RtlUnwindEx() failed" ); |
| 215 | } |
| 216 | // Anything else indicates a serious problem. |
| 217 | default: return ExceptionContinueExecution; |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | /// Personality function returned by \c __unw_get_proc_info() in SEH contexts. |
| 222 | /// This is a wrapper that calls the real SEH handler function, which in |
| 223 | /// turn (at least, for Itanium-style frames) calls the real Itanium |
| 224 | /// personality function (see \c _GCC_specific_handler()). |
| 225 | extern "C" _Unwind_Reason_Code |
| 226 | __libunwind_seh_personality(int version, _Unwind_Action state, |
| 227 | uint64_t klass, _Unwind_Exception *exc, |
| 228 | struct _Unwind_Context *context) { |
| 229 | (void)version; |
| 230 | (void)klass; |
| 231 | EXCEPTION_RECORD ms_exc; |
| 232 | bool phase2 = (state & (_UA_SEARCH_PHASE|_UA_CLEANUP_PHASE)) == _UA_CLEANUP_PHASE; |
| 233 | ms_exc.ExceptionCode = STATUS_GCC_THROW; |
| 234 | ms_exc.ExceptionFlags = 0; |
| 235 | ms_exc.NumberParameters = 3; |
| 236 | ms_exc.ExceptionInformation[0] = (ULONG_PTR)exc; |
| 237 | ms_exc.ExceptionInformation[1] = (ULONG_PTR)context; |
| 238 | ms_exc.ExceptionInformation[2] = state; |
| 239 | DISPATCHER_CONTEXT *disp_ctx = |
| 240 | __unw_seh_get_disp_ctx((unw_cursor_t *)context); |
| 241 | #if defined(__aarch64__) |
| 242 | LOCAL_DISPATCHER_CONTEXT_NONVOLREG_ARM64 nonvol; |
| 243 | memcpy(&nonvol.GpNvRegs, &disp_ctx->ContextRecord->X19, |
| 244 | sizeof(nonvol.GpNvRegs)); |
| 245 | for (int i = 0; i < 8; i++) |
| 246 | nonvol.FpNvRegs[i] = disp_ctx->ContextRecord->V[i + 8].D[0]; |
| 247 | disp_ctx->NonVolatileRegisters = nonvol.Buffer; |
| 248 | #elif defined(__arm__) |
| 249 | LOCAL_DISPATCHER_CONTEXT_NONVOLREG_ARM nonvol; |
| 250 | memcpy(&nonvol.GpNvRegs, &disp_ctx->ContextRecord->R4, |
| 251 | sizeof(nonvol.GpNvRegs)); |
| 252 | memcpy(&nonvol.FpNvRegs, &disp_ctx->ContextRecord->D[8], |
| 253 | sizeof(nonvol.FpNvRegs)); |
| 254 | disp_ctx->NonVolatileRegisters = nonvol.Buffer; |
| 255 | #endif |
| 256 | _LIBUNWIND_TRACE_UNWINDING("__libunwind_seh_personality() calling " |
| 257 | "LanguageHandler %p(%p, %p, %p, %p)" , |
| 258 | (void *)disp_ctx->LanguageHandler, (void *)&ms_exc, |
| 259 | (void *)disp_ctx->EstablisherFrame, |
| 260 | (void *)disp_ctx->ContextRecord, (void *)disp_ctx); |
| 261 | EXCEPTION_DISPOSITION ms_act = disp_ctx->LanguageHandler(&ms_exc, |
| 262 | (PVOID)disp_ctx->EstablisherFrame, |
| 263 | disp_ctx->ContextRecord, |
| 264 | disp_ctx); |
| 265 | _LIBUNWIND_TRACE_UNWINDING("__libunwind_seh_personality() LanguageHandler " |
| 266 | "returned %d" , |
| 267 | (int)ms_act); |
| 268 | switch (ms_act) { |
| 269 | case ExceptionContinueExecution: return _URC_END_OF_STACK; |
| 270 | case ExceptionContinueSearch: return _URC_CONTINUE_UNWIND; |
| 271 | case 4 /*ExceptionExecuteHandler*/: |
| 272 | return phase2 ? _URC_INSTALL_CONTEXT : _URC_HANDLER_FOUND; |
| 273 | default: |
| 274 | return phase2 ? _URC_FATAL_PHASE2_ERROR : _URC_FATAL_PHASE1_ERROR; |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | static _Unwind_Reason_Code |
| 279 | unwind_phase2_forced(unw_context_t *uc, |
| 280 | _Unwind_Exception *exception_object, |
| 281 | _Unwind_Stop_Fn stop, void *stop_parameter) { |
| 282 | unw_cursor_t cursor2; |
| 283 | __unw_init_local(&cursor2, uc); |
| 284 | |
| 285 | // Walk each frame until we reach where search phase said to stop |
| 286 | while (__unw_step(&cursor2) > 0) { |
| 287 | |
| 288 | // Update info about this frame. |
| 289 | unw_proc_info_t frameInfo; |
| 290 | if (__unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { |
| 291 | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_get_proc_info " |
| 292 | "failed => _URC_END_OF_STACK" , |
| 293 | (void *)exception_object); |
| 294 | return _URC_FATAL_PHASE2_ERROR; |
| 295 | } |
| 296 | |
| 297 | #ifndef NDEBUG |
| 298 | // When tracing, print state information. |
| 299 | if (_LIBUNWIND_TRACING_UNWINDING) { |
| 300 | char functionBuf[512]; |
| 301 | const char *functionName = functionBuf; |
| 302 | unw_word_t offset; |
| 303 | if ((__unw_get_proc_name(&cursor2, functionBuf, sizeof(functionBuf), |
| 304 | &offset) != UNW_ESUCCESS) || |
| 305 | (frameInfo.start_ip + offset > frameInfo.end_ip)) |
| 306 | functionName = ".anonymous." ; |
| 307 | _LIBUNWIND_TRACE_UNWINDING( |
| 308 | "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR |
| 309 | ", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR, |
| 310 | (void *)exception_object, frameInfo.start_ip, functionName, |
| 311 | frameInfo.lsda, frameInfo.handler); |
| 312 | } |
| 313 | #endif |
| 314 | |
| 315 | // Call stop function at each frame. |
| 316 | _Unwind_Action action = |
| 317 | (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); |
| 318 | _Unwind_Reason_Code stopResult = |
| 319 | (*stop)(1, action, exception_object->exception_class, exception_object, |
| 320 | (struct _Unwind_Context *)(&cursor2), stop_parameter); |
| 321 | _LIBUNWIND_TRACE_UNWINDING( |
| 322 | "unwind_phase2_forced(ex_ojb=%p): stop function returned %d" , |
| 323 | (void *)exception_object, stopResult); |
| 324 | if (stopResult != _URC_NO_REASON) { |
| 325 | _LIBUNWIND_TRACE_UNWINDING( |
| 326 | "unwind_phase2_forced(ex_ojb=%p): stopped by stop function" , |
| 327 | (void *)exception_object); |
| 328 | return _URC_FATAL_PHASE2_ERROR; |
| 329 | } |
| 330 | |
| 331 | // If there is a personality routine, tell it we are unwinding. |
| 332 | if (frameInfo.handler != 0) { |
| 333 | _Unwind_Personality_Fn p = |
| 334 | (_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler); |
| 335 | _LIBUNWIND_TRACE_UNWINDING( |
| 336 | "unwind_phase2_forced(ex_ojb=%p): calling personality function %p" , |
| 337 | (void *)exception_object, (void *)(uintptr_t)p); |
| 338 | _Unwind_Reason_Code personalityResult = |
| 339 | (*p)(1, action, exception_object->exception_class, exception_object, |
| 340 | (struct _Unwind_Context *)(&cursor2)); |
| 341 | switch (personalityResult) { |
| 342 | case _URC_CONTINUE_UNWIND: |
| 343 | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| 344 | "personality returned " |
| 345 | "_URC_CONTINUE_UNWIND" , |
| 346 | (void *)exception_object); |
| 347 | // Destructors called, continue unwinding |
| 348 | break; |
| 349 | case _URC_INSTALL_CONTEXT: |
| 350 | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| 351 | "personality returned " |
| 352 | "_URC_INSTALL_CONTEXT" , |
| 353 | (void *)exception_object); |
| 354 | // We may get control back if landing pad calls _Unwind_Resume(). |
| 355 | __unw_resume(&cursor2); |
| 356 | break; |
| 357 | case _URC_END_OF_STACK: |
| 358 | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| 359 | "personality returned " |
| 360 | "_URC_END_OF_STACK" , |
| 361 | (void *)exception_object); |
| 362 | break; |
| 363 | default: |
| 364 | // Personality routine returned an unknown result code. |
| 365 | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| 366 | "personality returned %d, " |
| 367 | "_URC_FATAL_PHASE2_ERROR" , |
| 368 | (void *)exception_object, personalityResult); |
| 369 | return _URC_FATAL_PHASE2_ERROR; |
| 370 | } |
| 371 | if (personalityResult == _URC_END_OF_STACK) |
| 372 | break; |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | // Call stop function one last time and tell it we've reached the end |
| 377 | // of the stack. |
| 378 | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " |
| 379 | "function with _UA_END_OF_STACK" , |
| 380 | (void *)exception_object); |
| 381 | _Unwind_Action lastAction = |
| 382 | (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); |
| 383 | (*stop)(1, lastAction, exception_object->exception_class, exception_object, |
| 384 | (struct _Unwind_Context *)(&cursor2), stop_parameter); |
| 385 | |
| 386 | // Clean up phase did not resume at the frame that the search phase said it |
| 387 | // would. |
| 388 | return _URC_FATAL_PHASE2_ERROR; |
| 389 | } |
| 390 | |
| 391 | /// Called by \c __cxa_throw(). Only returns if there is a fatal error. |
| 392 | _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| 393 | _Unwind_RaiseException(_Unwind_Exception *exception_object) { |
| 394 | _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)" , |
| 395 | (void *)exception_object); |
| 396 | |
| 397 | // Mark that this is a non-forced unwind, so _Unwind_Resume() |
| 398 | // can do the right thing. |
| 399 | memset(exception_object->private_, 0, sizeof(exception_object->private_)); |
| 400 | |
| 401 | // phase 1: the search phase |
| 402 | // We'll let the system do that for us. |
| 403 | RaiseException(STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exception_object); |
| 404 | |
| 405 | // If we get here, either something went horribly wrong or we reached the |
| 406 | // top of the stack. Either way, let libc++abi call std::terminate(). |
| 407 | return _URC_END_OF_STACK; |
| 408 | } |
| 409 | |
| 410 | /// When \c _Unwind_RaiseException() is in phase2, it hands control |
| 411 | /// to the personality function at each frame. The personality |
| 412 | /// may force a jump to a landing pad in that function; the landing |
| 413 | /// pad code may then call \c _Unwind_Resume() to continue with the |
| 414 | /// unwinding. Note: the call to \c _Unwind_Resume() is from compiler |
| 415 | /// generated user code. All other \c _Unwind_* routines are called |
| 416 | /// by the C++ runtime \c __cxa_* routines. |
| 417 | /// |
| 418 | /// Note: re-throwing an exception (as opposed to continuing the unwind) |
| 419 | /// is implemented by having the code call \c __cxa_rethrow() which |
| 420 | /// in turn calls \c _Unwind_Resume_or_Rethrow(). |
| 421 | _LIBUNWIND_EXPORT void |
| 422 | _Unwind_Resume(_Unwind_Exception *exception_object) { |
| 423 | _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)" , (void *)exception_object); |
| 424 | |
| 425 | if (exception_object->private_[0] != 0) { |
| 426 | unw_context_t uc; |
| 427 | |
| 428 | __unw_getcontext(&uc); |
| 429 | unwind_phase2_forced(&uc, exception_object, |
| 430 | (_Unwind_Stop_Fn) exception_object->private_[0], |
| 431 | (void *)exception_object->private_[4]); |
| 432 | } else { |
| 433 | // Recover the parameters for the unwind from the exception object |
| 434 | // so we can start unwinding again. |
| 435 | EXCEPTION_RECORD ms_exc; |
| 436 | CONTEXT ms_ctx; |
| 437 | UNWIND_HISTORY_TABLE hist; |
| 438 | |
| 439 | memset(&ms_exc, 0, sizeof(ms_exc)); |
| 440 | memset(&hist, 0, sizeof(hist)); |
| 441 | ms_exc.ExceptionCode = STATUS_GCC_THROW; |
| 442 | ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE; |
| 443 | ms_exc.NumberParameters = 4; |
| 444 | ms_exc.ExceptionInformation[0] = (ULONG_PTR)exception_object; |
| 445 | ms_exc.ExceptionInformation[1] = exception_object->private_[1]; |
| 446 | ms_exc.ExceptionInformation[2] = exception_object->private_[2]; |
| 447 | ms_exc.ExceptionInformation[3] = exception_object->private_[3]; |
| 448 | RtlUnwindEx((PVOID)exception_object->private_[1], |
| 449 | (PVOID)exception_object->private_[2], &ms_exc, |
| 450 | exception_object, &ms_ctx, &hist); |
| 451 | } |
| 452 | |
| 453 | // Clients assume _Unwind_Resume() does not return, so all we can do is abort. |
| 454 | _LIBUNWIND_ABORT("_Unwind_Resume() can't return" ); |
| 455 | } |
| 456 | |
| 457 | /// Not used by C++. |
| 458 | /// Unwinds stack, calling "stop" function at each frame. |
| 459 | /// Could be used to implement \c longjmp(). |
| 460 | _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| 461 | _Unwind_ForcedUnwind(_Unwind_Exception *exception_object, |
| 462 | _Unwind_Stop_Fn stop, void *stop_parameter) { |
| 463 | _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)" , |
| 464 | (void *)exception_object, (void *)(uintptr_t)stop); |
| 465 | unw_context_t uc; |
| 466 | __unw_getcontext(&uc); |
| 467 | |
| 468 | // Mark that this is a forced unwind, so _Unwind_Resume() can do |
| 469 | // the right thing. |
| 470 | exception_object->private_[0] = (uintptr_t) stop; |
| 471 | exception_object->private_[4] = (uintptr_t) stop_parameter; |
| 472 | |
| 473 | // do it |
| 474 | return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter); |
| 475 | } |
| 476 | |
| 477 | /// Called by personality handler during phase 2 to get LSDA for current frame. |
| 478 | _LIBUNWIND_EXPORT uintptr_t |
| 479 | _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { |
| 480 | uintptr_t result = |
| 481 | (uintptr_t)__unw_seh_get_disp_ctx((unw_cursor_t *)context)->HandlerData; |
| 482 | _LIBUNWIND_TRACE_API( |
| 483 | "_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR, |
| 484 | (void *)context, result); |
| 485 | return result; |
| 486 | } |
| 487 | |
| 488 | /// Called by personality handler during phase 2 to find the start of the |
| 489 | /// function. |
| 490 | _LIBUNWIND_EXPORT uintptr_t |
| 491 | _Unwind_GetRegionStart(struct _Unwind_Context *context) { |
| 492 | DISPATCHER_CONTEXT *disp = __unw_seh_get_disp_ctx((unw_cursor_t *)context); |
| 493 | uintptr_t result = (uintptr_t)disp->FunctionEntry->BeginAddress + disp->ImageBase; |
| 494 | _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR, |
| 495 | (void *)context, result); |
| 496 | return result; |
| 497 | } |
| 498 | |
| 499 | static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *context) { |
| 500 | #ifdef _LIBUNWIND_TARGET_X86_64 |
| 501 | new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)) |
| 502 | UnwindCursor<LocalAddressSpace, Registers_x86_64>( |
| 503 | context, LocalAddressSpace::sThisAddressSpace); |
| 504 | auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor); |
| 505 | co->setInfoBasedOnIPRegister(); |
| 506 | return UNW_ESUCCESS; |
| 507 | #elif defined(_LIBUNWIND_TARGET_ARM) |
| 508 | new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)) |
| 509 | UnwindCursor<LocalAddressSpace, Registers_arm>( |
| 510 | context, LocalAddressSpace::sThisAddressSpace); |
| 511 | auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor); |
| 512 | co->setInfoBasedOnIPRegister(); |
| 513 | return UNW_ESUCCESS; |
| 514 | #elif defined(_LIBUNWIND_TARGET_AARCH64) |
| 515 | new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)) |
| 516 | UnwindCursor<LocalAddressSpace, Registers_arm64>( |
| 517 | context, LocalAddressSpace::sThisAddressSpace); |
| 518 | auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor); |
| 519 | co->setInfoBasedOnIPRegister(); |
| 520 | return UNW_ESUCCESS; |
| 521 | #else |
| 522 | return UNW_EINVAL; |
| 523 | #endif |
| 524 | } |
| 525 | |
| 526 | static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor) { |
| 527 | #ifdef _LIBUNWIND_TARGET_X86_64 |
| 528 | return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->getDispatcherContext(); |
| 529 | #elif defined(_LIBUNWIND_TARGET_ARM) |
| 530 | return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->getDispatcherContext(); |
| 531 | #elif defined(_LIBUNWIND_TARGET_AARCH64) |
| 532 | return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->getDispatcherContext(); |
| 533 | #else |
| 534 | return nullptr; |
| 535 | #endif |
| 536 | } |
| 537 | |
| 538 | static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor, |
| 539 | DISPATCHER_CONTEXT *disp) { |
| 540 | #ifdef _LIBUNWIND_TARGET_X86_64 |
| 541 | reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->setDispatcherContext(disp); |
| 542 | #elif defined(_LIBUNWIND_TARGET_ARM) |
| 543 | reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->setDispatcherContext(disp); |
| 544 | #elif defined(_LIBUNWIND_TARGET_AARCH64) |
| 545 | reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->setDispatcherContext(disp); |
| 546 | #endif |
| 547 | } |
| 548 | |
| 549 | #endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) |
| 550 | |