Add support for SEH unwinding on Windows.

Summary:
I've tested this implementation on x86-64 to ensure that it works. All
`libc++abi` tests pass, as do all `libc++` exception-related tests. ARM
still remains to be implemented (@compnerd?).

Special thanks to KJK::Hyperion for his excellent series of articles on
how EH works on x86-64 Windows. (Seriously, check it out. It's awesome.)

I'm actually not sure if this should go in as is. I particularly don't
like that I duplicated the UnwindCursor class for this special case.

Reviewers: mstorsjo, rnk, compnerd, smeenai, javed.absar

Subscribers: mgorny, kristof.beyls, christof, chrib, cfe-commits, compnerd, llvm-commits

Differential Revision: https://reviews.llvm.org/D50564

llvm-svn: 341125
Cr-Mirrored-From: sso://chromium.googlesource.com/_direct/external/github.com/llvm/llvm-project
Cr-Mirrored-Commit: a7e3a6d8029dc16a3d91eeaf54e9e74ad56ad177
diff --git a/src/UnwindCursor.hpp b/src/UnwindCursor.hpp
index 0be4cd9..7c1506a 100644
--- a/src/UnwindCursor.hpp
+++ b/src/UnwindCursor.hpp
@@ -18,10 +18,51 @@
 #include <stdlib.h>
 #include <unwind.h>
 
+#ifdef _WIN32
+  #include <windows.h>
+  #include <ntverp.h>
+#endif
 #ifdef __APPLE__
   #include <mach-o/dyld.h>
 #endif
 
+#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
+// Provide a definition for the DISPATCHER_CONTEXT struct for old (Win7 and
+// earlier) SDKs.
+// MinGW-w64 has always provided this struct.
+  #if defined(_WIN32) && defined(_LIBUNWIND_TARGET_X86_64) && \
+      !defined(__MINGW32__) && VER_PRODUCTBUILD < 8000
+struct _DISPATCHER_CONTEXT {
+  ULONG64 ControlPc;
+  ULONG64 ImageBase;
+  PRUNTIME_FUNCTION FunctionEntry;
+  ULONG64 EstablisherFrame;
+  ULONG64 TargetIp;
+  PCONTEXT ContextRecord;
+  PEXCEPTION_ROUTINE LanguageHandler;
+  PVOID HandlerData;
+  PUNWIND_HISTORY_TABLE HistoryTable;
+  ULONG ScopeIndex;
+  ULONG Fill0;
+};
+  #endif
+
+struct UNWIND_INFO {
+  uint8_t Version : 3;
+  uint8_t Flags : 5;
+  uint8_t SizeOfProlog;
+  uint8_t CountOfCodes;
+  uint8_t FrameRegister : 4;
+  uint8_t FrameOffset : 4;
+  uint16_t UnwindCodes[2];
+};
+
+extern "C" _Unwind_Reason_Code __libunwind_seh_personality(
+    int, _Unwind_Action, uint64_t, _Unwind_Exception *,
+    struct _Unwind_Context *);
+
+#endif
+
 #include "config.h"
 
 #include "AddressSpace.hpp"
@@ -412,6 +453,472 @@
 #endif
 };
 
+#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32)
+
+/// \c UnwindCursor contains all state (including all register values) during
+/// an unwind.  This is normally stack-allocated inside a unw_cursor_t.
+template <typename A, typename R>
+class UnwindCursor : public AbstractUnwindCursor {
+  typedef typename A::pint_t pint_t;
+public:
+                      UnwindCursor(unw_context_t *context, A &as);
+                      UnwindCursor(CONTEXT *context, A &as);
+                      UnwindCursor(A &as, void *threadArg);
+  virtual             ~UnwindCursor() {}
+  virtual bool        validReg(int);
+  virtual unw_word_t  getReg(int);
+  virtual void        setReg(int, unw_word_t);
+  virtual bool        validFloatReg(int);
+  virtual unw_fpreg_t getFloatReg(int);
+  virtual void        setFloatReg(int, unw_fpreg_t);
+  virtual int         step();
+  virtual void        getInfo(unw_proc_info_t *);
+  virtual void        jumpto();
+  virtual bool        isSignalFrame();
+  virtual bool        getFunctionName(char *buf, size_t len, unw_word_t *off);
+  virtual void        setInfoBasedOnIPRegister(bool isReturnAddress = false);
+  virtual const char *getRegisterName(int num);
+#ifdef __arm__
+  virtual void        saveVFPAsX();
+#endif
+
+  DISPATCHER_CONTEXT *getDispatcherContext() { return &_dispContext; }
+  void setDispatcherContext(DISPATCHER_CONTEXT *disp) { _dispContext = *disp; }
+
+private:
+
+  pint_t getLastPC() const { return _dispContext.ControlPc; }
+  void setLastPC(pint_t pc) { _dispContext.ControlPc = pc; }
+  RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) {
+    _dispContext.FunctionEntry = RtlLookupFunctionEntry(pc,
+                                                        &_dispContext.ImageBase,
+                                                        _dispContext.HistoryTable);
+    *base = _dispContext.ImageBase;
+    return _dispContext.FunctionEntry;
+  }
+  bool getInfoFromSEH(pint_t pc);
+  int stepWithSEHData() {
+    _dispContext.LanguageHandler = RtlVirtualUnwind(UNW_FLAG_UHANDLER,
+                                                    _dispContext.ImageBase,
+                                                    _dispContext.ControlPc,
+                                                    _dispContext.FunctionEntry,
+                                                    _dispContext.ContextRecord,
+                                                    &_dispContext.HandlerData,
+                                                    &_dispContext.EstablisherFrame,
+                                                    NULL);
+    // Update some fields of the unwind info now, since we have them.
+    _info.lsda = reinterpret_cast<unw_word_t>(_dispContext.HandlerData);
+    if (_dispContext.LanguageHandler) {
+      _info.handler = reinterpret_cast<unw_word_t>(__libunwind_seh_personality);
+    } else
+      _info.handler = 0;
+    return UNW_STEP_SUCCESS;
+  }
+
+  A                   &_addressSpace;
+  unw_proc_info_t      _info;
+  DISPATCHER_CONTEXT   _dispContext;
+  CONTEXT              _msContext;
+  UNWIND_HISTORY_TABLE _histTable;
+  bool                 _unwindInfoMissing;
+};
+
+
+template <typename A, typename R>
+UnwindCursor<A, R>::UnwindCursor(unw_context_t *context, A &as)
+    : _addressSpace(as), _unwindInfoMissing(false) {
+  static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit),
+                "UnwindCursor<> does not fit in unw_cursor_t");
+  memset(&_info, 0, sizeof(_info));
+  memset(&_histTable, 0, sizeof(_histTable));
+  _dispContext.ContextRecord = &_msContext;
+  _dispContext.HistoryTable = &_histTable;
+  // Initialize MS context from ours.
+  R r(context);
+  _msContext.ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_FLOATING_POINT;
+#if defined(_LIBUNWIND_TARGET_X86_64)
+  _msContext.Rax = r.getRegister(UNW_X86_64_RAX);
+  _msContext.Rcx = r.getRegister(UNW_X86_64_RCX);
+  _msContext.Rdx = r.getRegister(UNW_X86_64_RDX);
+  _msContext.Rbx = r.getRegister(UNW_X86_64_RBX);
+  _msContext.Rsp = r.getRegister(UNW_X86_64_RSP);
+  _msContext.Rbp = r.getRegister(UNW_X86_64_RBP);
+  _msContext.Rsi = r.getRegister(UNW_X86_64_RSI);
+  _msContext.Rdi = r.getRegister(UNW_X86_64_RDI);
+  _msContext.R8 = r.getRegister(UNW_X86_64_R8);
+  _msContext.R9 = r.getRegister(UNW_X86_64_R9);
+  _msContext.R10 = r.getRegister(UNW_X86_64_R10);
+  _msContext.R11 = r.getRegister(UNW_X86_64_R11);
+  _msContext.R12 = r.getRegister(UNW_X86_64_R12);
+  _msContext.R13 = r.getRegister(UNW_X86_64_R13);
+  _msContext.R14 = r.getRegister(UNW_X86_64_R14);
+  _msContext.R15 = r.getRegister(UNW_X86_64_R15);
+  _msContext.Rip = r.getRegister(UNW_REG_IP);
+  union {
+    v128 v;
+    M128A m;
+  } t;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM0);
+  _msContext.Xmm0 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM1);
+  _msContext.Xmm1 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM2);
+  _msContext.Xmm2 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM3);
+  _msContext.Xmm3 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM4);
+  _msContext.Xmm4 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM5);
+  _msContext.Xmm5 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM6);
+  _msContext.Xmm6 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM7);
+  _msContext.Xmm7 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM8);
+  _msContext.Xmm8 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM9);
+  _msContext.Xmm9 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM10);
+  _msContext.Xmm10 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM11);
+  _msContext.Xmm11 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM12);
+  _msContext.Xmm12 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM13);
+  _msContext.Xmm13 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM14);
+  _msContext.Xmm14 = t.m;
+  t.v = r.getVectorRegister(UNW_X86_64_XMM15);
+  _msContext.Xmm15 = t.m;
+#elif defined(_LIBUNWIND_TARGET_ARM)
+  _msContext.R0 = r.getRegister(UNW_ARM_R0);
+  _msContext.R1 = r.getRegister(UNW_ARM_R1);
+  _msContext.R2 = r.getRegister(UNW_ARM_R2);
+  _msContext.R3 = r.getRegister(UNW_ARM_R3);
+  _msContext.R4 = r.getRegister(UNW_ARM_R4);
+  _msContext.R5 = r.getRegister(UNW_ARM_R5);
+  _msContext.R6 = r.getRegister(UNW_ARM_R6);
+  _msContext.R7 = r.getRegister(UNW_ARM_R7);
+  _msContext.R8 = r.getRegister(UNW_ARM_R8);
+  _msContext.R9 = r.getRegister(UNW_ARM_R9);
+  _msContext.R10 = r.getRegister(UNW_ARM_R10);
+  _msContext.R11 = r.getRegister(UNW_ARM_R11);
+  _msContext.R12 = r.getRegister(UNW_ARM_R12);
+  _msContext.Sp = r.getRegister(UNW_ARM_SP);
+  _msContext.Lr = r.getRegister(UNW_ARM_LR);
+  _msContext.Pc = r.getRegister(UNW_ARM_PC);
+  for (int r = UNW_ARM_D0; r <= UNW_ARM_D31; ++r) {
+    union {
+      uint64_t w;
+      double d;
+    } d;
+    d.d = r.getFloatRegister(r);
+    _msContext.D[r - UNW_ARM_D0] = d.w;
+  }
+#endif
+}
+
+template <typename A, typename R>
+UnwindCursor<A, R>::UnwindCursor(CONTEXT *context, A &as)
+    : _addressSpace(as), _unwindInfoMissing(false) {
+  static_assert((check_fit<UnwindCursor<A, R>, unw_cursor_t>::does_fit),
+                "UnwindCursor<> does not fit in unw_cursor_t");
+  memset(&_info, 0, sizeof(_info));
+  memset(&_histTable, 0, sizeof(_histTable));
+  _dispContext.ContextRecord = &_msContext;
+  _dispContext.HistoryTable = &_histTable;
+  _msContext = *context;
+}
+
+
+template <typename A, typename R>
+bool UnwindCursor<A, R>::validReg(int regNum) {
+  if (regNum == UNW_REG_IP || regNum == UNW_REG_SP) return true;
+#if defined(_LIBUNWIND_TARGET_X86_64)
+  if (regNum >= UNW_X86_64_RAX && regNum <= UNW_X86_64_R15) return true;
+#elif defined(_LIBUNWIND_TARGET_ARM)
+  if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) return true;
+#endif
+  return false;
+}
+
+template <typename A, typename R>
+unw_word_t UnwindCursor<A, R>::getReg(int regNum) {
+  switch (regNum) {
+#if defined(_LIBUNWIND_TARGET_X86_64)
+  case UNW_REG_IP: return _msContext.Rip;
+  case UNW_X86_64_RAX: return _msContext.Rax;
+  case UNW_X86_64_RDX: return _msContext.Rdx;
+  case UNW_X86_64_RCX: return _msContext.Rcx;
+  case UNW_X86_64_RBX: return _msContext.Rbx;
+  case UNW_REG_SP:
+  case UNW_X86_64_RSP: return _msContext.Rsp;
+  case UNW_X86_64_RBP: return _msContext.Rbp;
+  case UNW_X86_64_RSI: return _msContext.Rsi;
+  case UNW_X86_64_RDI: return _msContext.Rdi;
+  case UNW_X86_64_R8: return _msContext.R8;
+  case UNW_X86_64_R9: return _msContext.R9;
+  case UNW_X86_64_R10: return _msContext.R10;
+  case UNW_X86_64_R11: return _msContext.R11;
+  case UNW_X86_64_R12: return _msContext.R12;
+  case UNW_X86_64_R13: return _msContext.R13;
+  case UNW_X86_64_R14: return _msContext.R14;
+  case UNW_X86_64_R15: return _msContext.R15;
+#elif defined(_LIBUNWIND_TARGET_ARM)
+  case UNW_ARM_R0: return _msContext.R0;
+  case UNW_ARM_R1: return _msContext.R1;
+  case UNW_ARM_R2: return _msContext.R2;
+  case UNW_ARM_R3: return _msContext.R3;
+  case UNW_ARM_R4: return _msContext.R4;
+  case UNW_ARM_R5: return _msContext.R5;
+  case UNW_ARM_R6: return _msContext.R6;
+  case UNW_ARM_R7: return _msContext.R7;
+  case UNW_ARM_R8: return _msContext.R8;
+  case UNW_ARM_R9: return _msContext.R9;
+  case UNW_ARM_R10: return _msContext.R10;
+  case UNW_ARM_R11: return _msContext.R11;
+  case UNW_ARM_R12: return _msContext.R12;
+  case UNW_REG_SP:
+  case UNW_ARM_SP: return _msContext.Sp;
+  case UNW_ARM_LR: return _msContext.Lr;
+  case UNW_REG_IP:
+  case UNW_ARM_PC: return _msContext.Pc;
+#endif
+  }
+  _LIBUNWIND_ABORT("unsupported register");
+}
+
+template <typename A, typename R>
+void UnwindCursor<A, R>::setReg(int regNum, unw_word_t value) {
+  switch (regNum) {
+#if defined(_LIBUNWIND_TARGET_X86_64)
+  case UNW_REG_IP: _msContext.Rip = value; break;
+  case UNW_X86_64_RAX: _msContext.Rax = value; break;
+  case UNW_X86_64_RDX: _msContext.Rdx = value; break;
+  case UNW_X86_64_RCX: _msContext.Rcx = value; break;
+  case UNW_X86_64_RBX: _msContext.Rbx = value; break;
+  case UNW_REG_SP:
+  case UNW_X86_64_RSP: _msContext.Rsp = value; break;
+  case UNW_X86_64_RBP: _msContext.Rbp = value; break;
+  case UNW_X86_64_RSI: _msContext.Rsi = value; break;
+  case UNW_X86_64_RDI: _msContext.Rdi = value; break;
+  case UNW_X86_64_R8: _msContext.R8 = value; break;
+  case UNW_X86_64_R9: _msContext.R9 = value; break;
+  case UNW_X86_64_R10: _msContext.R10 = value; break;
+  case UNW_X86_64_R11: _msContext.R11 = value; break;
+  case UNW_X86_64_R12: _msContext.R12 = value; break;
+  case UNW_X86_64_R13: _msContext.R13 = value; break;
+  case UNW_X86_64_R14: _msContext.R14 = value; break;
+  case UNW_X86_64_R15: _msContext.R15 = value; break;
+#elif defined(_LIBUNWIND_TARGET_ARM)
+  case UNW_ARM_R0: _msContext.R0 = value; break;
+  case UNW_ARM_R1: _msContext.R1 = value; break;
+  case UNW_ARM_R2: _msContext.R2 = value; break;
+  case UNW_ARM_R3: _msContext.R3 = value; break;
+  case UNW_ARM_R4: _msContext.R4 = value; break;
+  case UNW_ARM_R5: _msContext.R5 = value; break;
+  case UNW_ARM_R6: _msContext.R6 = value; break;
+  case UNW_ARM_R7: _msContext.R7 = value; break;
+  case UNW_ARM_R8: _msContext.R8 = value; break;
+  case UNW_ARM_R9: _msContext.R9 = value; break;
+  case UNW_ARM_R10: _msContext.R10 = value; break;
+  case UNW_ARM_R11: _msContext.R11 = value; break;
+  case UNW_ARM_R12: _msContext.R12 = value; break;
+  case UNW_REG_SP:
+  case UNW_ARM_SP: _msContext.Sp = value; break;
+  case UNW_ARM_LR: _msContext.Lr = value; break;
+  case UNW_REG_IP:
+  case UNW_ARM_PC: _msContext.Pc = value; break;
+#endif
+  default:
+    _LIBUNWIND_ABORT("unsupported register");
+  }
+}
+
+template <typename A, typename R>
+bool UnwindCursor<A, R>::validFloatReg(int regNum) {
+#if defined(_LIBUNWIND_TARGET_ARM)
+  if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) return true;
+  if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) return true;
+#endif
+  return false;
+}
+
+template <typename A, typename R>
+unw_fpreg_t UnwindCursor<A, R>::getFloatReg(int regNum) {
+#if defined(_LIBUNWIND_TARGET_ARM)
+  if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) {
+    union {
+      uint32_t w;
+      float f;
+    } d;
+    d.w = _msContext.S[regNum - UNW_ARM_S0];
+    return d.f;
+  }
+  if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) {
+    union {
+      uint64_t w;
+      double d;
+    } d;
+    d.w = _msContext.D[regNum - UNW_ARM_D0];
+    return d.d;
+  }
+  _LIBUNWIND_ABORT("unsupported float register");
+#else
+  _LIBUNWIND_ABORT("float registers unimplemented");
+#endif
+}
+
+template <typename A, typename R>
+void UnwindCursor<A, R>::setFloatReg(int regNum, unw_fpreg_t value) {
+#if defined(_LIBUNWIND_TARGET_ARM)
+  if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) {
+    union {
+      uint32_t w;
+      float f;
+    } d;
+    d.f = value;
+    _msContext.S[regNum - UNW_ARM_S0] = d.w;
+  }
+  if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) {
+    union {
+      uint64_t w;
+      double d;
+    } d;
+    d.d = value;
+    _msContext.D[regNum - UNW_ARM_D0] = d.w;
+  }
+  _LIBUNWIND_ABORT("unsupported float register");
+#else
+  _LIBUNWIND_ABORT("float registers unimplemented");
+#endif
+}
+
+template <typename A, typename R> void UnwindCursor<A, R>::jumpto() {
+  RtlRestoreContext(&_msContext, nullptr);
+}
+
+#ifdef __arm__
+template <typename A, typename R> void UnwindCursor<A, R>::saveVFPAsX() {}
+#endif
+
+template <typename A, typename R>
+const char *UnwindCursor<A, R>::getRegisterName(int regNum) {
+  switch (regNum) {
+#if defined(_LIBUNWIND_TARGET_X86_64)
+  case UNW_REG_IP: return "rip";
+  case UNW_X86_64_RAX: return "rax";
+  case UNW_X86_64_RDX: return "rdx";
+  case UNW_X86_64_RCX: return "rcx";
+  case UNW_X86_64_RBX: return "rbx";
+  case UNW_REG_SP:
+  case UNW_X86_64_RSP: return "rsp";
+  case UNW_X86_64_RBP: return "rbp";
+  case UNW_X86_64_RSI: return "rsi";
+  case UNW_X86_64_RDI: return "rdi";
+  case UNW_X86_64_R8: return "r8";
+  case UNW_X86_64_R9: return "r9";
+  case UNW_X86_64_R10: return "r10";
+  case UNW_X86_64_R11: return "r11";
+  case UNW_X86_64_R12: return "r12";
+  case UNW_X86_64_R13: return "r13";
+  case UNW_X86_64_R14: return "r14";
+  case UNW_X86_64_R15: return "r15";
+#elif defined(_LIBUNWIND_TARGET_ARM)
+  case UNW_ARM_R0: return "r0";
+  case UNW_ARM_R1: return "r1";
+  case UNW_ARM_R2: return "r2";
+  case UNW_ARM_R3: return "r3";
+  case UNW_ARM_R4: return "r4";
+  case UNW_ARM_R5: return "r5";
+  case UNW_ARM_R6: return "r6";
+  case UNW_ARM_R7: return "r7";
+  case UNW_ARM_R8: return "r8";
+  case UNW_ARM_R9: return "r9";
+  case UNW_ARM_R10: return "r10";
+  case UNW_ARM_R11: return "r11";
+  case UNW_ARM_R12: return "r12";
+  case UNW_REG_SP:
+  case UNW_ARM_SP: return "sp";
+  case UNW_ARM_LR: return "lr";
+  case UNW_REG_IP:
+  case UNW_ARM_PC: return "pc";
+  case UNW_ARM_S0: return "s0";
+  case UNW_ARM_S1: return "s1";
+  case UNW_ARM_S2: return "s2";
+  case UNW_ARM_S3: return "s3";
+  case UNW_ARM_S4: return "s4";
+  case UNW_ARM_S5: return "s5";
+  case UNW_ARM_S6: return "s6";
+  case UNW_ARM_S7: return "s7";
+  case UNW_ARM_S8: return "s8";
+  case UNW_ARM_S9: return "s9";
+  case UNW_ARM_S10: return "s10";
+  case UNW_ARM_S11: return "s11";
+  case UNW_ARM_S12: return "s12";
+  case UNW_ARM_S13: return "s13";
+  case UNW_ARM_S14: return "s14";
+  case UNW_ARM_S15: return "s15";
+  case UNW_ARM_S16: return "s16";
+  case UNW_ARM_S17: return "s17";
+  case UNW_ARM_S18: return "s18";
+  case UNW_ARM_S19: return "s19";
+  case UNW_ARM_S20: return "s20";
+  case UNW_ARM_S21: return "s21";
+  case UNW_ARM_S22: return "s22";
+  case UNW_ARM_S23: return "s23";
+  case UNW_ARM_S24: return "s24";
+  case UNW_ARM_S25: return "s25";
+  case UNW_ARM_S26: return "s26";
+  case UNW_ARM_S27: return "s27";
+  case UNW_ARM_S28: return "s28";
+  case UNW_ARM_S29: return "s29";
+  case UNW_ARM_S30: return "s30";
+  case UNW_ARM_S31: return "s31";
+  case UNW_ARM_D0: return "d0";
+  case UNW_ARM_D1: return "d1";
+  case UNW_ARM_D2: return "d2";
+  case UNW_ARM_D3: return "d3";
+  case UNW_ARM_D4: return "d4";
+  case UNW_ARM_D5: return "d5";
+  case UNW_ARM_D6: return "d6";
+  case UNW_ARM_D7: return "d7";
+  case UNW_ARM_D8: return "d8";
+  case UNW_ARM_D9: return "d9";
+  case UNW_ARM_D10: return "d10";
+  case UNW_ARM_D11: return "d11";
+  case UNW_ARM_D12: return "d12";
+  case UNW_ARM_D13: return "d13";
+  case UNW_ARM_D14: return "d14";
+  case UNW_ARM_D15: return "d15";
+  case UNW_ARM_D16: return "d16";
+  case UNW_ARM_D17: return "d17";
+  case UNW_ARM_D18: return "d18";
+  case UNW_ARM_D19: return "d19";
+  case UNW_ARM_D20: return "d20";
+  case UNW_ARM_D21: return "d21";
+  case UNW_ARM_D22: return "d22";
+  case UNW_ARM_D23: return "d23";
+  case UNW_ARM_D24: return "d24";
+  case UNW_ARM_D25: return "d25";
+  case UNW_ARM_D26: return "d26";
+  case UNW_ARM_D27: return "d27";
+  case UNW_ARM_D28: return "d28";
+  case UNW_ARM_D29: return "d29";
+  case UNW_ARM_D30: return "d30";
+  case UNW_ARM_D31: return "d31";
+#endif
+  default:
+    _LIBUNWIND_ABORT("unsupported register");
+  }
+}
+
+template <typename A, typename R> bool UnwindCursor<A, R>::isSignalFrame() {
+  return false;
+}
+
+#else  // !defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) || !defined(_WIN32)
+
 /// UnwindCursor contains all state (including all register values) during
 /// an unwind.  This is normally stack allocated inside a unw_cursor_t.
 template <typename A, typename R>
@@ -651,6 +1158,20 @@
 #endif
 #endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
 
+#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
+  // For runtime environments using SEH unwind data without Windows runtime
+  // support.
+  pint_t getLastPC() const { /* FIXME: Implement */ return 0; }
+  void setLastPC(pint_t pc) { /* FIXME: Implement */ }
+  RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) {
+    /* FIXME: Implement */
+    *base = 0;
+    return nullptr;
+  }
+  bool getInfoFromSEH(pint_t pc);
+  int stepWithSEHData() { /* FIXME: Implement */ return 0; }
+#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
+
 
   A               &_addressSpace;
   R                _registers;
@@ -727,6 +1248,8 @@
   return _isSignalFrame;
 }
 
+#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
+
 #if defined(_LIBUNWIND_ARM_EHABI)
 struct EHABIIndexEntry {
   uint32_t functionOffset;
@@ -1261,6 +1784,55 @@
 #endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
 
 
+#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
+template <typename A, typename R>
+bool UnwindCursor<A, R>::getInfoFromSEH(pint_t pc) {
+  pint_t base;
+  RUNTIME_FUNCTION *unwindEntry = lookUpSEHUnwindInfo(pc, &base);
+  if (!unwindEntry) {
+    _LIBUNWIND_DEBUG_LOG("\tpc not in table, pc=0x%llX", (uint64_t) pc);
+    return false;
+  }
+  _info.gp = 0;
+  _info.flags = 0;
+  _info.format = 0;
+  _info.unwind_info_size = sizeof(RUNTIME_FUNCTION);
+  _info.unwind_info = reinterpret_cast<unw_word_t>(unwindEntry);
+  _info.extra = base;
+  _info.start_ip = base + unwindEntry->BeginAddress;
+#ifdef _LIBUNWIND_TARGET_X86_64
+  _info.end_ip = base + unwindEntry->EndAddress;
+  // Only fill in the handler and LSDA if they're stale.
+  if (pc != getLastPC()) {
+    UNWIND_INFO *xdata = reinterpret_cast<UNWIND_INFO *>(base + unwindEntry->UnwindData);
+    if (xdata->Flags & (UNW_FLAG_EHANDLER|UNW_FLAG_UHANDLER)) {
+      // The personality is given in the UNWIND_INFO itself. The LSDA immediately
+      // follows the UNWIND_INFO. (This follows how both Clang and MSVC emit
+      // these structures.)
+      // N.B. UNWIND_INFO structs are DWORD-aligned.
+      uint32_t lastcode = (xdata->CountOfCodes + 1) & ~1;
+      const uint32_t *handler = reinterpret_cast<uint32_t *>(&xdata->UnwindCodes[lastcode]);
+      _info.lsda = reinterpret_cast<unw_word_t>(handler+1);
+      if (*handler) {
+        _info.handler = reinterpret_cast<unw_word_t>(__libunwind_seh_personality);
+      } else
+        _info.handler = 0;
+    } else {
+      _info.lsda = 0;
+      _info.handler = 0;
+    }
+  }
+#elif defined(_LIBUNWIND_TARGET_ARM)
+  _info.end_ip = _info.start_ip + unwindEntry->FunctionLength;
+  _info.lsda = 0; // FIXME
+  _info.handler = 0; // FIXME
+#endif
+  setLastPC(pc);
+  return true;
+}
+#endif
+
+
 template <typename A, typename R>
 void UnwindCursor<A, R>::setInfoBasedOnIPRegister(bool isReturnAddress) {
   pint_t pc = (pint_t)this->getReg(UNW_REG_IP);
@@ -1304,6 +1876,12 @@
     }
 #endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
 
+#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
+    // If there is SEH unwind info, look there next.
+    if (this->getInfoFromSEH(pc))
+      return;
+#endif
+
 #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
     // If there is dwarf unwind info, look there next.
     if (sects.dwarf_section != 0) {
@@ -1396,12 +1974,15 @@
   int result;
 #if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND)
   result = this->stepWithCompactEncoding();
+#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND)
+  result = this->stepWithSEHData();
 #elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
   result = this->stepWithDwarfFDE();
 #elif defined(_LIBUNWIND_ARM_EHABI)
   result = this->stepWithEHABI();
 #else
   #error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or \
+              _LIBUNWIND_SUPPORT_SEH_UNWIND or \
               _LIBUNWIND_SUPPORT_DWARF_UNWIND or \
               _LIBUNWIND_ARM_EHABI
 #endif