diff --git a/common/Darwin/DarwinMisc.cpp b/common/Darwin/DarwinMisc.cpp index 5f12d186f7..074067515f 100644 --- a/common/Darwin/DarwinMisc.cpp +++ b/common/Darwin/DarwinMisc.cpp @@ -11,6 +11,7 @@ #include "common/Threading.h" #include "common/WindowInfo.h" #include "common/HostSys.h" +#include "fmt/format.h" #include #include @@ -19,12 +20,15 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include #include #include #include @@ -534,15 +538,196 @@ void HostSys::EndCodeWrite() #endif // _M_ARM64 +#define HANDLE_MACH_EXCEPTIONS + namespace PageFaultHandler { +#ifdef HANDLE_MACH_EXCEPTIONS + static void SignalHandler(mach_port_t port); +#else static void SignalHandler(int sig, siginfo_t* info, void* ctx); +#endif static std::recursive_mutex s_exception_handler_mutex; static bool s_in_exception_handler = false; static bool s_installed = false; } // namespace PageFaultHandler +#ifdef HANDLE_MACH_EXCEPTIONS + +#if defined(_M_X86) +#define THREAD_STATE64_COUNT x86_THREAD_STATE64_COUNT +#define THREAD_STATE64 x86_THREAD_STATE64 +#define thread_state64_t x86_thread_state64_t +#elif defined(_M_ARM64) +#define THREAD_STATE64_COUNT ARM_THREAD_STATE64_COUNT +#define THREAD_STATE64 ARM_THREAD_STATE64 +#define thread_state64_t arm_thread_state64_t +#else +#error Unknown Darwin Platform +#endif + +void PageFaultHandler::SignalHandler(mach_port_t port) +{ + Threading::SetNameOfCurrentThread("Mach Exception Thread"); + + #pragma pack(4) + struct + { + mach_msg_header_t Head; + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + int64_t code[2]; + int flavor; + mach_msg_type_number_t old_stateCnt; + natural_t old_state[THREAD_STATE64_COUNT]; + mach_msg_trailer_t trailer; + } msg_in; + + struct + { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + int flavor; + mach_msg_type_number_t new_stateCnt; + natural_t new_state[THREAD_STATE64_COUNT]; + } msg_out; + #pragma pack() + memset(&msg_in, 0xee, sizeof(msg_in)); + memset(&msg_out, 0xee, sizeof(msg_out)); + mach_msg_size_t send_size = 0; + mach_msg_option_t option = MACH_RCV_MSG; + while (true) + { + kern_return_t r; + if((r = mach_msg_overwrite(&msg_out.Head, option, send_size, sizeof(msg_in), port, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, &msg_in.Head, 0))) + { + pxFail(fmt::format("CRITICAL: mach_msg_overwrite: {:x}", r).c_str()); + } + + if (msg_in.Head.msgh_id == MACH_NOTIFY_NO_SENDERS) + { + // the other thread exited + mach_port_destroy(mach_task_self(), port); + return; + } + + if (msg_in.Head.msgh_id != 2406) + { + pxFailRel("unknown message received"); + return; + } + + if (msg_in.flavor != THREAD_STATE64) + { + pxFailRel(fmt::format("unknown flavour {}, expected {}", msg_in.flavor, THREAD_STATE64).c_str()); + return; + } + + s_exception_handler_mutex.lock(); + thread_state64_t* state = (thread_state64_t*)msg_in.old_state; + + HandlerResult result = HandlerResult::ExecuteNextHandler; + if (!s_in_exception_handler) + { + s_in_exception_handler = true; + result = HandlePageFault((void*)state->__rip, (void*)msg_in.code[1], (msg_in.code[0] & 2) != 0); + //result = HandlePageFault((void*)state->__pc, (void*)msg_in.code[1], (msg_in.code[0] & 2) != 0); ARM64 + s_in_exception_handler = false; + } + + // Set up the reply. + msg_out.Head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(msg_in.Head.msgh_bits), 0); + msg_out.Head.msgh_remote_port = msg_in.Head.msgh_remote_port; + msg_out.Head.msgh_local_port = MACH_PORT_NULL; + msg_out.Head.msgh_id = msg_in.Head.msgh_id + 100; + msg_out.NDR = msg_in.NDR; + + // Resumes execution right where we left off (re-executes instruction that caused the SIGSEGV). + if (result == HandlerResult::ContinueExecution) + { + msg_out.RetCode = KERN_SUCCESS; + msg_out.flavor = THREAD_STATE64; + msg_out.new_stateCnt = THREAD_STATE64_COUNT; + memcpy(msg_out.new_state, msg_in.old_state, THREAD_STATE64_COUNT * sizeof(natural_t)); + } + else // cooked + { + msg_out.RetCode = KERN_FAILURE; + msg_out.flavor = 0; + msg_out.new_stateCnt = 0; + } + + // We couldn't handle it. Pass it off to the crash dumper. + //CrashHandler::CrashSignalHandler(sig, info, ctx); + + msg_out.Head.msgh_size = + offsetof(__typeof__(msg_out), new_state) + msg_out.new_stateCnt * sizeof(natural_t); + + send_size = msg_out.Head.msgh_size; + option |= MACH_SEND_MSG; + + s_exception_handler_mutex.unlock(); + } +} + +bool PageFaultHandler::Install(Error* error) +{ + + exception_mask_t masks[EXC_TYPES_COUNT]; + mach_port_t ports[EXC_TYPES_COUNT]; + exception_behavior_t behaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; + mach_msg_type_number_t count = EXC_TYPES_COUNT; + + kern_return_t r = task_get_exception_ports(mach_task_self(), EXC_MASK_ALL, + masks, &count, ports, behaviors, flavors); + + for (mach_msg_type_number_t i = 0; i < count; i++) { + Console.Warning("##### Port %d: %d (behavior: %d, flavor: %d)\n", i, ports[i], behaviors[i], flavors[i]); + } + + mach_port_t port; + if((r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port))) + { + pxFailRel(fmt::format("mach_port_allocate: {:x}", r).c_str()); + return false; + } + + std::thread sig_thread(PageFaultHandler::SignalHandler, port); + sig_thread.detach(); + + if((r = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) + { + pxFailRel(fmt::format("mach_port_insert_right: {:x}", r).c_str()); + return false; + } + + task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, THREAD_STATE_NONE); + + if((r = thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port, EXCEPTION_STATE | MACH_EXCEPTION_CODES, THREAD_STATE64))) + { + pxFailRel(fmt::format("thread_set_exception_ports: {:x}", r).c_str()); + return false; + } + + if((r = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1))) + { + pxFailRel(fmt::format("mach_port_mod_refs: {:x}", r).c_str()); + return false; + } + + mach_port_t previous; + mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_NO_SENDERS, 0, port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); + + s_installed = true; + return true; +} + +#else void PageFaultHandler::SignalHandler(int sig, siginfo_t* info, void* ctx) { #if defined(_M_X86) @@ -610,3 +795,4 @@ bool PageFaultHandler::Install(Error* error) s_installed = true; return true; } +#endif