exception_handler_posix: Support arm64 backend
This commit is contained in:
parent
59fc92ee6e
commit
d635b12b84
9 changed files with 399 additions and 262 deletions
|
@ -1,4 +1,5 @@
|
|||
add_library(dynarmic
|
||||
backend/exception_handler.h
|
||||
common/cast_util.h
|
||||
common/crypto/aes.cpp
|
||||
common/crypto/aes.h
|
||||
|
@ -289,7 +290,6 @@ if (ARCHITECTURE STREQUAL "x86_64")
|
|||
backend/x64/emit_x64_vector.cpp
|
||||
backend/x64/emit_x64_vector_floating_point.cpp
|
||||
backend/x64/emit_x64_vector_saturation.cpp
|
||||
backend/x64/exception_handler.h
|
||||
backend/x64/exclusive_monitor.cpp
|
||||
backend/x64/exclusive_monitor_friend.h
|
||||
backend/x64/host_feature.h
|
||||
|
@ -336,7 +336,7 @@ if (ARCHITECTURE STREQUAL "x86_64")
|
|||
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/x64/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(
|
||||
|
@ -361,9 +361,9 @@ if (ARCHITECTURE STREQUAL "x86_64")
|
|||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_link_libraries(dynarmic PRIVATE rt)
|
||||
endif()
|
||||
target_sources(dynarmic PRIVATE backend/x64/exception_handler_posix.cpp)
|
||||
target_sources(dynarmic PRIVATE backend/exception_handler_posix.cpp)
|
||||
else()
|
||||
target_sources(dynarmic PRIVATE backend/x64/exception_handler_generic.cpp)
|
||||
target_sources(dynarmic PRIVATE backend/exception_handler_generic.cpp)
|
||||
endif()
|
||||
elseif(ARCHITECTURE STREQUAL "arm64")
|
||||
target_link_libraries(dynarmic PRIVATE merry::oaknut)
|
||||
|
@ -421,6 +421,15 @@ elseif(ARCHITECTURE STREQUAL "arm64")
|
|||
backend/arm64/a64_interface.cpp
|
||||
)
|
||||
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()
|
||||
target_sources(dynarmic PRIVATE backend/exception_handler_generic.cpp)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture")
|
||||
endif()
|
||||
|
|
67
src/dynarmic/backend/exception_handler.h
Normal file
67
src/dynarmic/backend/exception_handler.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <mcl/macro/architecture.hpp>
|
||||
#include <mcl/stdint.hpp>
|
||||
|
||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
class BlockOfCode;
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
||||
namespace oaknut {
|
||||
class CodeBlock;
|
||||
} // namespace oaknut
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
namespace Dynarmic::Backend {
|
||||
|
||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
||||
struct FakeCall {
|
||||
u64 call_rip;
|
||||
u64 ret_rip;
|
||||
};
|
||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
||||
struct FakeCall {
|
||||
u64 call_pc;
|
||||
u64 ret_pc;
|
||||
std::optional<int> load_xscratch0;
|
||||
std::optional<int> load_xscratch1;
|
||||
std::optional<int> load_q0;
|
||||
};
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
class ExceptionHandler final {
|
||||
public:
|
||||
ExceptionHandler();
|
||||
~ExceptionHandler();
|
||||
|
||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
||||
void Register(X64::BlockOfCode& code);
|
||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
||||
void Register(oaknut::CodeBlock& mem, std::size_t mem_size);
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
bool SupportsFastmem() const noexcept;
|
||||
void SetFastmemCallback(std::function<FakeCall(u64)> cb);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend
|
|
@ -3,9 +3,9 @@
|
|||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include "dynarmic/backend/x64/exception_handler.h"
|
||||
#include "dynarmic/backend/exception_handler.h"
|
||||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
namespace Dynarmic::Backend {
|
||||
|
||||
struct ExceptionHandler::Impl final {
|
||||
};
|
||||
|
@ -13,9 +13,17 @@ struct ExceptionHandler::Impl final {
|
|||
ExceptionHandler::ExceptionHandler() = default;
|
||||
ExceptionHandler::~ExceptionHandler() = default;
|
||||
|
||||
void ExceptionHandler::Register(BlockOfCode&) {
|
||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
||||
void ExceptionHandler::Register(X64::BlockOfCode&) {
|
||||
// Do nothing
|
||||
}
|
||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
||||
void ExceptionHandler::Register(oaknut::CodeBlock&, std::size_t) {
|
||||
// Do nothing
|
||||
}
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
bool ExceptionHandler::SupportsFastmem() const noexcept {
|
||||
return false;
|
||||
|
@ -25,4 +33,4 @@ void ExceptionHandler::SetFastmemCallback(std::function<FakeCall(u64)>) {
|
|||
// Do nothing
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
} // namespace Dynarmic::Backend
|
294
src/dynarmic/backend/exception_handler_posix.cpp
Normal file
294
src/dynarmic/backend/exception_handler_posix.cpp
Normal file
|
@ -0,0 +1,294 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2019 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include "dynarmic/backend/exception_handler.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <signal.h>
|
||||
# include <sys/ucontext.h>
|
||||
#else
|
||||
# include <signal.h>
|
||||
# include <ucontext.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include <mcl/assert.hpp>
|
||||
#include <mcl/bit_cast.hpp>
|
||||
#include <mcl/stdint.hpp>
|
||||
|
||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
||||
# include "dynarmic/backend/x64/block_of_code.h"
|
||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
||||
# include <oaknut/code_block.hpp>
|
||||
|
||||
# include "dynarmic/backend/arm64/abi.h"
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
namespace Dynarmic::Backend {
|
||||
|
||||
namespace {
|
||||
|
||||
struct CodeBlockInfo {
|
||||
u64 code_begin, code_end;
|
||||
std::function<FakeCall(u64)> cb;
|
||||
};
|
||||
|
||||
class SigHandler {
|
||||
public:
|
||||
SigHandler();
|
||||
~SigHandler();
|
||||
|
||||
void AddCodeBlock(CodeBlockInfo info);
|
||||
void RemoveCodeBlock(u64 host_pc);
|
||||
|
||||
bool SupportsFastmem() const { return supports_fast_mem; }
|
||||
|
||||
private:
|
||||
auto FindCodeBlockInfo(u64 host_pc) {
|
||||
return std::find_if(code_block_infos.begin(), code_block_infos.end(), [&](const auto& x) { return x.code_begin <= host_pc && x.code_end > host_pc; });
|
||||
}
|
||||
|
||||
bool supports_fast_mem = true;
|
||||
|
||||
void* signal_stack_memory = nullptr;
|
||||
|
||||
std::vector<CodeBlockInfo> code_block_infos;
|
||||
std::mutex code_block_infos_mutex;
|
||||
|
||||
struct sigaction old_sa_segv;
|
||||
struct sigaction old_sa_bus;
|
||||
|
||||
static void SigAction(int sig, siginfo_t* info, void* raw_context);
|
||||
};
|
||||
|
||||
SigHandler sig_handler;
|
||||
|
||||
SigHandler::SigHandler() {
|
||||
const size_t signal_stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||
|
||||
signal_stack_memory = std::malloc(signal_stack_size);
|
||||
|
||||
stack_t signal_stack;
|
||||
signal_stack.ss_sp = signal_stack_memory;
|
||||
signal_stack.ss_size = signal_stack_size;
|
||||
signal_stack.ss_flags = 0;
|
||||
if (sigaltstack(&signal_stack, nullptr) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = nullptr;
|
||||
sa.sa_sigaction = &SigHandler::SigAction;
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIGSEGV, &sa, &old_sa_segv) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGSEGV handler\n");
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n");
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SigHandler::~SigHandler() {
|
||||
std::free(signal_stack_memory);
|
||||
}
|
||||
|
||||
void SigHandler::AddCodeBlock(CodeBlockInfo cbi) {
|
||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||
if (auto iter = FindCodeBlockInfo(cbi.code_begin); iter != code_block_infos.end()) {
|
||||
code_block_infos.erase(iter);
|
||||
}
|
||||
code_block_infos.push_back(cbi);
|
||||
}
|
||||
|
||||
void SigHandler::RemoveCodeBlock(u64 host_pc) {
|
||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||
const auto iter = FindCodeBlockInfo(host_pc);
|
||||
if (iter == code_block_infos.end()) {
|
||||
return;
|
||||
}
|
||||
code_block_infos.erase(iter);
|
||||
}
|
||||
|
||||
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
||||
|
||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
||||
|
||||
# if defined(__APPLE__)
|
||||
# define CTX_RIP (((ucontext_t*)raw_context)->uc_mcontext->__ss.__rip)
|
||||
# define CTX_RSP (((ucontext_t*)raw_context)->uc_mcontext->__ss.__rsp)
|
||||
# elif defined(__linux__)
|
||||
# define CTX_RIP (((ucontext_t*)raw_context)->uc_mcontext.gregs[REG_RIP])
|
||||
# define CTX_RSP (((ucontext_t*)raw_context)->uc_mcontext.gregs[REG_RSP])
|
||||
# elif defined(__FreeBSD__)
|
||||
# define CTX_RIP (((ucontext_t*)raw_context)->uc_mcontext.mc_rip)
|
||||
# define CTX_RSP (((ucontext_t*)raw_context)->uc_mcontext.mc_rsp)
|
||||
# else
|
||||
# error "Unknown platform"
|
||||
# endif
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(sig_handler.code_block_infos_mutex);
|
||||
|
||||
const auto iter = sig_handler.FindCodeBlockInfo(CTX_RIP);
|
||||
if (iter != sig_handler.code_block_infos.end()) {
|
||||
FakeCall fc = iter->cb(CTX_RIP);
|
||||
|
||||
CTX_RSP -= sizeof(u64);
|
||||
*mcl::bit_cast<u64*>(CTX_RSP) = fc.ret_rip;
|
||||
CTX_RIP = fc.call_rip;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_RIP);
|
||||
|
||||
#elif defined(MCL_ARCHITECTURE_ARM64)
|
||||
|
||||
# if defined(__APPLE__)
|
||||
# define CTX_PC (((ucontext_t*)raw_context)->uc_mcontext->__ss.__pc)
|
||||
# define CTX_SP (((ucontext_t*)raw_context)->uc_mcontext->__ss.__sp)
|
||||
# define CTX_LR (((ucontext_t*)raw_context)->uc_mcontext->__ss.__lr)
|
||||
# define CTX_X(i) (((ucontext_t*)raw_context)->uc_mcontext->__ss.__x[i])
|
||||
# define CTX_Q(i) (((ucontext_t*)raw_context)->uc_mcontext->__ns.__v[i])
|
||||
# elif defined(__linux__)
|
||||
# define CTX_PC (((ucontext_t*)raw_context)->uc_mcontext.pc)
|
||||
# define CTX_SP (((ucontext_t*)raw_context)->uc_mcontext.sp)
|
||||
# define CTX_LR (((ucontext_t*)raw_context)->uc_mcontext.regs[30])
|
||||
# define CTX_X(i) (((ucontext_t*)raw_context)->uc_mcontext.regs[i])
|
||||
# define CTX_Q(i) (fp->vregs[i])
|
||||
const auto fp = [raw_context] {
|
||||
_aarch64_ctx* head = (_aarch64_ctx*)(((ucontext_t*)raw_context)->uc_mcontext.__reserved);
|
||||
while (head->magic != FPSIMD_MAGIC) {
|
||||
ASSERT(head->magic && head->size);
|
||||
head = (_aarch64_ctx*)(((char*)head) + head->size);
|
||||
}
|
||||
return (fpsimd_context*)head;
|
||||
}();
|
||||
# elif defined(__FreeBSD__)
|
||||
# define CTX_PC (((ucontext_t*)raw_context)->uc_mcontext.mc_gpregs.gp_elr)
|
||||
# define CTX_SP (((ucontext_t*)raw_context)->uc_mcontext.mc_gpregs.gp_sp)
|
||||
# define CTX_LR (((ucontext_t*)raw_context)->uc_mcontext.mc_gpregs.gp_lr)
|
||||
# define CTX_X(i) (((ucontext_t*)raw_context)->uc_mcontext.mc_gpregs.gp_x[i])
|
||||
# define CTX_Q(i) (((ucontext_t*)raw_context)->uc_mcontext.mc_fpregs.fp_q[i])
|
||||
# else
|
||||
# error "Unknown platform"
|
||||
# endif
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(sig_handler.code_block_infos_mutex);
|
||||
|
||||
const auto iter = sig_handler.FindCodeBlockInfo(CTX_PC);
|
||||
if (iter != sig_handler.code_block_infos.end()) {
|
||||
FakeCall fc = iter->cb(CTX_PC);
|
||||
|
||||
CTX_LR = fc.ret_pc;
|
||||
CTX_PC = fc.call_pc;
|
||||
|
||||
if (fc.load_xscratch0) {
|
||||
CTX_X(Arm64::Xscratch0.index()) = CTX_X(*fc.load_xscratch0);
|
||||
}
|
||||
if (fc.load_xscratch1) {
|
||||
CTX_X(Arm64::Xscratch1.index()) = CTX_X(*fc.load_xscratch1);
|
||||
}
|
||||
if (fc.load_q0) {
|
||||
CTX_Q(0) = CTX_Q(*fc.load_q0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
|
||||
#else
|
||||
|
||||
# error "Invalid architecture"
|
||||
|
||||
#endif
|
||||
|
||||
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler.old_sa_segv : &sig_handler.old_sa_bus;
|
||||
if (retry_sa->sa_flags & SA_SIGINFO) {
|
||||
retry_sa->sa_sigaction(sig, info, raw_context);
|
||||
return;
|
||||
}
|
||||
if (retry_sa->sa_handler == SIG_DFL) {
|
||||
signal(sig, SIG_DFL);
|
||||
return;
|
||||
}
|
||||
if (retry_sa->sa_handler == SIG_IGN) {
|
||||
return;
|
||||
}
|
||||
retry_sa->sa_handler(sig);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
struct ExceptionHandler::Impl final {
|
||||
Impl(u64 code_begin_, u64 code_end_)
|
||||
: code_begin(code_begin_)
|
||||
, code_end(code_end_) {}
|
||||
|
||||
void SetCallback(std::function<FakeCall(u64)> cb) {
|
||||
CodeBlockInfo cbi;
|
||||
cbi.code_begin = code_begin;
|
||||
cbi.code_end = code_end;
|
||||
cbi.cb = cb;
|
||||
sig_handler.AddCodeBlock(cbi);
|
||||
}
|
||||
|
||||
~Impl() {
|
||||
sig_handler.RemoveCodeBlock(code_begin);
|
||||
}
|
||||
|
||||
private:
|
||||
u64 code_begin, code_end;
|
||||
};
|
||||
|
||||
ExceptionHandler::ExceptionHandler() = default;
|
||||
ExceptionHandler::~ExceptionHandler() = default;
|
||||
|
||||
#if defined(MCL_ARCHITECTURE_X86_64)
|
||||
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 {
|
||||
return static_cast<bool>(impl) && sig_handler.SupportsFastmem();
|
||||
}
|
||||
|
||||
void ExceptionHandler::SetFastmemCallback(std::function<FakeCall(u64)> cb) {
|
||||
impl->SetCallback(cb);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend
|
|
@ -19,7 +19,7 @@
|
|||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
#include "dynarmic/backend/x64/exception_handler.h"
|
||||
#include "dynarmic/backend/exception_handler.h"
|
||||
#include "dynarmic/backend/x64/reg_alloc.h"
|
||||
#include "dynarmic/common/fp/fpcr.h"
|
||||
#include "dynarmic/ir/location_descriptor.h"
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <mcl/stdint.hpp>
|
||||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
class BlockOfCode;
|
||||
|
||||
struct FakeCall {
|
||||
u64 call_rip;
|
||||
u64 ret_rip;
|
||||
};
|
||||
|
||||
class ExceptionHandler final {
|
||||
public:
|
||||
ExceptionHandler();
|
||||
~ExceptionHandler();
|
||||
|
||||
void Register(BlockOfCode& code);
|
||||
|
||||
bool SupportsFastmem() const noexcept;
|
||||
void SetFastmemCallback(std::function<FakeCall(u64)> cb);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
|
@ -18,13 +18,15 @@
|
|||
#include <mcl/bit_cast.hpp>
|
||||
#include <mcl/stdint.hpp>
|
||||
|
||||
#include "dynarmic/backend/exception_handler.h"
|
||||
#include "dynarmic/backend/x64/block_of_code.h"
|
||||
#include "dynarmic/backend/x64/exception_handler.h"
|
||||
|
||||
#define mig_external extern "C"
|
||||
#include "dynarmic/backend/x64/mig/mach_exc_server.h"
|
||||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
namespace Dynarmic::Backend {
|
||||
|
||||
using namespace Dynarmic::Backend::X64;
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -224,4 +226,4 @@ void ExceptionHandler::SetFastmemCallback(std::function<FakeCall(u64)> cb) {
|
|||
impl->SetCallback(cb);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
} // namespace Dynarmic::Backend
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2019 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include "dynarmic/backend/x64/exception_handler.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <signal.h>
|
||||
# include <sys/ucontext.h>
|
||||
#else
|
||||
# include <signal.h>
|
||||
# include <ucontext.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include <mcl/assert.hpp>
|
||||
#include <mcl/bit_cast.hpp>
|
||||
#include <mcl/stdint.hpp>
|
||||
|
||||
#include "dynarmic/backend/x64/block_of_code.h"
|
||||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
namespace {
|
||||
|
||||
struct CodeBlockInfo {
|
||||
u64 code_begin, code_end;
|
||||
std::function<FakeCall(u64)> cb;
|
||||
};
|
||||
|
||||
class SigHandler {
|
||||
public:
|
||||
SigHandler();
|
||||
~SigHandler();
|
||||
|
||||
void AddCodeBlock(CodeBlockInfo info);
|
||||
void RemoveCodeBlock(u64 rip);
|
||||
|
||||
bool SupportsFastmem() const { return supports_fast_mem; }
|
||||
|
||||
private:
|
||||
auto FindCodeBlockInfo(u64 rip) {
|
||||
return std::find_if(code_block_infos.begin(), code_block_infos.end(), [&](const auto& x) { return x.code_begin <= rip && x.code_end > rip; });
|
||||
}
|
||||
|
||||
bool supports_fast_mem = true;
|
||||
|
||||
void* signal_stack_memory = nullptr;
|
||||
|
||||
std::vector<CodeBlockInfo> code_block_infos;
|
||||
std::mutex code_block_infos_mutex;
|
||||
|
||||
struct sigaction old_sa_segv;
|
||||
struct sigaction old_sa_bus;
|
||||
|
||||
static void SigAction(int sig, siginfo_t* info, void* raw_context);
|
||||
};
|
||||
|
||||
SigHandler sig_handler;
|
||||
|
||||
SigHandler::SigHandler() {
|
||||
const size_t signal_stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||
|
||||
signal_stack_memory = std::malloc(signal_stack_size);
|
||||
|
||||
stack_t signal_stack;
|
||||
signal_stack.ss_sp = signal_stack_memory;
|
||||
signal_stack.ss_size = signal_stack_size;
|
||||
signal_stack.ss_flags = 0;
|
||||
if (sigaltstack(&signal_stack, nullptr) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = nullptr;
|
||||
sa.sa_sigaction = &SigHandler::SigAction;
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIGSEGV, &sa, &old_sa_segv) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGSEGV handler\n");
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n");
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SigHandler::~SigHandler() {
|
||||
std::free(signal_stack_memory);
|
||||
}
|
||||
|
||||
void SigHandler::AddCodeBlock(CodeBlockInfo cbi) {
|
||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||
if (auto iter = FindCodeBlockInfo(cbi.code_begin); iter != code_block_infos.end()) {
|
||||
code_block_infos.erase(iter);
|
||||
}
|
||||
code_block_infos.push_back(cbi);
|
||||
}
|
||||
|
||||
void SigHandler::RemoveCodeBlock(u64 rip) {
|
||||
std::lock_guard<std::mutex> guard(code_block_infos_mutex);
|
||||
const auto iter = FindCodeBlockInfo(rip);
|
||||
if (iter == code_block_infos.end()) {
|
||||
return;
|
||||
}
|
||||
code_block_infos.erase(iter);
|
||||
}
|
||||
|
||||
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define CTX_RIP (((ucontext_t*)raw_context)->uc_mcontext->__ss.__rip)
|
||||
# define CTX_RSP (((ucontext_t*)raw_context)->uc_mcontext->__ss.__rsp)
|
||||
#elif defined(__linux__)
|
||||
# define CTX_RIP (((ucontext_t*)raw_context)->uc_mcontext.gregs[REG_RIP])
|
||||
# define CTX_RSP (((ucontext_t*)raw_context)->uc_mcontext.gregs[REG_RSP])
|
||||
#elif defined(__FreeBSD__)
|
||||
# define CTX_RIP (((ucontext_t*)raw_context)->uc_mcontext.mc_rip)
|
||||
# define CTX_RSP (((ucontext_t*)raw_context)->uc_mcontext.mc_rsp)
|
||||
#else
|
||||
# error "Unknown platform"
|
||||
#endif
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(sig_handler.code_block_infos_mutex);
|
||||
|
||||
const auto iter = sig_handler.FindCodeBlockInfo(CTX_RIP);
|
||||
if (iter != sig_handler.code_block_infos.end()) {
|
||||
FakeCall fc = iter->cb(CTX_RIP);
|
||||
|
||||
CTX_RSP -= sizeof(u64);
|
||||
*mcl::bit_cast<u64*>(CTX_RSP) = fc.ret_rip;
|
||||
CTX_RIP = fc.call_rip;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print(stderr, "Unhandled {} at rip {:#016x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_RIP);
|
||||
|
||||
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler.old_sa_segv : &sig_handler.old_sa_bus;
|
||||
if (retry_sa->sa_flags & SA_SIGINFO) {
|
||||
retry_sa->sa_sigaction(sig, info, raw_context);
|
||||
return;
|
||||
}
|
||||
if (retry_sa->sa_handler == SIG_DFL) {
|
||||
signal(sig, SIG_DFL);
|
||||
return;
|
||||
}
|
||||
if (retry_sa->sa_handler == SIG_IGN) {
|
||||
return;
|
||||
}
|
||||
retry_sa->sa_handler(sig);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
struct ExceptionHandler::Impl final {
|
||||
Impl(BlockOfCode& code)
|
||||
: code_begin(mcl::bit_cast<u64>(code.getCode()))
|
||||
, code_end(code_begin + code.GetTotalCodeSize()) {}
|
||||
|
||||
void SetCallback(std::function<FakeCall(u64)> cb) {
|
||||
CodeBlockInfo cbi;
|
||||
cbi.code_begin = code_begin;
|
||||
cbi.code_end = code_end;
|
||||
cbi.cb = cb;
|
||||
sig_handler.AddCodeBlock(cbi);
|
||||
}
|
||||
|
||||
~Impl() {
|
||||
sig_handler.RemoveCodeBlock(code_begin);
|
||||
}
|
||||
|
||||
private:
|
||||
u64 code_begin, code_end;
|
||||
};
|
||||
|
||||
ExceptionHandler::ExceptionHandler() = default;
|
||||
ExceptionHandler::~ExceptionHandler() = default;
|
||||
|
||||
void ExceptionHandler::Register(BlockOfCode& code) {
|
||||
impl = std::make_unique<Impl>(code);
|
||||
}
|
||||
|
||||
bool ExceptionHandler::SupportsFastmem() const noexcept {
|
||||
return static_cast<bool>(impl) && sig_handler.SupportsFastmem();
|
||||
}
|
||||
|
||||
void ExceptionHandler::SetFastmemCallback(std::function<FakeCall(u64)> cb) {
|
||||
impl->SetCallback(cb);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
|
@ -13,8 +13,8 @@
|
|||
#include <mcl/bit_cast.hpp>
|
||||
#include <mcl/stdint.hpp>
|
||||
|
||||
#include "dynarmic/backend/exception_handler.h"
|
||||
#include "dynarmic/backend/x64/block_of_code.h"
|
||||
#include "dynarmic/backend/x64/exception_handler.h"
|
||||
#include "dynarmic/common/safe_ops.h"
|
||||
|
||||
using UBYTE = u8;
|
||||
|
@ -77,7 +77,9 @@ struct UNW_EXCEPTION_INFO {
|
|||
// OPTIONAL ARBITRARY HandlerData;
|
||||
};
|
||||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
namespace Dynarmic::Backend {
|
||||
|
||||
using namespace Dynarmic::Backend::X64;
|
||||
|
||||
struct PrologueInformation {
|
||||
std::vector<UNWIND_CODE> unwind_code;
|
||||
|
@ -259,4 +261,4 @@ void ExceptionHandler::SetFastmemCallback(std::function<FakeCall(u64)> cb) {
|
|||
impl->SetCallback(cb);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::X64
|
||||
} // namespace Dynarmic::Backend
|
||||
|
|
Loading…
Reference in a new issue