|
|
@ -22,6 +22,7 @@ |
|
|
#include <sys/mman.h>
|
|
|
#include <sys/mman.h>
|
|
|
|
|
|
|
|
|
#include "common/assert.h"
|
|
|
#include "common/assert.h"
|
|
|
|
|
|
#include "common/logging.h"
|
|
|
#include "common/common_types.h"
|
|
|
#include "common/common_types.h"
|
|
|
#include "dynarmic/backend/exception_handler.h"
|
|
|
#include "dynarmic/backend/exception_handler.h"
|
|
|
#include "dynarmic/common/context.h"
|
|
|
#include "dynarmic/common/context.h"
|
|
|
@ -53,23 +54,21 @@ class SigHandler { |
|
|
return e.first <= offset && e.first + e.second.size > offset; |
|
|
return e.first <= offset && e.first + e.second.size > offset; |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
static void SigAction(int sig, siginfo_t* info, void* raw_context); |
|
|
|
|
|
|
|
|
|
|
|
bool supports_fast_mem = true; |
|
|
|
|
|
void* signal_stack_memory = nullptr; |
|
|
|
|
|
ankerl::unordered_dense::map<u64, CodeBlockInfo> code_block_infos; |
|
|
ankerl::unordered_dense::map<u64, CodeBlockInfo> code_block_infos; |
|
|
std::shared_mutex code_block_infos_mutex; |
|
|
std::shared_mutex code_block_infos_mutex; |
|
|
struct sigaction old_sa_segv; |
|
|
struct sigaction old_sa_segv; |
|
|
struct sigaction old_sa_bus; |
|
|
struct sigaction old_sa_bus; |
|
|
std::size_t signal_stack_size; |
|
|
|
|
|
|
|
|
std::unique_ptr<uint8_t[]> signal_stack_memory; |
|
|
|
|
|
bool supports_fast_mem = true; |
|
|
public: |
|
|
public: |
|
|
SigHandler() noexcept { |
|
|
SigHandler() noexcept { |
|
|
signal_stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024); |
|
|
|
|
|
signal_stack_memory = mmap(nullptr, signal_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
|
|
|
|
|
|
|
|
auto const stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024); |
|
|
|
|
|
signal_stack_memory = std::make_unique<uint8_t[]>(stack_size); |
|
|
|
|
|
|
|
|
stack_t signal_stack{}; |
|
|
stack_t signal_stack{}; |
|
|
signal_stack.ss_sp = signal_stack_memory; |
|
|
|
|
|
signal_stack.ss_size = signal_stack_size; |
|
|
|
|
|
|
|
|
signal_stack.ss_sp = signal_stack_memory.get(); |
|
|
|
|
|
signal_stack.ss_size = stack_size; |
|
|
signal_stack.ss_flags = 0; |
|
|
signal_stack.ss_flags = 0; |
|
|
if (sigaltstack(&signal_stack, nullptr) != 0) { |
|
|
if (sigaltstack(&signal_stack, nullptr) != 0) { |
|
|
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n"); |
|
|
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n"); |
|
|
@ -87,7 +86,7 @@ public: |
|
|
supports_fast_mem = false; |
|
|
supports_fast_mem = false; |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
#ifdef __APPLE__
|
|
|
|
|
|
|
|
|
#if defined(__APPLE__)
|
|
|
if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) { |
|
|
if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) { |
|
|
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n"); |
|
|
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n"); |
|
|
supports_fast_mem = false; |
|
|
supports_fast_mem = false; |
|
|
@ -96,10 +95,6 @@ public: |
|
|
#endif
|
|
|
#endif
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
~SigHandler() noexcept { |
|
|
|
|
|
munmap(signal_stack_memory, signal_stack_size); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void AddCodeBlock(u64 offset, CodeBlockInfo cbi) noexcept { |
|
|
void AddCodeBlock(u64 offset, CodeBlockInfo cbi) noexcept { |
|
|
std::unique_lock guard(code_block_infos_mutex); |
|
|
std::unique_lock guard(code_block_infos_mutex); |
|
|
code_block_infos.insert_or_assign(offset, cbi); |
|
|
code_block_infos.insert_or_assign(offset, cbi); |
|
|
@ -109,14 +104,17 @@ public: |
|
|
code_block_infos.erase(offset); |
|
|
code_block_infos.erase(offset); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool SupportsFastmem() const noexcept { return supports_fast_mem; } |
|
|
|
|
|
|
|
|
[[nodiscard]] inline bool SupportsFastmem() const noexcept { |
|
|
|
|
|
return supports_fast_mem; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void RegisterHandler(); |
|
|
|
|
|
static void SigAction(int sig, siginfo_t* info, void* raw_context); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
std::mutex handler_lock; |
|
|
|
|
|
std::optional<SigHandler> sig_handler; |
|
|
std::optional<SigHandler> sig_handler; |
|
|
|
|
|
|
|
|
void RegisterHandler() { |
|
|
|
|
|
std::lock_guard<std::mutex> guard(handler_lock); |
|
|
|
|
|
|
|
|
void SigHandler::RegisterHandler() { |
|
|
if (!sig_handler) { |
|
|
if (!sig_handler) { |
|
|
sig_handler.emplace(); |
|
|
sig_handler.emplace(); |
|
|
} |
|
|
} |
|
|
@ -125,51 +123,27 @@ void RegisterHandler() { |
|
|
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { |
|
|
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { |
|
|
DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS); |
|
|
DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS); |
|
|
CTX_DECLARE(raw_context); |
|
|
CTX_DECLARE(raw_context); |
|
|
#if defined(ARCHITECTURE_x86_64)
|
|
|
|
|
|
{ |
|
|
{ |
|
|
std::shared_lock guard(sig_handler->code_block_infos_mutex); |
|
|
std::shared_lock guard(sig_handler->code_block_infos_mutex); |
|
|
if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) { |
|
|
if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) { |
|
|
FakeCall fc = iter->second.cb(CTX_PC); |
|
|
FakeCall fc = iter->second.cb(CTX_PC); |
|
|
|
|
|
#if defined(ARCHITECTURE_x86_64)
|
|
|
CTX_SP -= sizeof(u64); |
|
|
CTX_SP -= sizeof(u64); |
|
|
*std::bit_cast<u64*>(CTX_SP) = fc.ret_rip; |
|
|
*std::bit_cast<u64*>(CTX_SP) = fc.ret_rip; |
|
|
CTX_PC = fc.call_rip; |
|
|
CTX_PC = fc.call_rip; |
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC); |
|
|
|
|
|
#elif defined(ARCHITECTURE_arm64)
|
|
|
#elif defined(ARCHITECTURE_arm64)
|
|
|
{ |
|
|
|
|
|
std::shared_lock guard(sig_handler->code_block_infos_mutex); |
|
|
|
|
|
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) { |
|
|
|
|
|
FakeCall fc = iter->second.cb(CTX_PC); |
|
|
|
|
|
CTX_PC = fc.call_pc; |
|
|
CTX_PC = fc.call_pc; |
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC); |
|
|
|
|
|
#elif defined(ARCHITECTURE_riscv64)
|
|
|
#elif defined(ARCHITECTURE_riscv64)
|
|
|
{ |
|
|
|
|
|
std::shared_lock guard(sig_handler->code_block_infos_mutex); |
|
|
|
|
|
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_SEPC); iter != sig_handler->code_block_infos.end()) { |
|
|
|
|
|
FakeCall fc = iter->second.cb(CTX_SEPC); |
|
|
|
|
|
CTX_SEPC = fc.call_sepc; |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_SEPC); |
|
|
|
|
|
|
|
|
CTX_PC = fc.call_sepc; |
|
|
#elif defined(ARCHITECTURE_loongarch64)
|
|
|
#elif defined(ARCHITECTURE_loongarch64)
|
|
|
{ |
|
|
|
|
|
std::shared_lock guard(sig_handler->code_block_infos_mutex); |
|
|
|
|
|
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) { |
|
|
|
|
|
FakeCall fc = iter->second.cb(CTX_PC); |
|
|
|
|
|
CTX_PC = fc.call_pc; |
|
|
CTX_PC = fc.call_pc; |
|
|
|
|
|
#else
|
|
|
|
|
|
ASSERT(false); |
|
|
|
|
|
#endif
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC); |
|
|
|
|
|
#else
|
|
|
|
|
|
# error "Invalid architecture"
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
LOG_ERROR(Core, "Unhandled {} at {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC); |
|
|
|
|
|
|
|
|
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus; |
|
|
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus; |
|
|
if (retry_sa->sa_flags & SA_SIGINFO) { |
|
|
if (retry_sa->sa_flags & SA_SIGINFO) { |
|
|
@ -190,9 +164,10 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) { |
|
|
|
|
|
|
|
|
struct ExceptionHandler::Impl final { |
|
|
struct ExceptionHandler::Impl final { |
|
|
Impl(u64 offset_, u64 size_) |
|
|
Impl(u64 offset_, u64 size_) |
|
|
: offset(offset_) |
|
|
|
|
|
, size(size_) { |
|
|
|
|
|
RegisterHandler(); |
|
|
|
|
|
|
|
|
: offset(offset_) |
|
|
|
|
|
, size(size_) |
|
|
|
|
|
{ |
|
|
|
|
|
SigHandler::RegisterHandler(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void SetCallback(std::function<FakeCall(u64)> cb) { |
|
|
void SetCallback(std::function<FakeCall(u64)> cb) { |
|
|
|