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