From c72ee5473b5b11ac110e86cc9544cab5159ccce8 Mon Sep 17 00:00:00 2001 From: Merry Date: Fri, 6 Jan 2023 14:24:09 +0000 Subject: [PATCH] backend: Unify exception handlers across architectures --- .gitignore | 1 + src/dynarmic/CMakeLists.txt | 86 ++++++++++--------- .../{x64 => }/exception_handler_macos.cpp | 83 ++++++++++++++---- .../backend/exception_handler_macos_mig.c | 14 +++ .../backend/exception_handler_windows.cpp | 14 +++ 5 files changed, 139 insertions(+), 59 deletions(-) rename src/dynarmic/backend/{x64 => }/exception_handler_macos.cpp (72%) create mode 100644 src/dynarmic/backend/exception_handler_macos_mig.c create mode 100644 src/dynarmic/backend/exception_handler_windows.cpp diff --git a/.gitignore b/.gitignore index 3621caac..d735e662 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ cmake-build-*/ .idea/ docs/Doxygen/ # Generated files +src/dynarmic/backend/arm64/mig/ src/dynarmic/backend/x64/mig/ # System files .DS_Store diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 85a118cf..6312af19 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -330,41 +330,6 @@ if (ARCHITECTURE STREQUAL "x86_64") ) endif() - if (WIN32) - target_sources(dynarmic PRIVATE backend/x64/exception_handler_windows.cpp) - elseif (APPLE) - find_path(MACH_EXC_DEFS_DIR "mach/mach_exc.defs") - if (NOT MACH_EXC_DEFS_DIR) - message(WARNING "macOS fastmem disabled: unable to find mach/mach_exc.defs") - target_sources(dynarmic PRIVATE backend/exception_handler_generic.cpp) - else() - message(STATUS "mach/mach_exc.defs location: ${MACH_EXC_DEFS_DIR}") - execute_process( - COMMAND - mkdir -p "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig" - COMMAND - mig - -arch x86_64 - -user "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig/mach_exc_user.c" - -header "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig/mach_exc_user.h" - -server "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig/mach_exc_server.c" - -sheader "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig/mach_exc_server.h" - "${MACH_EXC_DEFS_DIR}/mach/mach_exc.defs" - ) - target_sources(dynarmic PRIVATE - backend/x64/exception_handler_macos.cpp - backend/x64/mig/mach_exc_server.c - backend/x64/mig/mach_exc_server.h - ) - endif() - elseif (UNIX) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - target_link_libraries(dynarmic PRIVATE rt) - endif() - target_sources(dynarmic PRIVATE backend/exception_handler_posix.cpp) - else() - target_sources(dynarmic PRIVATE backend/exception_handler_generic.cpp) - endif() elseif(ARCHITECTURE STREQUAL "arm64") target_link_libraries(dynarmic PRIVATE merry::oaknut) @@ -423,17 +388,54 @@ elseif(ARCHITECTURE STREQUAL "arm64") backend/arm64/a64_interface.cpp ) endif() +endif() - if (UNIX) - if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - target_link_libraries(dynarmic PRIVATE rt) - endif() - target_sources(dynarmic PRIVATE backend/exception_handler_posix.cpp) - else() +if (WIN32) + target_sources(dynarmic PRIVATE backend/exception_handler_windows.cpp) +elseif (APPLE) + find_path(MACH_EXC_DEFS_DIR "mach/mach_exc.defs") + if (NOT MACH_EXC_DEFS_DIR) + message(WARNING "macOS fastmem disabled: unable to find mach/mach_exc.defs") target_sources(dynarmic PRIVATE backend/exception_handler_generic.cpp) + else() + message(STATUS "mach/mach_exc.defs location: ${MACH_EXC_DEFS_DIR}") + execute_process( + COMMAND + mkdir -p "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig" + COMMAND + mig + -arch x86_64 + -user "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig/mach_exc_user.c" + -header "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig/mach_exc_user.h" + -server "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig/mach_exc_server.c" + -sheader "${CMAKE_CURRENT_SOURCE_DIR}/backend/x64/mig/mach_exc_server.h" + "${MACH_EXC_DEFS_DIR}/mach/mach_exc.defs" + ) + message(STATUS "mach/mach_exc.defs location: ${MACH_EXC_DEFS_DIR}") + execute_process( + COMMAND + mkdir -p "${CMAKE_CURRENT_SOURCE_DIR}/backend/arm64/mig" + COMMAND + mig + -arch arm64 + -user "${CMAKE_CURRENT_SOURCE_DIR}/backend/arm64/mig/mach_exc_user.c" + -header "${CMAKE_CURRENT_SOURCE_DIR}/backend/arm64/mig/mach_exc_user.h" + -server "${CMAKE_CURRENT_SOURCE_DIR}/backend/arm64/mig/mach_exc_server.c" + -sheader "${CMAKE_CURRENT_SOURCE_DIR}/backend/arm64/mig/mach_exc_server.h" + "${MACH_EXC_DEFS_DIR}/mach/mach_exc.defs" + ) + target_sources(dynarmic PRIVATE + backend/exception_handler_macos.cpp + backend/exception_handler_macos_mig.c + ) endif() +elseif (UNIX) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(dynarmic PRIVATE rt) + endif() + target_sources(dynarmic PRIVATE backend/exception_handler_posix.cpp) else() - message(FATAL_ERROR "Unsupported architecture") + target_sources(dynarmic PRIVATE backend/exception_handler_generic.cpp) endif() include(CreateDirectoryGroups) diff --git a/src/dynarmic/backend/x64/exception_handler_macos.cpp b/src/dynarmic/backend/exception_handler_macos.cpp similarity index 72% rename from src/dynarmic/backend/x64/exception_handler_macos.cpp rename to src/dynarmic/backend/exception_handler_macos.cpp index 763ced7d..201c7584 100644 --- a/src/dynarmic/backend/x64/exception_handler_macos.cpp +++ b/src/dynarmic/backend/exception_handler_macos.cpp @@ -16,18 +16,37 @@ #include #include #include +#include #include #include "dynarmic/backend/exception_handler.h" -#include "dynarmic/backend/x64/block_of_code.h" -#define mig_external extern "C" -#include "dynarmic/backend/x64/mig/mach_exc_server.h" +#if defined(MCL_ARCHITECTURE_X86_64) + +# include "dynarmic/backend/x64/block_of_code.h" +# define mig_external extern "C" +# include "dynarmic/backend/x64/mig/mach_exc_server.h" + +# define THREAD_STATE x86_THREAD_STATE64 +# define THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT + +using dynarmic_thread_state_t = x86_thread_state64_t; + +#elif defined(MCL_ARCHITECTURE_ARM64) + +# include +# define mig_external extern "C" +# include "dynarmic/backend/arm64/mig/mach_exc_server.h" + +# define THREAD_STATE ARM_THREAD_STATE64 +# define THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT + +using dynarmic_thread_state_t = arm_thread_state64_t; + +#endif namespace Dynarmic::Backend { -using namespace Dynarmic::Backend::X64; - namespace { struct CodeBlockInfo { @@ -45,7 +64,7 @@ public: MachHandler(); ~MachHandler(); - kern_return_t HandleRequest(x86_thread_state64_t* thread_state); + kern_return_t HandleRequest(dynarmic_thread_state_t* thread_state); void AddCodeBlock(CodeBlockInfo info); void RemoveCodeBlock(u64 rip); @@ -69,7 +88,7 @@ MachHandler::MachHandler() { KCHECK(mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &server_port)); KCHECK(mach_port_insert_right(mach_task_self(), server_port, server_port, MACH_MSG_TYPE_MAKE_SEND)); - KCHECK(task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, server_port, EXCEPTION_STATE | MACH_EXCEPTION_CODES, x86_THREAD_STATE64)); + KCHECK(task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, server_port, EXCEPTION_STATE | MACH_EXCEPTION_CODES, THREAD_STATE)); // The below doesn't actually work, and I'm not sure why; since this doesn't work we'll have a spurious error message upon shutdown. mach_port_t prev; @@ -110,6 +129,7 @@ void MachHandler::MessagePump() { } } +#if defined(MCL_ARCHITECTURE_X86_64) kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) { std::lock_guard guard(code_block_infos_mutex); @@ -127,6 +147,24 @@ kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) { return KERN_SUCCESS; } +#elif defined(MCL_ARCHITECTURE_ARM64) +kern_return_t MachHandler::HandleRequest(arm_thread_state64_t* ts) { + std::lock_guard guard(code_block_infos_mutex); + + const auto iter = FindCodeBlockInfo(ts->__pc); + if (iter == code_block_infos.end()) { + fmt::print(stderr, "Unhandled EXC_BAD_ACCESS at pc {:#016x}\n", ts->__pc); + return KERN_FAILURE; + } + + FakeCall fc = iter->cb(ts->__pc); + + // TODO: Sign with ptrauth_sign_unauthenticated if pointer authentication is enabled. + ts->__pc = fc.call_pc; + + return KERN_SUCCESS; +} +#endif void MachHandler::AddCodeBlock(CodeBlockInfo cbi) { std::lock_guard guard(code_block_infos_mutex); @@ -173,7 +211,7 @@ mig_external kern_return_t catch_mach_exception_raise_state( fmt::print(stderr, "dynarmic: catch_mach_exception_raise_state: Invalid arguments.\n"); return KERN_INVALID_ARGUMENT; } - if (*flavor != x86_THREAD_STATE64 || old_stateCnt != x86_THREAD_STATE64_COUNT || *new_stateCnt < x86_THREAD_STATE64_COUNT) { + if (*flavor != THREAD_STATE || old_stateCnt != THREAD_STATE_COUNT || *new_stateCnt < THREAD_STATE_COUNT) { fmt::print(stderr, "dynarmic: catch_mach_exception_raise_state: Unexpected flavor.\n"); return KERN_INVALID_ARGUMENT; } @@ -182,17 +220,17 @@ mig_external kern_return_t catch_mach_exception_raise_state( return KERN_FAILURE; } - x86_thread_state64_t* ts = reinterpret_cast(new_state); - std::memcpy(ts, reinterpret_cast(old_state), sizeof(x86_thread_state64_t)); - *new_stateCnt = x86_THREAD_STATE64_COUNT; + dynarmic_thread_state_t* ts = reinterpret_cast(new_state); + std::memcpy(ts, reinterpret_cast(old_state), sizeof(dynarmic_thread_state_t)); + *new_stateCnt = THREAD_STATE_COUNT; return mach_handler.HandleRequest(ts); } struct ExceptionHandler::Impl final { - Impl(BlockOfCode& code) - : code_begin(mcl::bit_cast(code.getCode())) - , code_end(code_begin + code.GetTotalCodeSize()) {} + Impl(u64 code_begin_, u64 code_end_) + : code_begin(code_begin_) + , code_end(code_end_) {} void SetCallback(std::function cb) { CodeBlockInfo cbi; @@ -211,12 +249,23 @@ private: }; ExceptionHandler::ExceptionHandler() = default; - ExceptionHandler::~ExceptionHandler() = default; -void ExceptionHandler::Register(BlockOfCode& code) { - impl = std::make_unique(code); +#if defined(MCL_ARCHITECTURE_X86_64) +void ExceptionHandler::Register(X64::BlockOfCode& code) { + const u64 code_begin = mcl::bit_cast(code.getCode()); + const u64 code_end = code_begin + code.GetTotalCodeSize(); + impl = std::make_unique(code_begin, code_end); } +#elif defined(MCL_ARCHITECTURE_ARM64) +void ExceptionHandler::Register(oaknut::CodeBlock& mem, std::size_t size) { + const u64 code_begin = mcl::bit_cast(mem.ptr()); + const u64 code_end = code_begin + size; + impl = std::make_unique(code_begin, code_end); +} +#else +# error "Invalid architecture" +#endif bool ExceptionHandler::SupportsFastmem() const noexcept { return static_cast(impl); diff --git a/src/dynarmic/backend/exception_handler_macos_mig.c b/src/dynarmic/backend/exception_handler_macos_mig.c new file mode 100644 index 00000000..762a80ca --- /dev/null +++ b/src/dynarmic/backend/exception_handler_macos_mig.c @@ -0,0 +1,14 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2023 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include + +#if defined(MCL_ARCHITECTURE_X86_64) +# include "dynarmic/backend/x64/mig/mach_exc_server.c" +#elif defined(MCL_ARCHITECTURE_ARM64) +# include "dynarmic/backend/arm64/mig/mach_exc_server.c" +#else +# error "Invalid architecture" +#endif diff --git a/src/dynarmic/backend/exception_handler_windows.cpp b/src/dynarmic/backend/exception_handler_windows.cpp new file mode 100644 index 00000000..719c0075 --- /dev/null +++ b/src/dynarmic/backend/exception_handler_windows.cpp @@ -0,0 +1,14 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2023 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include + +#if defined(MCL_ARCHITECTURE_X86_64) +# include "dynarmic/backend/x64/exception_handler_windows.cpp" +#elif defined(MCL_ARCHITECTURE_ARM64) +# include "dynarmic/backend/exception_handler_generic.cpp" +#else +# error "Invalid architecture" +#endif