diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index db8e1e2a4..ffe615e86 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -627,6 +627,18 @@ if(BUILD_TESTING) endif() endif() +add_test_executable(guest_libc_mutex_init_with_name_tests EXCLUDE_FROM_ALL ${LIBS_INCLUDE_DIR}/tests/GuestLibcMutexInitWithName.cpp) +target_include_directories(guest_libc_mutex_init_with_name_tests PRIVATE ${LIBS_INCLUDE_DIR}) +target_link_libraries(guest_libc_mutex_init_with_name_tests PRIVATE libc) +configure_windows_unwind(guest_libc_mutex_init_with_name_tests) +if(BUILD_TESTING) + set_target_properties(guest_libc_mutex_init_with_name_tests PROPERTIES EXCLUDE_FROM_ALL FALSE) + add_test(NAME guest_libc_mutex_init_with_name COMMAND guest_libc_mutex_init_with_name_tests) + if(WIN32) + set_tests_properties(guest_libc_mutex_init_with_name PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") + endif() +endif() + if(MINGW) if(BUILD_TESTING) enable_language(ASM) diff --git a/core/libs/prx/libc/src/Threads.cpp b/core/libs/prx/libc/src/Threads.cpp index 44db68abe..fd8aa32c7 100644 --- a/core/libs/prx/libc/src/Threads.cpp +++ b/core/libs/prx/libc/src/Threads.cpp @@ -107,6 +107,10 @@ int APS5_VABI _Mtx_init_nid_postfix(void** handle, int type) { return threadSuccess; } +int APS5_VABI _Mtx_init_with_name_nid_postfix(void** handle, int type, const char*) { + return _Mtx_init_nid_postfix(handle, type); +} + void APS5_VABI _Mtx_destroy_nid_postfix(void** handle) { if (!handle) throw std::invalid_argument("_Mtx_destroy: null mutex handle pointer"); diff --git a/core/libs/tests/GuestLibcMutexInitWithName.cpp b/core/libs/tests/GuestLibcMutexInitWithName.cpp new file mode 100644 index 000000000..2501aef36 --- /dev/null +++ b/core/libs/tests/GuestLibcMutexInitWithName.cpp @@ -0,0 +1,41 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include + +extern "C" { +int APS5_VABI _Mtx_init_with_name_nid_postfix(void** handle, int type, const char* name); +void APS5_VABI _Mtx_destroy_nid_postfix(void** handle); +int APS5_VABI _Mtx_lock_nid_postfix(void** handle); +int APS5_VABI _Mtx_unlock_nid_postfix(void** handle); +} + +static void Require(bool value) { if (!value) std::abort(); } + +template +static void RequireThrows(TCall call) { + try { + call(); + } catch (const TException&) { + return; + } + std::abort(); +} + +constexpr int success = 0; +constexpr int busy = 3; +constexpr int plain = 0x01; + +int main() { + void* mutex = nullptr; + Require(_Mtx_init_with_name_nid_postfix(&mutex, plain, "GameMutex") == success); + Require(mutex != nullptr); + Require(_Mtx_lock_nid_postfix(&mutex) == success); + Require(_Mtx_lock_nid_postfix(&mutex) == busy); + Require(_Mtx_unlock_nid_postfix(&mutex) == success); + _Mtx_destroy_nid_postfix(&mutex); + Require(mutex == nullptr); + + RequireThrows([] { + _Mtx_init_with_name_nid_postfix(nullptr, plain, "NullHandle"); + }); +} diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index beed675fd..b673ca6f1 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -5,6 +5,7 @@ - [_Mtx_trylock](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `k6pGNMwJB08`) - returns busy for a non-recursive mutex already owned by the caller, assumed to match `_Mtx_lock`'s ownership handling; exact console behavior is unverified. - [nearbyintf](../../../core/libs/prx/libc/src/Math.cpp) (libc, NID `c+4r-T-tEIc`) uses the host rounding mode, not the console's; console rounding mode state remains unverified. - [_Thrd_sleep](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `jfRI3snge3o`) follows the Dinkumware threads library shipped with [Visual C++ 2013](https://github.com/adityaramesh/concurrency_runtime/tree/master/VS2013_SP2/crt/src/thr) (`xtime.c`) and C11 Annex K, without console verification. It reads a 16-byte xtime (64-bit seconds and nanoseconds) on the guest `CLOCK_REALTIME`, normalizes the nanoseconds and sleeps through `nanosleep`, retrying on `EINTR`, so it is a cancellation point; a null xtime or one whose normalization overflows throws. The console's `_Thrd_sleep` (disassembled from a title's `libc.prx`) also takes an optional `xtime*` of remaining time and returns `int`; here the sleep is never cut short, so it returns 0 and leaves the remaining time untouched. +- [_Mtx_init_with_name](../../../core/libs/prx/libc/src/Threads.cpp) (libc, NID `tgioGpKtmbE`) forwards to `_Mtx_init` and ignores the mutex name parameter. - [Guest arena](../../../core/libs/prx/libc/src/GuestArena.cpp) (libc) - the application map area is assumed to end at 0xFC_0000_0000, as `SCE_KERNEL_APP_MAP_AREA_END_ADDR` does on the PS4. On Windows, host allocations made in the arena before libc loads stay, and a fixed guest mapping over one throws - [sceLibcInternalBacktraceForGame](../../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - `(const char* heapName)` and its return follow the `libc.prx` ASTRO BOT (PPSA21564 01.007.000) bundles: its four calls (0x6801, 0x7F46, 0xE75E, 0xEE02) are in the `malloc_stats` and `sceLibcMspaceMallocStats` paths, each after a `[%s] ...` line printed with the same heap name buffer, and the caller ignores the result and continues. The console's backtrace output is not reproduced: the heap name and the guest return address are written to the host standard error - [sceLibcInternalHeapErrorReportForGame](../../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libSceLibcInternal, implemented in libc) - `(void* heap, void* block, uint32 error)` follows the same `libc.prx` (call at 0xF634): it passes the heap, the block and the nonzero result of its block check, and then calls a function that never returns, so the report only has to return. The meaning of the error values is unknown; the three values and the guest return address are written to the host standard error