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