From 3a6db232b9077c536c59d3d8af9673f00894b46b Mon Sep 17 00:00:00 2001 From: maximusron Date: Thu, 2 May 2024 19:01:15 +0200 Subject: [PATCH] Add llvm libunwind callback to suppress exceptions on apple silicon See llvm/llvm-project#49036 --- lib/Interpreter/Compatibility.h | 2 + lib/Interpreter/CppInterOp.cpp | 56 +++++++++++++++++++++++- unittests/CppInterOp/InterpreterTest.cpp | 22 ++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/lib/Interpreter/Compatibility.h b/lib/Interpreter/Compatibility.h index ecc5a3be..49cc2156 100644 --- a/lib/Interpreter/Compatibility.h +++ b/lib/Interpreter/Compatibility.h @@ -23,9 +23,11 @@ #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 a1b3b97d..8f7bb876 100644 --- a/lib/Interpreter/CppInterOp.cpp +++ b/lib/Interpreter/CppInterOp.cpp @@ -49,6 +49,44 @@ #include #endif // WIN32 +#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 { using namespace clang; @@ -62,7 +100,15 @@ namespace Cpp { // This might fix the issue https://reviews.llvm.org/D107087 // FIXME: For now we just leak the Interpreter. struct InterpDeleter { - ~InterpDeleter() { sInterpreter.release(); } + ~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() { @@ -2612,6 +2658,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.reset(I); return I; } diff --git a/unittests/CppInterOp/InterpreterTest.cpp b/unittests/CppInterOp/InterpreterTest.cpp index 01b3bb11..90faa44e 100644 --- a/unittests/CppInterOp/InterpreterTest.cpp +++ b/unittests/CppInterOp/InterpreterTest.cpp @@ -132,3 +132,25 @@ TEST(InterpreterTest, CodeCompletion) { GTEST_SKIP(); #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"; +}