aboutsummaryrefslogtreecommitdiffstats
path: root/src/Unwind-seh.cpp
blob: 7647f2e0db0bfe3dd9f0e7eaef77b3e0982a7c26 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
//===--------------------------- Unwind-seh.cpp ---------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//  Implements SEH-based Itanium C++ exceptions.
//
//===----------------------------------------------------------------------===//

#include "config.h"

#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)

#include <unwind.h>

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>

#include <windef.h>
#include <excpt.h>
#include <winnt.h>
#include <ntstatus.h>

#include "libunwind_ext.h"
#include "UnwindCursor.hpp"

using namespace libunwind;

#define STATUS_USER_DEFINED (1u << 29)

#define STATUS_GCC_MAGIC  (('G' << 16) | ('C' << 8) | 'C')

#define MAKE_CUSTOM_STATUS(s, c) \
  ((NTSTATUS)(((s) << 30) | STATUS_USER_DEFINED | (c)))
#define MAKE_GCC_EXCEPTION(c) \
  MAKE_CUSTOM_STATUS(STATUS_SEVERITY_SUCCESS, STATUS_GCC_MAGIC | ((c) << 24))

/// SEH exception raised by libunwind when the program calls
/// \c _Unwind_RaiseException.
#define STATUS_GCC_THROW MAKE_GCC_EXCEPTION(0) // 0x20474343
/// SEH exception raised by libunwind to initiate phase 2 of exception
/// handling.
#define STATUS_GCC_UNWIND MAKE_GCC_EXCEPTION(1) // 0x21474343

/// Class of foreign exceptions based on unrecognized SEH exceptions.
static const uint64_t kSEHExceptionClass = 0x434C4E4753454800; // CLNGSEH\0

/// Exception cleanup routine used by \c _GCC_specific_handler to
/// free foreign exceptions.
static void seh_exc_cleanup(_Unwind_Reason_Code urc, _Unwind_Exception *exc) {
  (void)urc;
  if (exc->exception_class != kSEHExceptionClass)
    _LIBUNWIND_ABORT("SEH cleanup called on non-SEH exception");
  free(exc);
}

static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *ctx);
static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor);
static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor,
                                   DISPATCHER_CONTEXT *disp);

/// Common implementation of SEH-style handler functions used by Itanium-
/// style frames.  Depending on how and why it was called, it may do one of:
///  a) Delegate to the given Itanium-style personality function; or
///  b) Initiate a collided unwind to halt unwinding.
_LIBUNWIND_EXPORT EXCEPTION_DISPOSITION
_GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx,
                      DISPATCHER_CONTEXT *disp, __personality_routine pers) {
  unw_cursor_t cursor;
  _Unwind_Exception *exc;
  _Unwind_Action action;
  struct _Unwind_Context *ctx = nullptr;
  _Unwind_Reason_Code urc;
  uintptr_t retval, target;
  bool ours = false;

  _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler(%#010lx(%lx), %p)",
                             ms_exc->ExceptionCode, ms_exc->ExceptionFlags,
                             (void *)frame);
  if (ms_exc->ExceptionCode == STATUS_GCC_UNWIND) {
    if (IS_TARGET_UNWIND(ms_exc->ExceptionFlags)) {
      // Set up the upper return value (the lower one and the target PC
      // were set in the call to RtlUnwindEx()) for the landing pad.
#ifdef __x86_64__
      disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3];
#elif defined(__arm__)
      disp->ContextRecord->R1 = ms_exc->ExceptionInformation[3];
#elif defined(__aarch64__)
      disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3];
#endif
    }
    // This is the collided unwind to the landing pad. Nothing to do.
    return ExceptionContinueSearch;
  }

  if (ms_exc->ExceptionCode == STATUS_GCC_THROW) {
    // This is (probably) a libunwind-controlled exception/unwind. Recover the
    // parameters which we set below, and pass them to the personality function.
    ours = true;
    exc = (_Unwind_Exception *)ms_exc->ExceptionInformation[0];
    if (!IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) {
      ctx = (struct _Unwind_Context *)ms_exc->ExceptionInformation[1];
      action = (_Unwind_Action)ms_exc->ExceptionInformation[2];
    }
  } else {
    // Foreign exception.
    exc = (_Unwind_Exception *)malloc(sizeof(_Unwind_Exception));
    exc->exception_class = kSEHExceptionClass;
    exc->exception_cleanup = seh_exc_cleanup;
    memset(exc->private_, 0, sizeof(exc->private_));
  }
  if (!ctx) {
    __unw_init_seh(&cursor, disp->ContextRecord);
    __unw_seh_set_disp_ctx(&cursor, disp);
    __unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc - 1);
    ctx = (struct _Unwind_Context *)&cursor;

    if (!IS_UNWINDING(ms_exc->ExceptionFlags)) {
      if (ours && ms_exc->NumberParameters > 1)
        action =  (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_FORCE_UNWIND);
      else
        action = _UA_SEARCH_PHASE;
    } else {
      if (ours && ms_exc->ExceptionInformation[1] == (ULONG_PTR)frame)
        action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME);
      else
        action = _UA_CLEANUP_PHASE;
    }
  }

  _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() calling personality "
                             "function %p(1, %d, %llx, %p, %p)",
                             (void *)pers, action, exc->exception_class,
                             (void *)exc, (void *)ctx);
  urc = pers(1, action, exc->exception_class, exc, ctx);
  _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() personality returned %d", urc);
  switch (urc) {
  case _URC_CONTINUE_UNWIND:
    // If we're in phase 2, and the personality routine said to continue
    // at the target frame, we're in real trouble.
    if (action & _UA_HANDLER_FRAME)
      _LIBUNWIND_ABORT("Personality continued unwind at the target frame!");
    return ExceptionContinueSearch;
  case _URC_HANDLER_FOUND:
    // If we were called by __libunwind_seh_personality(), indicate that
    // a handler was found; otherwise, initiate phase 2 by unwinding.
    if (ours && ms_exc->NumberParameters > 1)
      return 4 /* ExecptionExecuteHandler in mingw */;
    // This should never happen in phase 2.
    if (IS_UNWINDING(ms_exc->ExceptionFlags))
      _LIBUNWIND_ABORT("Personality indicated exception handler in phase 2!");
    exc->private_[1] = (ULONG_PTR)frame;
    if (ours) {
      ms_exc->NumberParameters = 4;
      ms_exc->ExceptionInformation[1] = (ULONG_PTR)frame;
    }
    // FIXME: Indicate target frame in foreign case!
    // phase 2: the clean up phase
    RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, ms_ctx, disp->HistoryTable);
    _LIBUNWIND_ABORT("RtlUnwindEx() failed");
  case _URC_INSTALL_CONTEXT: {
    // If we were called by __libunwind_seh_personality(), indicate that
    // a handler was found; otherwise, it's time to initiate a collided
    // unwind to the target.
    if (ours && !IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1)
      return 4 /* ExecptionExecuteHandler in mingw */;
    // This should never happen in phase 1.
    if (!IS_UNWINDING(ms_exc->ExceptionFlags))
      _LIBUNWIND_ABORT("Personality installed context during phase 1!");
#ifdef __x86_64__
    exc->private_[2] = disp->TargetIp;
    __unw_get_reg(&cursor, UNW_X86_64_RAX, &retval);
    __unw_get_reg(&cursor, UNW_X86_64_RDX, &exc->private_[3]);
#elif defined(__arm__)
    exc->private_[2] = disp->TargetPc;
    __unw_get_reg(&cursor, UNW_ARM_R0, &retval);
    __unw_get_reg(&cursor, UNW_ARM_R1, &exc->private_[3]);
#elif defined(__aarch64__)
    exc->private_[2] = disp->TargetPc;
    __unw_get_reg(&cursor, UNW_ARM64_X0, &retval);
    __unw_get_reg(&cursor, UNW_ARM64_X1, &exc->private_[3]);
#endif
    __unw_get_reg(&cursor, UNW_REG_IP, &target);
    ms_exc->ExceptionCode = STATUS_GCC_UNWIND;
#ifdef __x86_64__
    ms_exc->ExceptionInformation[2] = disp->TargetIp;
#elif defined(__arm__) || defined(__aarch64__)
    ms_exc->ExceptionInformation[2] = disp->TargetPc;
#endif
    ms_exc->ExceptionInformation[3] = exc->private_[3];
    // Give NTRTL some scratch space to keep track of the collided unwind.
    // Don't use the one that was passed in; we don't want to overwrite the
    // context in the DISPATCHER_CONTEXT.
    CONTEXT new_ctx;
    RtlUnwindEx(frame, (PVOID)target, ms_exc, (PVOID)retval, &new_ctx, disp->HistoryTable);
    _LIBUNWIND_ABORT("RtlUnwindEx() failed");
  }
  // Anything else indicates a serious problem.
  default: return ExceptionContinueExecution;
  }
}

/// Personality function returned by \c __unw_get_proc_info() in SEH contexts.
/// This is a wrapper that calls the real SEH handler function, which in
/// turn (at least, for Itanium-style frames) calls the real Itanium
/// personality function (see \c _GCC_specific_handler()).
extern "C" _Unwind_Reason_Code
__libunwind_seh_personality(int version, _Unwind_Action state,
                            uint64_t klass, _Unwind_Exception *exc,
                            struct _Unwind_Context *context) {
  (void)version;
  (void)klass;
  EXCEPTION_RECORD ms_exc;
  bool phase2 = (state & (_UA_SEARCH_PHASE|_UA_CLEANUP_PHASE)) == _UA_CLEANUP_PHASE;
  ms_exc.ExceptionCode = STATUS_GCC_THROW;
  ms_exc.ExceptionFlags = 0;
  ms_exc.NumberParameters = 3;
  ms_exc.ExceptionInformation[0] = (ULONG_PTR)exc;
  ms_exc.ExceptionInformation[1] = (ULONG_PTR)context;
  ms_exc.ExceptionInformation[2] = state;
  DISPATCHER_CONTEXT *disp_ctx =
      __unw_seh_get_disp_ctx((unw_cursor_t *)context);
  EXCEPTION_DISPOSITION ms_act = disp_ctx->LanguageHandler(&ms_exc,
                                                           (PVOID)disp_ctx->EstablisherFrame,
                                                           disp_ctx->ContextRecord,
                                                           disp_ctx);
  switch (ms_act) {
  case ExceptionContinueSearch: return _URC_CONTINUE_UNWIND;
  case 4 /*ExceptionExecuteHandler*/:
    return phase2 ? _URC_INSTALL_CONTEXT : _URC_HANDLER_FOUND;
  default:
    return phase2 ? _URC_FATAL_PHASE2_ERROR : _URC_FATAL_PHASE1_ERROR;
  }
}

static _Unwind_Reason_Code
unwind_phase2_forced(unw_context_t *uc,
                     _Unwind_Exception *exception_object,
                     _Unwind_Stop_Fn stop, void *stop_parameter) {
  unw_cursor_t cursor2;
  __unw_init_local(&cursor2, uc);

  // Walk each frame until we reach where search phase said to stop
  while (__unw_step(&cursor2) > 0) {

    // Update info about this frame.
    unw_proc_info_t frameInfo;
    if (__unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) {
      _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step "
                                 "failed => _URC_END_OF_STACK",
                                 (void *)exception_object);
      return _URC_FATAL_PHASE2_ERROR;
    }

    // When tracing, print state information.
    if (_LIBUNWIND_TRACING_UNWINDING) {
      char functionBuf[512];
      const char *functionName = functionBuf;
      unw_word_t offset;
      if ((__unw_get_proc_name(&cursor2, functionBuf, sizeof(functionBuf),
                               &offset) != UNW_ESUCCESS) ||
          (frameInfo.start_ip + offset > frameInfo.end_ip))
        functionName = ".anonymous.";
      _LIBUNWIND_TRACE_UNWINDING(
          "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIx64
          ", func=%s, lsda=0x%" PRIx64 ", personality=0x%" PRIx64,
          (void *)exception_object, frameInfo.start_ip, functionName,
          frameInfo.lsda, frameInfo.handler);
    }

    // Call stop function at each frame.
    _Unwind_Action action =
        (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE);
    _Unwind_Reason_Code stopResult =
        (*stop)(1, action, exception_object->exception_class, exception_object,
                (struct _Unwind_Context *)(&cursor2), stop_parameter);
    _LIBUNWIND_TRACE_UNWINDING(
        "unwind_phase2_forced(ex_ojb=%p): stop function returned %d",
        (void *)exception_object, stopResult);
    if (stopResult != _URC_NO_REASON) {
      _LIBUNWIND_TRACE_UNWINDING(
          "unwind_phase2_forced(ex_ojb=%p): stopped by stop function",
          (void *)exception_object);
      return _URC_FATAL_PHASE2_ERROR;
    }

    // If there is a personality routine, tell it we are unwinding.
    if (frameInfo.handler != 0) {
      __personality_routine p =
          (__personality_routine)(intptr_t)(frameInfo.handler);
      _LIBUNWIND_TRACE_UNWINDING(
          "unwind_phase2_forced(ex_ojb=%p): calling personality function %p",
          (void *)exception_object, (void *)(uintptr_t)p);
      _Unwind_Reason_Code personalityResult =
          (*p)(1, action, exception_object->exception_class, exception_object,
               (struct _Unwind_Context *)(&cursor2));
      switch (personalityResult) {
      case _URC_CONTINUE_UNWIND:
        _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
                                   "personality returned "
                                   "_URC_CONTINUE_UNWIND",
                                   (void *)exception_object);
        // Destructors called, continue unwinding
        break;
      case _URC_INSTALL_CONTEXT:
        _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
                                   "personality returned "
                                   "_URC_INSTALL_CONTEXT",
                                   (void *)exception_object);
        // We may get control back if landing pad calls _Unwind_Resume().
        __unw_resume(&cursor2);
        break;
      default:
        // Personality routine returned an unknown result code.
        _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): "
                                   "personality returned %d, "
                                   "_URC_FATAL_PHASE2_ERROR",
                                   (void *)exception_object, personalityResult);
        return _URC_FATAL_PHASE2_ERROR;
      }
    }
  }

  // Call stop function one last time and tell it we've reached the end
  // of the stack.
  _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop "
                             "function with _UA_END_OF_STACK",
                             (void *)exception_object);
  _Unwind_Action lastAction =
      (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK);
  (*stop)(1, lastAction, exception_object->exception_class, exception_object,
          (struct _Unwind_Context *)(&cursor2), stop_parameter);

  // Clean up phase did not resume at the frame that the search phase said it
  // would.
  return _URC_FATAL_PHASE2_ERROR;
}

/// Called by \c __cxa_throw().  Only returns if there is a fatal error.
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
  _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)",
                       (void *)exception_object);

  // Mark that this is a non-forced unwind, so _Unwind_Resume()
  // can do the right thing.
  memset(exception_object->private_, 0, sizeof(exception_object->private_));

  // phase 1: the search phase
  // We'll let the system do that for us.
  RaiseException(STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exception_object);

  // If we get here, either something went horribly wrong or we reached the
  // top of the stack. Either way, let libc++abi call std::terminate().
  return _URC_END_OF_STACK;
}

/// When \c _Unwind_RaiseException() is in phase2, it hands control
/// to the personality function at each frame.  The personality
/// may force a jump to a landing pad in that function; the landing
/// pad code may then call \c _Unwind_Resume() to continue with the
/// unwinding.  Note: the call to \c _Unwind_Resume() is from compiler
/// geneated user code.  All other \c _Unwind_* routines are called
/// by the C++ runtime \c __cxa_* routines.
///
/// Note: re-throwing an exception (as opposed to continuing the unwind)
/// is implemented by having the code call \c __cxa_rethrow() which
/// in turn calls \c _Unwind_Resume_or_Rethrow().
_LIBUNWIND_EXPORT void
_Unwind_Resume(_Unwind_Exception *exception_object) {
  _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object);

  if (exception_object->private_[0] != 0) {
    unw_context_t uc;

    __unw_getcontext(&uc);
    unwind_phase2_forced(&uc, exception_object,
                         (_Unwind_Stop_Fn) exception_object->private_[0],
                         (void *)exception_object->private_[4]);
  } else {
    // Recover the parameters for the unwind from the exception object
    // so we can start unwinding again.
    EXCEPTION_RECORD ms_exc;
    CONTEXT ms_ctx;
    UNWIND_HISTORY_TABLE hist;

    memset(&ms_exc, 0, sizeof(ms_exc));
    memset(&hist, 0, sizeof(hist));
    ms_exc.ExceptionCode = STATUS_GCC_THROW;
    ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
    ms_exc.NumberParameters = 4;
    ms_exc.ExceptionInformation[0] = (ULONG_PTR)exception_object;
    ms_exc.ExceptionInformation[1] = exception_object->private_[1];
    ms_exc.ExceptionInformation[2] = exception_object->private_[2];
    ms_exc.ExceptionInformation[3] = exception_object->private_[3];
    RtlUnwindEx((PVOID)exception_object->private_[1],
                (PVOID)exception_object->private_[2], &ms_exc,
                exception_object, &ms_ctx, &hist);
  }

  // Clients assume _Unwind_Resume() does not return, so all we can do is abort.
  _LIBUNWIND_ABORT("_Unwind_Resume() can't return");
}

/// Not used by C++.
/// Unwinds stack, calling "stop" function at each frame.
/// Could be used to implement \c longjmp().
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
                     _Unwind_Stop_Fn stop, void *stop_parameter) {
  _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)",
                       (void *)exception_object, (void *)(uintptr_t)stop);
  unw_context_t uc;
  __unw_getcontext(&uc);

  // Mark that this is a forced unwind, so _Unwind_Resume() can do
  // the right thing.
  exception_object->private_[0] = (uintptr_t) stop;
  exception_object->private_[4] = (uintptr_t) stop_parameter;

  // do it
  return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter);
}

/// Called by personality handler during phase 2 to get LSDA for current frame.
_LIBUNWIND_EXPORT uintptr_t
_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
  uintptr_t result =
      (uintptr_t)__unw_seh_get_disp_ctx((unw_cursor_t *)context)->HandlerData;
  _LIBUNWIND_TRACE_API(
      "_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR,
      (void *)context, result);
  return result;
}

/// Called by personality handler during phase 2 to find the start of the
/// function.
_LIBUNWIND_EXPORT uintptr_t
_Unwind_GetRegionStart(struct _Unwind_Context *context) {
  DISPATCHER_CONTEXT *disp = __unw_seh_get_disp_ctx((unw_cursor_t *)context);
  uintptr_t result = (uintptr_t)disp->FunctionEntry->BeginAddress + disp->ImageBase;
  _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR,
                       (void *)context, result);
  return result;
}

static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *context) {
#ifdef _LIBUNWIND_TARGET_X86_64
  new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor))
      UnwindCursor<LocalAddressSpace, Registers_x86_64>(
          context, LocalAddressSpace::sThisAddressSpace);
  auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);
  co->setInfoBasedOnIPRegister();
  return UNW_ESUCCESS;
#elif defined(_LIBUNWIND_TARGET_ARM)
  new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor))
      UnwindCursor<LocalAddressSpace, Registers_arm>(
          context, LocalAddressSpace::sThisAddressSpace);
  auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);
  co->setInfoBasedOnIPRegister();
  return UNW_ESUCCESS;
#elif defined(_LIBUNWIND_TARGET_AARCH64)
  new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor))
      UnwindCursor<LocalAddressSpace, Registers_arm64>(
          context, LocalAddressSpace::sThisAddressSpace);
  auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor);
  co->setInfoBasedOnIPRegister();
  return UNW_ESUCCESS;
#else
  return UNW_EINVAL;
#endif
}

static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor) {
#ifdef _LIBUNWIND_TARGET_X86_64
  return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->getDispatcherContext();
#elif defined(_LIBUNWIND_TARGET_ARM)
  return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->getDispatcherContext();
#elif defined(_LIBUNWIND_TARGET_AARCH64)
  return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->getDispatcherContext();
#else
  return nullptr;
#endif
}

static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor,
                                   DISPATCHER_CONTEXT *disp) {
#ifdef _LIBUNWIND_TARGET_X86_64
  reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->setDispatcherContext(disp);
#elif defined(_LIBUNWIND_TARGET_ARM)
  reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->setDispatcherContext(disp);
#elif defined(_LIBUNWIND_TARGET_AARCH64)
  reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->setDispatcherContext(disp);
#endif
}

#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)