From 76dd09ea6025316980487b325095c2b3f3a2c891 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sun, 12 Sep 2021 01:57:45 -0700 Subject: [PATCH] Linux: Implements support for timer_create Needed to fix rt_sigtimedwait to use the raw syscall. Needed to have sigtimedwait and sigtimedwait_time64 parse siginfo_t correctly. glibc uses this to ensure it is sending the correct signal across from their helper thread. Needed to correctly parse sigval and sigevent for 32-bit. Passes my unit test for timer_create --- .../Tests/LinuxSyscalls/SignalDelegator.cpp | 11 +-- Source/Tests/LinuxSyscalls/Syscalls/Timer.cpp | 5 -- Source/Tests/LinuxSyscalls/x32/Signals.cpp | 69 +++++++++++++++- Source/Tests/LinuxSyscalls/x32/Timer.cpp | 6 ++ Source/Tests/LinuxSyscalls/x32/Types.h | 78 ++++++++++++++++++- Source/Tests/LinuxSyscalls/x64/Time.cpp | 6 ++ 6 files changed, 157 insertions(+), 18 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/SignalDelegator.cpp b/Source/Tests/LinuxSyscalls/SignalDelegator.cpp index 06c86b47d..dd1a39293 100644 --- a/Source/Tests/LinuxSyscalls/SignalDelegator.cpp +++ b/Source/Tests/LinuxSyscalls/SignalDelegator.cpp @@ -572,16 +572,7 @@ namespace FEX::HLE { return -EINVAL; } - sigset_t HostSet{}; - sigemptyset(&HostSet); - - for (int32_t i = 0; i < MAX_SIGNALS; ++i) { - if (*set & (1ULL << i)) { - sigaddset(&HostSet, i + 1); - } - } - - uint64_t Result = sigtimedwait(&HostSet, info, timeout); + uint64_t Result = ::syscall(SYS_rt_sigtimedwait, set, info, timeout); return Result == -1 ? -errno : Result; } diff --git a/Source/Tests/LinuxSyscalls/Syscalls/Timer.cpp b/Source/Tests/LinuxSyscalls/Syscalls/Timer.cpp index 3a096008c..c1b720c16 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/Timer.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls/Timer.cpp @@ -25,11 +25,6 @@ namespace FEX::HLE { SYSCALL_ERRNO(); }); - REGISTER_SYSCALL_IMPL(timer_create, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clockid, struct sigevent *sevp, kernel_timer_t *timerid) -> uint64_t { - uint64_t Result = ::syscall(SYS_timer_create, clockid, sevp, timerid); - SYSCALL_ERRNO(); - }); - REGISTER_SYSCALL_IMPL(timer_getoverrun, [](FEXCore::Core::CpuStateFrame *Frame, kernel_timer_t timerid) -> uint64_t { uint64_t Result = ::syscall(SYS_timer_getoverrun, timerid); SYSCALL_ERRNO(); diff --git a/Source/Tests/LinuxSyscalls/x32/Signals.cpp b/Source/Tests/LinuxSyscalls/x32/Signals.cpp index 50eda434e..3e323a548 100644 --- a/Source/Tests/LinuxSyscalls/x32/Signals.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Signals.cpp @@ -10,6 +10,7 @@ $end_info$ #include "Tests/LinuxSyscalls/x32/Types.h" #include +#include #include #include #include @@ -20,6 +21,53 @@ namespace FEXCore::Core { } namespace FEX::HLE::x32 { + void CopySigInfo(FEXCore::x86::siginfo_t *Info, siginfo_t const &Host) { + // Copy the basic things first + Info->si_signo = Host.si_signo; + Info->si_errno = Host.si_errno; + Info->si_code = Host.si_code; + + // Check si_code to determine how we need to interpret this + if (Info->si_code == SI_TIMER) { + // SI_TIMER means pid, uid, value + Info->_sifields._timer.tid = Host.si_timerid; + Info->_sifields._timer.overrun = Host.si_overrun; + Info->_sifields._timer.sigval.sival_int = Host.si_value.sival_int; + } + else { + // Now we need to copy over the more complex things + switch (Info->si_signo) { + case SIGSEGV: + case SIGBUS: + // This is the address trying to be accessed, not the RIP + Info->_sifields._sigfault.addr = static_cast(reinterpret_cast(Host.si_addr)); + break; + case SIGFPE: + case SIGILL: + // Can't really give a real result here. This is the RIP causing a sigill or sigfpe + // Claim at RIP 0 for now + Info->_sifields._sigfault.addr = 0; + break; + case SIGCHLD: + Info->_sifields._sigchld.pid = Host.si_pid; + Info->_sifields._sigchld.uid = Host.si_uid; + Info->_sifields._sigchld.status = Host.si_status; + Info->_sifields._sigchld.utime = Host.si_utime; + Info->_sifields._sigchld.stime = Host.si_stime; + break; + case SIGALRM: + case SIGVTALRM: + Info->_sifields._timer.tid = Host.si_timerid; + Info->_sifields._timer.overrun = Host.si_overrun; + Info->_sifields._timer.sigval.sival_int = Host.si_int; + break; + default: + LogMan::Msg::E("Unhandled siginfo_t for sigtimedwait: %d", Info->si_signo); + break; + } + } + } + void RegisterSignals() { REGISTER_SYSCALL_IMPL_X32(sigpending, [](FEXCore::Core::CpuStateFrame *Frame, compat_old_sigset_t *set) -> uint64_t { uint64_t HostSet{}; @@ -66,7 +114,7 @@ namespace FEX::HLE::x32 { return Result; }); - REGISTER_SYSCALL_IMPL_X32(rt_sigtimedwait, [](FEXCore::Core::CpuStateFrame *Frame, uint64_t *set, siginfo_t *info, const struct timespec32* timeout, size_t sigsetsize) -> uint64_t { + REGISTER_SYSCALL_IMPL_X32(rt_sigtimedwait, [](FEXCore::Core::CpuStateFrame *Frame, uint64_t *set, compat_ptr info, const struct timespec32* timeout, size_t sigsetsize) -> uint64_t { struct timespec* timeout_ptr{}; struct timespec tp64{}; if (timeout) { @@ -74,7 +122,24 @@ namespace FEX::HLE::x32 { timeout_ptr = &tp64; } - return FEX::HLE::_SyscallHandler->GetSignalDelegator()->GuestSigTimedWait(set, info, timeout_ptr, sigsetsize); + siginfo_t HostInfo{}; + uint64_t Result = FEX::HLE::_SyscallHandler->GetSignalDelegator()->GuestSigTimedWait(set, &HostInfo, timeout_ptr, sigsetsize); + if (Result != -1) { + // We need to translate the 64-bit siginfo_t to 32-bit siginfo_t + CopySigInfo(info, HostInfo); + } + return Result; + }); + + + REGISTER_SYSCALL_IMPL_X32(rt_sigtimedwait_time64, [](FEXCore::Core::CpuStateFrame *Frame, uint64_t *set, compat_ptr info, const struct timespec* timeout, size_t sigsetsize) -> uint64_t { + siginfo_t HostInfo{}; + uint64_t Result = FEX::HLE::_SyscallHandler->GetSignalDelegator()->GuestSigTimedWait(set, &HostInfo, timeout, sigsetsize); + if (Result != -1) { + // We need to translate the 64-bit siginfo_t to 32-bit siginfo_t + CopySigInfo(info, HostInfo); + } + return Result; }); } diff --git a/Source/Tests/LinuxSyscalls/x32/Timer.cpp b/Source/Tests/LinuxSyscalls/x32/Timer.cpp index a0bdf6edb..21624e799 100644 --- a/Source/Tests/LinuxSyscalls/x32/Timer.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Timer.cpp @@ -96,5 +96,11 @@ namespace FEX::HLE::x32 { } SYSCALL_ERRNO(); }); + + REGISTER_SYSCALL_IMPL_X32(timer_create, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clockid, compat_ptr sevp, kernel_timer_t *timerid) -> uint64_t { + sigevent Host = *sevp; + uint64_t Result = ::syscall(SYS_timer_create, clockid, &Host, timerid); + SYSCALL_ERRNO(); + }); } } diff --git a/Source/Tests/LinuxSyscalls/x32/Types.h b/Source/Tests/LinuxSyscalls/x32/Types.h index ec20f8992..aab41fc1e 100644 --- a/Source/Tests/LinuxSyscalls/x32/Types.h +++ b/Source/Tests/LinuxSyscalls/x32/Types.h @@ -13,6 +13,7 @@ $end_info$ #include #include #include +#include #include #include #include @@ -962,7 +963,6 @@ rlimit32 { static_assert(std::is_trivial>::value, "Needs to be trivial"); static_assert(sizeof(rlimit32) == 8, "Incorrect size"); - struct FEX_ANNOTATE("alias-x86_32-timex") FEX_ANNOTATE("fex-match") @@ -1048,4 +1048,80 @@ timex32 { static_assert(std::is_trivial::value, "Needs to be trivial"); static_assert(sizeof(timex32) == 128, "Incorrect size"); +union +FEX_ANNOTATE("alias-x86_32-sigval") +FEX_ANNOTATE("fex-match") +sigval32 { + int sival_int; + compat_ptr sival_ptr; + + sigval32() = delete; + + operator sigval() const { + sigval val{}; + val.sival_ptr = sival_ptr; + return val; + } + + sigval32(sigval val) { + sival_ptr = val.sival_ptr; + } +}; + +static_assert(std::is_trivial::value, "Needs to be trivial"); +static_assert(sizeof(sigval32) == 4, "Incorrect size"); + +constexpr size_t SIGEV_MAX_SIZE = 64; +constexpr size_t SIGEV_PAD_SIZE = (SIGEV_MAX_SIZE - (sizeof(int32_t) * 2 + sizeof(sigval32))) / sizeof(int32_t); + +struct +FEX_ANNOTATE("fex-match") +sigevent32 { + FEX::HLE::x32::sigval32 sigev_value; + int sigev_signo; + int sigev_notify; + union { + int _pad[SIGEV_PAD_SIZE]; + int _tid; + struct { + uint32_t _function; + uint32_t _attribute; + } _sigev_thread; + } _sigev_un; + + sigevent32() = delete; + + operator sigevent() const { + sigevent val{}; + val.sigev_value = sigev_value; + val.sigev_signo = sigev_signo; + val.sigev_notify = sigev_notify; + + if (sigev_notify == SIGEV_THREAD_ID) { + val._sigev_un._tid = _sigev_un._tid; + } + else if (sigev_notify == SIGEV_THREAD) { + val.sigev_notify_function = reinterpret_cast(_sigev_un._sigev_thread._function); + val.sigev_notify_attributes = reinterpret_cast(_sigev_un._sigev_thread._attribute); + } + return val; + } + + sigevent32(sigevent val) + : sigev_value { val.sigev_value } { + sigev_signo = val.sigev_signo; + sigev_notify = val.sigev_notify; + + if (sigev_notify == SIGEV_THREAD_ID) { + _sigev_un._tid = val._sigev_un._tid; + } + else if (sigev_notify == SIGEV_THREAD) { + sigev_notify_function = static_cast(reinterpret_cast(val._sigev_un._sigev_thread._function)); + sigev_notify_attributes = static_cast(reinterpret_cast(val._sigev_un._sigev_thread._attribute)); + } + } +}; + +static_assert(std::is_trivial::value, "Needs to be trivial"); +static_assert(sizeof(sigval32) == 4, "Incorrect size"); } diff --git a/Source/Tests/LinuxSyscalls/x64/Time.cpp b/Source/Tests/LinuxSyscalls/x64/Time.cpp index c3c595cd6..2dc800c98 100644 --- a/Source/Tests/LinuxSyscalls/x64/Time.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Time.cpp @@ -94,6 +94,7 @@ namespace FEX::HLE::x64 { uint64_t Result = ::syscall(SYS_timer_gettime, timerid, curr_value); SYSCALL_ERRNO(); }); + REGISTER_SYSCALL_IMPL_X64(adjtimex, [](FEXCore::Core::CpuStateFrame *Frame, struct timex *buf) -> uint64_t { uint64_t Result = ::adjtimex(buf); SYSCALL_ERRNO(); @@ -103,6 +104,11 @@ namespace FEX::HLE::x64 { uint64_t Result = ::clock_adjtime(clk_id, buf); SYSCALL_ERRNO(); }); + + REGISTER_SYSCALL_IMPL_X64(timer_create, [](FEXCore::Core::CpuStateFrame *Frame, clockid_t clockid, struct sigevent *sevp, kernel_timer_t *timerid) -> uint64_t { + uint64_t Result = ::syscall(SYS_timer_create, clockid, sevp, timerid); + SYSCALL_ERRNO(); + }); } }