From 2264e5b79437b8471a7a5d1837cca909a09400fc Mon Sep 17 00:00:00 2001
From: Frederich Munch <marsupial@users.noreply.github.com>
Date: Fri, 3 Mar 2017 06:55:01 -0500
Subject: [PATCH] Windows: Exception handling.

---
 .../cling/include/cling/Utils/Platform.h      |   9 +
 .../cling/lib/Interpreter/CIFactory.cpp       |  27 +--
 .../cling/lib/Interpreter/IncrementalJIT.cpp  |  25 +++
 .../cling/lib/Interpreter/Interpreter.cpp     |   9 +
 interpreter/cling/lib/Utils/PlatformWin.cpp   | 188 ++++++++++++++++++
 5 files changed, 236 insertions(+), 22 deletions(-)

diff --git a/interpreter/cling/include/cling/Utils/Platform.h b/interpreter/cling/include/cling/Utils/Platform.h
index 07f34e2fc11..f87c86c880f 100644
--- a/interpreter/cling/include/cling/Utils/Platform.h
+++ b/interpreter/cling/include/cling/Utils/Platform.h
@@ -153,6 +153,15 @@ inline namespace windows {
                            std::string* UniversalSDK = nullptr,
                            bool Verbose = false);
 
+  ///\brief Runtime override for _CxxThrowException in Interpreter.
+  //
+  __declspec(noreturn) void __stdcall ClingRaiseSEHException(void*, void*);
+
+  void RegisterEHFrames(uint8_t* Addr, size_t Size, uintptr_t BaseAddr,
+                        bool Block);
+
+  void DeRegisterEHFrames(uint8_t* Addr, size_t Size);
+
 } // namespace windows
 #endif // LLVM_ON_WIN32
 
diff --git a/interpreter/cling/lib/Interpreter/CIFactory.cpp b/interpreter/cling/lib/Interpreter/CIFactory.cpp
index a0ff8ab5694..07e2eaab065 100644
--- a/interpreter/cling/lib/Interpreter/CIFactory.cpp
+++ b/interpreter/cling/lib/Interpreter/CIFactory.cpp
@@ -347,24 +347,6 @@ namespace {
   static void SetClingCustomLangOpts(LangOptions& Opts) {
     Opts.EmitAllDecls = 0; // Otherwise if PCH attached will codegen all decls.
 #ifdef _MSC_VER
-#if 0 //_HAS_EXCEPTIONS
-// FIXME: Disable exceptions on Windows for the time being.
-// Enabling exceptions here makes 42 more tests failing.
-// For example, CodeGeneration\RecursiveInit.C raises a unhandled exception in
-// WinEHPrepare.cpp, at:
-//      if (UserI->isEHPad())
-//        report_fatal_error("Cleanup funclets for the MSVC++ personality cannot "
-//                           "contain exceptional actions");
-    Opts.Exceptions = 1;
-    if (Opts.CPlusPlus) {
-      Opts.CXXExceptions = 1;
-    }
-#else
-    Opts.Exceptions = 0;
-    if (Opts.CPlusPlus) {
-      Opts.CXXExceptions = 0;
-    }
-#endif // _HAS_EXCEPTIONS
 #ifdef _DEBUG
     // FIXME: This requires bufferoverflowu.lib, but adding:
     // #pragma comment(lib, "bufferoverflowu.lib") still gives errors!
@@ -378,16 +360,17 @@ namespace {
     Opts.Trigraphs = 0;
     Opts.setDefaultCallingConv(clang::LangOptions::DCC_CDecl);
 #else // !_MSC_VER
-    Opts.Exceptions = 1;
-    if (Opts.CPlusPlus) {
-      Opts.CXXExceptions = 1;
-    }
     Opts.Trigraphs = 1;
 //    Opts.RTTIData = 1;
 //    Opts.DefaultCallingConventions = 1;
 //    Opts.StackProtector = 0;
 #endif // _MSC_VER
 
+    Opts.Exceptions = 1;
+    if (Opts.CPlusPlus) {
+      Opts.CXXExceptions = 1;
+    }
+
     Opts.Deprecated = 1;
     //Opts.Modules = 1;
 
diff --git a/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp b/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp
index c823e4d16c3..f40e7955869 100644
--- a/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp
+++ b/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp
@@ -127,6 +127,23 @@ class Azog: public RTDyldMemoryManager {
   AllocInfo m_ROData;
   AllocInfo m_RWData;
 
+#ifdef LLVM_ON_WIN32
+  uintptr_t getBaseAddr() const {
+    if (LLVM_LIKELY(m_Code.m_Start && m_ROData.m_Start && m_RWData.m_Start)) {
+      return uintptr_t(std::min(std::min(m_Code.m_Start, m_ROData.m_Start),
+                                m_RWData.m_Start));
+    }
+    if (LLVM_LIKELY(m_Code.m_Start)) {
+      return uintptr_t(m_ROData.m_Start
+                           ? std::min(m_Code.m_Start, m_ROData.m_Start)
+                           : std::min(m_Code.m_Start, m_RWData.m_Start));
+    }
+    return uintptr_t(m_ROData.m_Start && m_RWData.m_Start
+                         ? std::min(m_ROData.m_Start, m_RWData.m_Start)
+                         : std::max(m_ROData.m_Start, m_RWData.m_Start));
+  }
+#endif
+
 public:
   Azog(cling::IncrementalJIT& Jit): m_jit(Jit) {}
 
@@ -183,12 +200,20 @@ public:
 
   void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr,
                         size_t Size) override {
+#ifdef LLVM_ON_WIN32
+    platform::RegisterEHFrames(Addr, Size, getBaseAddr(), true);
+#else
     return getExeMM()->registerEHFrames(Addr, LoadAddr, Size);
+#endif
   }
 
   void deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr,
                           size_t Size) override {
+#ifdef LLVM_ON_WIN32
+    platform::DeRegisterEHFrames(Addr, Size);
+#else
     return getExeMM()->deregisterEHFrames(Addr, LoadAddr, Size);
+#endif
   }
 
   uint64_t getSymbolAddress(const std::string &Name) override {
diff --git a/interpreter/cling/lib/Interpreter/Interpreter.cpp b/interpreter/cling/lib/Interpreter/Interpreter.cpp
index fbdb3b5d9d5..aa3e9a56fd6 100644
--- a/interpreter/cling/lib/Interpreter/Interpreter.cpp
+++ b/interpreter/cling/lib/Interpreter/Interpreter.cpp
@@ -9,6 +9,9 @@
 
 #include "cling/Interpreter/Interpreter.h"
 #include "cling/Utils/Paths.h"
+#ifdef LLVM_ON_WIN32
+#include "cling/Utils/Platform.h"
+#endif
 #include "ClingUtils.h"
 
 #include "DynamicLookup.h"
@@ -431,6 +434,12 @@ namespace cling {
                             utils::FunctionToVoidPtr(&local_cxa_atexit), true);
       // __dso_handle is inserted for the link phase, as macro is useless then
       m_Executor->addSymbol("__dso_handle", this, true);
+
+#ifdef LLVM_ON_WIN32
+      // Windows C++ SEH handler
+      m_Executor->addSymbol("_CxxThrowException",
+          utils::FunctionToVoidPtr(&platform::ClingRaiseSEHException), true);
+#endif
     }
 
     if (m_Opts.Verbose())
diff --git a/interpreter/cling/lib/Utils/PlatformWin.cpp b/interpreter/cling/lib/Utils/PlatformWin.cpp
index 3702b8c9b86..1da32452891 100644
--- a/interpreter/cling/lib/Utils/PlatformWin.cpp
+++ b/interpreter/cling/lib/Utils/PlatformWin.cpp
@@ -20,8 +20,10 @@
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/Path.h"
 
+#include <map>
 #include <sstream>
 #include <stdlib.h>
+#include <vector>
 
 #ifndef WIN32_LEAN_AND_MEAN
  #define WIN32_LEAN_AND_MEAN
@@ -699,6 +701,192 @@ std::string Demangle(const std::string& Symbol) {
   return af.Str ? std::string(af.Str) : std::string();
 }
 
+namespace windows {
+// Use less memory and store the function ranges to watch as a mapping
+// between of BaseAddr to Ranges watched.
+//
+typedef std::vector<std::pair<DWORD, DWORD>> ImageRanges;
+typedef std::map<uintptr_t, ImageRanges> ImageBaseMap;
+
+ImageBaseMap& getImageBaseMap() {
+  static ImageBaseMap sMap;
+  return sMap;
+}
+
+// Merge overlaping ranges
+static void MergeRanges(ImageRanges &Ranges) {
+  std::sort(Ranges.begin(), Ranges.end());
+
+  ImageRanges Merged;
+  ImageRanges::iterator It = Ranges.begin();
+  auto Current = *(It)++;
+  while (It != Ranges.end()) {
+    if (Current.second+1 < It->first) {
+      Merged.push_back(Current);
+      Current = *(It);
+    } else
+      Current.second = std::max(Current.second, It->second);
+    ++It;
+  }
+  Merged.emplace_back(Current);
+  Ranges.swap(Merged);
+}
+
+static uintptr_t FindEHFrame(uintptr_t Caller) {
+  ImageBaseMap& Unwind = getImageBaseMap();
+  for (auto&& Itr : Unwind) {
+    const uintptr_t CurAddr = Itr.first;
+    for (auto&& Rng : Itr.second) {
+      if (Caller >=(CurAddr + Rng.first) &&
+          Caller <= (CurAddr + Rng.second)) {
+        return CurAddr;
+      }
+    }
+  }
+  return 0;
+}
+
+void RegisterEHFrames(uint8_t* A, size_t Size, uintptr_t BaseAddr, bool Block) {
+  assert(getImageBaseMap().find(DWORD64(A)) == getImageBaseMap().end());
+
+  PRUNTIME_FUNCTION RFunc = reinterpret_cast<PRUNTIME_FUNCTION>(A);
+  const size_t N = Size / sizeof(RUNTIME_FUNCTION);
+  ImageBaseMap::mapped_type &Ranges = getImageBaseMap()[BaseAddr];
+  if (Block) {
+    // Merge all unwind adresses into a single contiguous block
+    Ranges.emplace_back(std::numeric_limits<DWORD>::max(),
+                        std::numeric_limits<DWORD>::min());
+    ImageRanges::value_type& Range = Ranges.front();
+    for (PRUNTIME_FUNCTION It = RFunc, End = RFunc +N; It < End; ++It) {
+      Range.first = std::min(Range.first, It->BeginAddress);
+      Range.second = std::max(Range.second, It->EndAddress);
+    }
+  } else {
+    for (PRUNTIME_FUNCTION It = RFunc, End = RFunc +N; It < End; ++It)
+      Ranges.emplace_back(It->BeginAddress, It->EndAddress);
+
+    // Initial sort and merge
+    MergeRanges(Ranges);
+  }
+
+  ::RtlAddFunctionTable(RFunc, N, BaseAddr);
+}
+
+void DeRegisterEHFrames(uint8_t* Addr, size_t Size) {
+  assert(getImageBaseMap().find(DWORD64(Addr)) != getImageBaseMap().end());
+
+  ImageBaseMap& Unwind = getImageBaseMap();
+  Unwind.erase(Unwind.find(DWORD64(Addr)));
+  ::RtlDeleteFunctionTable(reinterpret_cast<PRUNTIME_FUNCTION>(Addr));
+}
+
+// Adapted from VisualStudio/VC/crt/src/vcruntime/throw.cpp
+#ifdef _WIN64
+ #define _EH_RELATIVE_OFFSETS 1
+#endif
+// The NT Exception # that we use
+#define EH_EXCEPTION_NUMBER ('msc' | 0xE0000000)
+// The magic # identifying this version
+#define EH_MAGIC_NUMBER1 0x19930520
+#define EH_PURE_MAGIC_NUMBER1 0x01994000
+// Number of parameters in exception record
+#define EH_EXCEPTION_PARAMETERS 4
+
+// A generic exception record
+struct EHExceptionRecord {
+  DWORD ExceptionCode;
+  DWORD ExceptionFlags;     // Flags determined by NT
+  _EXCEPTION_RECORD* ExceptionRecord; // Extra exception record (unused)
+  void* ExceptionAddress;   // Address at which exception occurred
+  DWORD NumberParameters;   // No. of parameters = EH_EXCEPTION_PARAMETERS
+  struct EHParameters {
+    DWORD  magicNumber;           // = EH_MAGIC_NUMBER1
+    void *pExceptionObject;       // Pointer to the actual object thrown
+    struct ThrowInfo *pThrowInfo; // Description of thrown object
+#if _EH_RELATIVE_OFFSETS
+    DWORD64 pThrowImageBase;      // Image base of thrown object
+#endif
+  } params;
+};
+
+__declspec(noreturn) void
+__stdcall ClingRaiseSEHException(void* CxxExcept, void* Info) {
+
+  uintptr_t Caller;
+  static_assert(sizeof(Caller) == sizeof(PVOID), "Size mismatch");
+
+  USHORT Frames = CaptureStackBackTrace(1, 1,(PVOID*)&Caller, NULL);
+  assert(Frames && "No frames captured");
+
+  const DWORD64 BaseAddr = FindEHFrame(Caller);
+  if (BaseAddr == 0)
+    _CxxThrowException(CxxExcept, (_ThrowInfo*) Info);
+
+  // A generic exception record
+  EHExceptionRecord Exception = {
+    EH_EXCEPTION_NUMBER,      // Exception number
+    EXCEPTION_NONCONTINUABLE, // Exception flags (we don't do resume)
+    nullptr,                  // Additional record (none)
+    nullptr,                  // Address of exception (OS fills in)
+    EH_EXCEPTION_PARAMETERS,  // Number of parameters
+    {
+      EH_MAGIC_NUMBER1,
+      CxxExcept,
+      (struct ThrowInfo*)Info,
+#if _EH_RELATIVE_OFFSETS
+      BaseAddr
+#endif
+    }
+  };
+
+  // const ThrowInfo* pTI = (const ThrowInfo*)Info;
+
+#ifdef THROW_ISWINRT
+  if (pTI && (THROW_ISWINRT((*pTI)))) {
+    // The pointer to the ExceptionInfo structure is stored sizeof(void*)
+    // infront of each WinRT Exception Info.
+    ULONG_PTR* EPtr = *reinterpret_cast<ULONG_PTR**>(CxxExcept);
+    EPtr--;
+
+    WINRTEXCEPTIONINFO** ppWei = reinterpret_cast<WINRTEXCEPTIONINFO**>(EPtr);
+    pTI = (*ppWei)->throwInfo;
+    (*ppWei)->PrepareThrow(ppWei);
+  }
+#endif
+
+  // If the throw info indicates this throw is from a pure region,
+  // set the magic number to the Pure one, so only a pure-region
+  // catch will see it.
+  //
+  // Also use the Pure magic number on Win64 if we were unable to
+  // determine an image base, since that was the old way to determine
+  // a pure throw, before the TI_IsPure bit was added to the FuncInfo
+  // attributes field.
+  if (Info != nullptr) {
+#ifdef THROW_ISPURE
+    if (THROW_ISPURE(*pTI))
+      Exception.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
+#if _EH_RELATIVE_OFFSETS
+    else
+#endif // _EH_RELATIVE_OFFSETS
+#endif // THROW_ISPURE
+
+#if 0 && _EH_RELATIVE_OFFSETS
+    if (Exception.params.pThrowImageBase == 0)
+      Exception.params.magicNumber = EH_PURE_MAGIC_NUMBER1;
+#endif // _EH_RELATIVE_OFFSETS
+  }
+
+// Hand it off to the OS:
+#if defined(_M_X64) && defined(_NTSUBSET_)
+  RtlRaiseException((PEXCEPTION_RECORD)&Exception);
+#else
+  RaiseException(Exception.ExceptionCode, Exception.ExceptionFlags,
+                 Exception.NumberParameters, (PULONG_PTR)&Exception.params);
+#endif
+}
+} // namespace windows
+
 } // namespace platform
 } // namespace utils
 } // namespace cling
-- 
GitLab