mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-01-20 15:40:30 +01:00
orbis: propagate SIGSEGV, SIGBUS and SIGFPE in userspace to guest handler
fix minor race fix signal delivery without handler freeze
This commit is contained in:
parent
70fa577a7b
commit
f7651d7eae
|
|
@ -325,14 +325,14 @@ void Thread::sendSignal(int signo) {
|
|||
}
|
||||
}
|
||||
|
||||
if (pthread_sigqueue(getNativeHandle(), SIGUSR1, {.sival_int = signo})) {
|
||||
perror("pthread_sigqueue");
|
||||
}
|
||||
|
||||
|
||||
// TODO: suspend uses another delivery confirmation
|
||||
if (signo != -1) {
|
||||
interruptedMtx.store(1, std::memory_order::release);
|
||||
|
||||
if (pthread_sigqueue(getNativeHandle(), SIGUSR1, {.sival_int = signo})) {
|
||||
perror("pthread_sigqueue");
|
||||
}
|
||||
|
||||
while (interruptedMtx.wait(1, std::chrono::microseconds(1000)) !=
|
||||
std::errc{}) {
|
||||
if (interruptedMtx.load() == 0) {
|
||||
|
|
@ -343,6 +343,10 @@ void Thread::sendSignal(int signo) {
|
|||
perror("pthread_sigqueue");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pthread_sigqueue(getNativeHandle(), SIGUSR1, {.sival_int = signo})) {
|
||||
perror("pthread_sigqueue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "ipmi.hpp"
|
||||
#include "linker.hpp"
|
||||
#include "ops.hpp"
|
||||
#include "orbis/ucontext.hpp"
|
||||
#include "orbis/utils/Logs.hpp"
|
||||
#include "rx/Config.hpp"
|
||||
#include "rx/mem.hpp"
|
||||
|
|
@ -16,6 +17,7 @@
|
|||
#include "vfs.hpp"
|
||||
#include "vm.hpp"
|
||||
#include "xbyak/xbyak.h"
|
||||
#include <optional>
|
||||
#include <orbis/utils/Rc.hpp>
|
||||
#include <print>
|
||||
#include <rx/Version.hpp>
|
||||
|
|
@ -170,6 +172,37 @@ handle_signal(int sig, siginfo_t *info, void *ucontext) {
|
|||
} else {
|
||||
rx::printStackTrace(reinterpret_cast<ucontext_t *>(ucontext), 2);
|
||||
}
|
||||
|
||||
if (orbis::g_currentThread != nullptr) {
|
||||
auto toGuestSigno = [](int sig) -> std::optional<orbis::Signal> {
|
||||
switch (sig) {
|
||||
case SIGSEGV:
|
||||
return orbis::kSigSegv;
|
||||
|
||||
case SIGBUS:
|
||||
return orbis::kSigBus;
|
||||
|
||||
case SIGFPE:
|
||||
return orbis::kSigFpe;
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
if (auto guestSigno = toGuestSigno(sig)) {
|
||||
auto context = reinterpret_cast<ucontext_t *>(ucontext);
|
||||
bool inGuestCode =
|
||||
context->uc_mcontext.gregs[REG_RIP] < orbis::kMaxAddress;
|
||||
|
||||
if (inGuestCode) {
|
||||
if (rx::thread::invokeSignalHandler(orbis::g_currentThread,
|
||||
*guestSigno, context)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct sigaction act{};
|
||||
|
|
|
|||
|
|
@ -129,42 +129,16 @@ handleSigUser(int sig, siginfo_t *info, void *ucontext) {
|
|||
} else if (guestSignal >= 0) {
|
||||
ORBIS_LOG_WARNING(__FUNCTION__, "handled signal", guestSignal, inGuestCode,
|
||||
::getpid(), thread->tid);
|
||||
auto it = thread->tproc->sigActions.find(guestSignal);
|
||||
|
||||
if (it != thread->tproc->sigActions.end()) {
|
||||
auto guestContext = reinterpret_cast<ucontext_t *>(
|
||||
inGuestCode ? context : thread->context);
|
||||
if (!rx::thread::invokeSignalHandler(thread, guestSignal,
|
||||
inGuestCode ? context : nullptr)) {
|
||||
// no handler, mark signal as delivered
|
||||
std::uint32_t prevValue = 1;
|
||||
if (thread->interruptedMtx.compare_exchange_strong(prevValue, 0)) {
|
||||
thread->interruptedMtx.notify_one();
|
||||
}
|
||||
|
||||
auto sigact = it->second;
|
||||
thread->setSigMask(sigact.mask);
|
||||
auto handlerPtr = reinterpret_cast<std::uintptr_t>(sigact.handler);
|
||||
|
||||
auto rsp = guestContext->uc_mcontext.gregs[REG_RSP];
|
||||
|
||||
ORBIS_LOG_WARNING(__FUNCTION__, "invoking signal handler", guestSignal,
|
||||
rsp, thread->stackStart, thread->stackEnd);
|
||||
|
||||
// FIXME: alt stack?
|
||||
rsp -= 128; // redzone
|
||||
rsp -= sizeof(orbis::SigFrame);
|
||||
|
||||
// FIXME handle flags
|
||||
auto &sigFrame = *std::bit_cast<orbis::SigFrame *>(rsp);
|
||||
sigFrame = {};
|
||||
|
||||
rx::thread::copyContext(thread, sigFrame.context, *guestContext);
|
||||
sigFrame.info.signo = guestSignal;
|
||||
sigFrame.handler = handlerPtr;
|
||||
|
||||
guestContext->uc_mcontext.gregs[REG_RDI] = guestSignal; // arg1, signo
|
||||
guestContext->uc_mcontext.gregs[REG_RSI] =
|
||||
std::bit_cast<std::uintptr_t>(&sigFrame.info); // arg2, siginfo
|
||||
guestContext->uc_mcontext.gregs[REG_RDX] =
|
||||
std::bit_cast<std::uintptr_t>(&sigFrame.context); // arg3, ucontext
|
||||
guestContext->uc_mcontext.gregs[REG_RCX] = 0; // arg4, si_addr
|
||||
guestContext->uc_mcontext.gregs[REG_RIP] = handlerPtr;
|
||||
|
||||
guestContext->uc_mcontext.gregs[REG_RSP] = rx::alignDown(rsp, 16);
|
||||
guestSignal = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,6 +160,50 @@ std::size_t rx::thread::getSigAltStackSize() {
|
|||
return sigStackSize;
|
||||
}
|
||||
|
||||
bool rx::thread::invokeSignalHandler(orbis::Thread *thread, int guestSignal,
|
||||
ucontext_t *context) {
|
||||
auto it = thread->tproc->sigActions.find(guestSignal);
|
||||
|
||||
if (it == thread->tproc->sigActions.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto guestContext =
|
||||
reinterpret_cast<ucontext_t *>(context ? context : thread->context);
|
||||
|
||||
auto sigact = it->second;
|
||||
thread->setSigMask(sigact.mask);
|
||||
auto handlerPtr = reinterpret_cast<std::uintptr_t>(sigact.handler);
|
||||
|
||||
auto rsp = guestContext->uc_mcontext.gregs[REG_RSP];
|
||||
|
||||
ORBIS_LOG_WARNING(__FUNCTION__, "invoking signal handler", guestSignal, rsp,
|
||||
thread->stackStart, thread->stackEnd);
|
||||
|
||||
// FIXME: alt stack?
|
||||
rsp -= 128; // redzone
|
||||
rsp -= sizeof(orbis::SigFrame);
|
||||
|
||||
// FIXME handle flags
|
||||
auto &sigFrame = *std::bit_cast<orbis::SigFrame *>(rsp);
|
||||
sigFrame = {};
|
||||
|
||||
rx::thread::copyContext(thread, sigFrame.context, *guestContext);
|
||||
sigFrame.info.signo = guestSignal;
|
||||
sigFrame.handler = handlerPtr;
|
||||
|
||||
guestContext->uc_mcontext.gregs[REG_RDI] = guestSignal; // arg1, signo
|
||||
guestContext->uc_mcontext.gregs[REG_RSI] =
|
||||
std::bit_cast<std::uintptr_t>(&sigFrame.info); // arg2, siginfo
|
||||
guestContext->uc_mcontext.gregs[REG_RDX] =
|
||||
std::bit_cast<std::uintptr_t>(&sigFrame.context); // arg3, ucontext
|
||||
guestContext->uc_mcontext.gregs[REG_RCX] = 0; // arg4, si_addr
|
||||
guestContext->uc_mcontext.gregs[REG_RIP] = handlerPtr;
|
||||
|
||||
guestContext->uc_mcontext.gregs[REG_RSP] = rx::alignDown(rsp, 16);
|
||||
return true;
|
||||
}
|
||||
|
||||
void rx::thread::copyContext(orbis::MContext &dst, const mcontext_t &src) {
|
||||
// dst.onstack = src.gregs[REG_ONSTACK];
|
||||
dst.rdi = src.gregs[REG_RDI];
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ void setupThisThread();
|
|||
void copyContext(orbis::MContext &dst, const mcontext_t &src);
|
||||
void copyContext(orbis::Thread *thread, orbis::UContext &dst,
|
||||
const ucontext_t &src);
|
||||
bool invokeSignalHandler(orbis::Thread *thread, int signo,
|
||||
ucontext_t *context = nullptr);
|
||||
void setContext(orbis::Thread *thread, const orbis::UContext &src);
|
||||
void invoke(orbis::Thread *thread);
|
||||
} // namespace rx::thread
|
||||
|
|
|
|||
Loading…
Reference in a new issue