backend: Unify exception handlers across architectures

This commit is contained in:
Merry 2023-01-06 14:24:09 +00:00
parent 46e5f4ee97
commit c72ee5473b
5 changed files with 139 additions and 59 deletions

1
.gitignore vendored
View file

@ -5,6 +5,7 @@ cmake-build-*/
.idea/ .idea/
docs/Doxygen/ docs/Doxygen/
# Generated files # Generated files
src/dynarmic/backend/arm64/mig/
src/dynarmic/backend/x64/mig/ src/dynarmic/backend/x64/mig/
# System files # System files
.DS_Store .DS_Store

View file

@ -330,41 +330,6 @@ if (ARCHITECTURE STREQUAL "x86_64")
) )
endif() 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") elseif(ARCHITECTURE STREQUAL "arm64")
target_link_libraries(dynarmic PRIVATE merry::oaknut) target_link_libraries(dynarmic PRIVATE merry::oaknut)
@ -423,17 +388,54 @@ elseif(ARCHITECTURE STREQUAL "arm64")
backend/arm64/a64_interface.cpp backend/arm64/a64_interface.cpp
) )
endif() endif()
endif()
if (UNIX) if (WIN32)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_sources(dynarmic PRIVATE backend/exception_handler_windows.cpp)
target_link_libraries(dynarmic PRIVATE rt) elseif (APPLE)
endif() find_path(MACH_EXC_DEFS_DIR "mach/mach_exc.defs")
target_sources(dynarmic PRIVATE backend/exception_handler_posix.cpp) if (NOT MACH_EXC_DEFS_DIR)
else() message(WARNING "macOS fastmem disabled: unable to find mach/mach_exc.defs")
target_sources(dynarmic PRIVATE backend/exception_handler_generic.cpp) 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() 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() else()
message(FATAL_ERROR "Unsupported architecture") target_sources(dynarmic PRIVATE backend/exception_handler_generic.cpp)
endif() endif()
include(CreateDirectoryGroups) include(CreateDirectoryGroups)

View file

@ -16,18 +16,37 @@
#include <fmt/format.h> #include <fmt/format.h>
#include <mcl/assert.hpp> #include <mcl/assert.hpp>
#include <mcl/bit_cast.hpp> #include <mcl/bit_cast.hpp>
#include <mcl/macro/architecture.hpp>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
#include "dynarmic/backend/exception_handler.h" #include "dynarmic/backend/exception_handler.h"
#include "dynarmic/backend/x64/block_of_code.h"
#define mig_external extern "C" #if defined(MCL_ARCHITECTURE_X86_64)
#include "dynarmic/backend/x64/mig/mach_exc_server.h"
# 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 <oaknut/code_block.hpp>
# 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 { namespace Dynarmic::Backend {
using namespace Dynarmic::Backend::X64;
namespace { namespace {
struct CodeBlockInfo { struct CodeBlockInfo {
@ -45,7 +64,7 @@ public:
MachHandler(); MachHandler();
~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 AddCodeBlock(CodeBlockInfo info);
void RemoveCodeBlock(u64 rip); 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_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(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. // 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; 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) { kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) {
std::lock_guard<std::mutex> guard(code_block_infos_mutex); std::lock_guard<std::mutex> guard(code_block_infos_mutex);
@ -127,6 +147,24 @@ kern_return_t MachHandler::HandleRequest(x86_thread_state64_t* ts) {
return KERN_SUCCESS; return KERN_SUCCESS;
} }
#elif defined(MCL_ARCHITECTURE_ARM64)
kern_return_t MachHandler::HandleRequest(arm_thread_state64_t* ts) {
std::lock_guard<std::mutex> 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) { void MachHandler::AddCodeBlock(CodeBlockInfo cbi) {
std::lock_guard<std::mutex> guard(code_block_infos_mutex); std::lock_guard<std::mutex> 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"); fmt::print(stderr, "dynarmic: catch_mach_exception_raise_state: Invalid arguments.\n");
return KERN_INVALID_ARGUMENT; 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"); fmt::print(stderr, "dynarmic: catch_mach_exception_raise_state: Unexpected flavor.\n");
return KERN_INVALID_ARGUMENT; return KERN_INVALID_ARGUMENT;
} }
@ -182,17 +220,17 @@ mig_external kern_return_t catch_mach_exception_raise_state(
return KERN_FAILURE; return KERN_FAILURE;
} }
x86_thread_state64_t* ts = reinterpret_cast<x86_thread_state64_t*>(new_state); dynarmic_thread_state_t* ts = reinterpret_cast<dynarmic_thread_state_t*>(new_state);
std::memcpy(ts, reinterpret_cast<const x86_thread_state64_t*>(old_state), sizeof(x86_thread_state64_t)); std::memcpy(ts, reinterpret_cast<const dynarmic_thread_state_t*>(old_state), sizeof(dynarmic_thread_state_t));
*new_stateCnt = x86_THREAD_STATE64_COUNT; *new_stateCnt = THREAD_STATE_COUNT;
return mach_handler.HandleRequest(ts); return mach_handler.HandleRequest(ts);
} }
struct ExceptionHandler::Impl final { struct ExceptionHandler::Impl final {
Impl(BlockOfCode& code) Impl(u64 code_begin_, u64 code_end_)
: code_begin(mcl::bit_cast<u64>(code.getCode())) : code_begin(code_begin_)
, code_end(code_begin + code.GetTotalCodeSize()) {} , code_end(code_end_) {}
void SetCallback(std::function<FakeCall(u64)> cb) { void SetCallback(std::function<FakeCall(u64)> cb) {
CodeBlockInfo cbi; CodeBlockInfo cbi;
@ -211,12 +249,23 @@ private:
}; };
ExceptionHandler::ExceptionHandler() = default; ExceptionHandler::ExceptionHandler() = default;
ExceptionHandler::~ExceptionHandler() = default; ExceptionHandler::~ExceptionHandler() = default;
void ExceptionHandler::Register(BlockOfCode& code) { #if defined(MCL_ARCHITECTURE_X86_64)
impl = std::make_unique<Impl>(code); void ExceptionHandler::Register(X64::BlockOfCode& code) {
const u64 code_begin = mcl::bit_cast<u64>(code.getCode());
const u64 code_end = code_begin + code.GetTotalCodeSize();
impl = std::make_unique<Impl>(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<u64>(mem.ptr());
const u64 code_end = code_begin + size;
impl = std::make_unique<Impl>(code_begin, code_end);
}
#else
# error "Invalid architecture"
#endif
bool ExceptionHandler::SupportsFastmem() const noexcept { bool ExceptionHandler::SupportsFastmem() const noexcept {
return static_cast<bool>(impl); return static_cast<bool>(impl);

View file

@ -0,0 +1,14 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2023 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <mcl/macro/architecture.hpp>
#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

View file

@ -0,0 +1,14 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2023 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <mcl/macro/architecture.hpp>
#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