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