mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-18 23:07:07 -05:00
PSXS5, SwanStationPS5 and Nuvio stop at start-up on setcontext on main, and EVO Player and ProsperoStore do on Windows. The three are imported by the statically linked OpenSSL's ASYNC code. The three use FreeBSD 11's amd64 ucontext_t (mcontext at 0x10, mc_len 800, uc_link at 0x330, 0x370 bytes), which the titles are built against: EVO Player's ASYNC_is_capable captures into a 0x370-byte stack buffer, and async_fibre_makecontext sets uc_link at 0x330 and uc_stack at 0x338. getcontext saves every register at its call in assembly and stores the context as FreeBSD's sys_getcontext does: rax and rdx as 0, the carry flag clear, the FXSAVE image and the signal mask, and nothing from uc_link on. setcontext checks mc_len and mc_flags as set_mcontext does, installs the signal mask, masks MXCSR with the host's MXCSR_MASK and returns through iretq, so every general register and the flags are restored and nothing is written below the target stack pointer. makecontext follows FreeBSD 11's amd64 libc. Windows checks guard-page growth and exception unwinding against the stack bounds in the TEB. setcontext and longjmp now set them to the stack they switch to, which makecontext registers and pins writable as libSceFiber does for fiber stacks. OpenSSL enters a context with setcontext and then switches with _setjmp and _longjmp.
64 lines
2.6 KiB
C++
64 lines
2.6 KiB
C++
#include "prx/libc/include/GuestStacks.hpp"
|
|
#include "prx/libc/include/Shutdown.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "prx/libkernel/System/include/UserContext.hpp"
|
|
#include <cstdarg>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
|
|
extern "C" int APS5_VABI setcontext_nid_postfix(const UserUcontext* context);
|
|
|
|
namespace {
|
|
|
|
using ContextFunction = void (APS5_VABI*)(std::uint64_t, std::uint64_t, std::uint64_t, std::uint64_t, std::uint64_t, std::uint64_t);
|
|
|
|
constexpr int MaximumArguments = 6;
|
|
constexpr std::uint64_t MinimumStackSize = 2048;
|
|
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
[[noreturn]] void APS5_VABI Aps5MakecontextStart_nid_no_patch(UserUcontext* context, ContextFunction function, const std::uint64_t* arguments) {
|
|
function(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
|
|
if (context->link == nullptr) LibcExit_nid_no_patch(0);
|
|
setcontext_nid_postfix(context->link);
|
|
std::abort();
|
|
}
|
|
|
|
void APS5_VABI makecontext_nid_postfix(UserUcontext* context, void (APS5_VABI* start)(), int count, ...) {
|
|
if (context == nullptr || context->mcontext.len != sizeof(UserMcontext)) return;
|
|
if (count < 0 || count > MaximumArguments || context->stackPointer == nullptr || context->stackSize < MinimumStackSize) {
|
|
context->mcontext.len = 0;
|
|
return;
|
|
}
|
|
const auto top = (reinterpret_cast<std::uintptr_t>(context->stackPointer) + context->stackSize) & ~static_cast<std::uintptr_t>(15);
|
|
auto* arguments = reinterpret_cast<std::uint64_t*>(top) - MaximumArguments;
|
|
auto* stack = arguments - 1;
|
|
#ifdef _WIN32
|
|
__builtin_sysv_va_list list;
|
|
__builtin_sysv_va_start(list, count);
|
|
for (int index = 0; index < count; ++index) arguments[index] = __builtin_va_arg(list, std::uint64_t);
|
|
__builtin_sysv_va_end(list);
|
|
#else
|
|
std::va_list list;
|
|
va_start(list, count);
|
|
for (int index = 0; index < count; ++index) arguments[index] = va_arg(list, std::uint64_t);
|
|
va_end(list);
|
|
#endif
|
|
for (int index = count; index < MaximumArguments; ++index) arguments[index] = 0;
|
|
*stack = 0;
|
|
auto& machine = context->mcontext;
|
|
machine.rdi = reinterpret_cast<std::uint64_t>(context);
|
|
machine.rsi = reinterpret_cast<std::uint64_t>(start);
|
|
machine.rdx = reinterpret_cast<std::uint64_t>(arguments);
|
|
machine.rbp = 0;
|
|
machine.rbx = reinterpret_cast<std::uint64_t>(stack);
|
|
machine.rsp = reinterpret_cast<std::uint64_t>(stack);
|
|
machine.rip = reinterpret_cast<std::uint64_t>(&Aps5MakecontextStart_nid_no_patch);
|
|
GuestStacks::GuestStackRegister_nid_no_patch(context->stackPointer, context->stackSize);
|
|
}
|
|
|
|
}
|