diff --git a/lib/Interpreter/Compatibility.h b/lib/Interpreter/Compatibility.h index 0a0dfaa4..4ad9f8fd 100644 --- a/lib/Interpreter/Compatibility.h +++ b/lib/Interpreter/Compatibility.h @@ -72,9 +72,11 @@ static inline char* GetEnv(const char* Var_Name) { #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/MachO.h" #include "llvm/Config/llvm-config.h" #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/Object/MachO.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Path.h" diff --git a/lib/Interpreter/CppInterOp.cpp b/lib/Interpreter/CppInterOp.cpp index 111c3897..4f1862a7 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -37,6 +37,7 @@ #include #include +#include #include // Stream redirect. @@ -54,7 +55,43 @@ #include #endif // WIN32 -#include +#ifdef __APPLE__ +// Define a minimal mach header for JIT'd code, to support exceptions on osx 14 +// and later. See llvm/llvm-project#49036 +static llvm::MachO::mach_header_64 fake_mach_header = { + .magic = llvm::MachO::MH_MAGIC_64, + .cputype = llvm::MachO::CPU_TYPE_ARM64, + .cpusubtype = llvm::MachO::CPU_SUBTYPE_ARM64_ALL, + .filetype = llvm::MachO::MH_DYLIB, + .ncmds = 0, + .sizeofcmds = 0, + .flags = 0, + .reserved = 0}; + +// Declare libunwind SPI types and functions. +struct unw_dynamic_unwind_sections { + uintptr_t dso_base; + uintptr_t dwarf_section; + size_t dwarf_section_length; + uintptr_t compact_unwind_section; + size_t compact_unwind_section_length; +}; + +int find_dynamic_unwind_sections(uintptr_t addr, + unw_dynamic_unwind_sections* info) { + info->dso_base = (uintptr_t)&fake_mach_header; + info->dwarf_section = 0; + info->dwarf_section_length = 0; + info->compact_unwind_section = 0; + info->compact_unwind_section_length = 0; + return 1; +} + +// Typedef for callback above. +typedef int (*unw_find_dynamic_unwind_sections)( + uintptr_t addr, struct unw_dynamic_unwind_sections* info); + +#endif // __APPLE__ namespace Cpp { @@ -71,7 +108,15 @@ namespace Cpp { // This might fix the issue https://reviews.llvm.org/D107087 // FIXME: For now we just leak the Interpreter. struct InterpDeleter { - ~InterpDeleter() = default; + ~InterpDeleter() { +#ifdef __APPLE__ + if (auto* unw_remove_find_dynamic_unwind_sections = (int (*)( + unw_find_dynamic_unwind_sections find_dynamic_unwind_sections)) + dlsym(RTLD_DEFAULT, "__unw_remove_find_dynamic_unwind_sections")) + unw_remove_find_dynamic_unwind_sections(find_dynamic_unwind_sections); +#endif + // sInterpreter.release(); + } } Deleter; static compat::Interpreter& getInterp() { @@ -2665,6 +2710,8 @@ namespace Cpp { #ifdef _WIN32 // FIXME : Workaround Sema::PushDeclContext assert on windows ClingArgv.push_back("-fno-delayed-template-parsing"); +#elif __APPLE__ + ClingArgv.push_back("-fforce-dwarf-frame"); #endif ClingArgv.insert(ClingArgv.end(), Args.begin(), Args.end()); // To keep the Interpreter creation interface between cling and clang-repl @@ -2716,6 +2763,14 @@ namespace Cpp { // FIXME: Enable this assert once we figure out how to fix the multiple // calls to CreateInterpreter. //assert(!sInterpreter && "Interpreter already set."); +#ifdef __APPLE__ + // Add a handler to support exceptions from interpreted code. + // See llvm/llvm-project#49036 + if (auto* unw_add_find_dynamic_unwind_sections = (int (*)( + unw_find_dynamic_unwind_sections find_dynamic_unwind_sections)) + dlsym(RTLD_DEFAULT, "__unw_add_find_dynamic_unwind_sections")) + unw_add_find_dynamic_unwind_sections(find_dynamic_unwind_sections); +#endif // __APPLE__ sInterpreter = I; return I; } diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 0d9ed77a..ad1bee9b 100644 --- a/unittests/CppInterOp/InterpreterTest.cpp +++ b/unittests/CppInterOp/InterpreterTest.cpp @@ -230,3 +230,25 @@ if (llvm::sys::RunningOnValgrind()) delete ExtInterp; #endif } + +TEST(InterpreterTest, InterpreterExceptions) { + Cpp::CreateInterpreter(); + EXPECT_TRUE(Cpp::Declare("int f() { throw 1; return 2; }") == 0); + EXPECT_TRUE( + Cpp::Process( + "int ex() { try { f(); return 0; } catch(...){return 1;} }") == 0); + EXPECT_EQ(Cpp::Evaluate("ex()"), 1) + << "Failed to catch exceptions in interpreter"; +} + +TEST(InterpreterTest, InterpreterExceptionsCompiledCode) { + Cpp::CreateInterpreter(); + bool caught = false; + try { + EXPECT_TRUE(Cpp::Declare("int f() { throw 1; return 2; }") == 0); + EXPECT_TRUE(Cpp::Process("int res = f();") == 0); + } catch (...) { + caught = true; + } + EXPECT_TRUE(caught) << "Unable to catch exception coming from interpreter"; +}