diff --git a/core/libs/prx/libSceAmpr/Export.cpp b/core/libs/prx/libSceAmpr/Export.cpp index f1918902d..fba6d9efb 100644 --- a/core/libs/prx/libSceAmpr/Export.cpp +++ b/core/libs/prx/libSceAmpr/Export.cpp @@ -898,15 +898,9 @@ int APS5_VABI sceAmprAmmWaitCommandBufferCompletion(std::uint32_t id) { return AmmSubmitted_nid_no_patch(id) ? 0 : SCE_KERNEL_ERROR_ESRCH; } -int APS5_VABI _ZNSt8ios_base7_AddstdEPS__nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - [[noreturn]] void APS5_VABI _ZSt16_Throw_Cpp_errori_nid_postfix(int code) { ThrowCppError_nid_no_patch(code); } - int APS5_VABI _Thrd_join_nid_postfix(Pthread thread, int* code) { void* result = nullptr; if (pthread_join_nid_postfix(thread, &result) != 0) return ThrdError; @@ -957,16 +951,11 @@ void APS5_VABI _ZNSt4_Pad7_LaunchEPP7pthread_nid_postfix(GuestPad* pad, Pthread* if (created != 0) ThrowCppError_nid_no_patch(CppResourceUnavailableTryAgain); while (!pad->started) RequireThrd(_Cnd_wait_nid_postfix(&pad->condition, &pad->mutex), "_Pad::_Launch: _Cnd_wait"); } - int APS5_VABI _ZNSt7num_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; } -int APS5_VABI _Cnd_signal_nid_postfix(void** handle) { - return SignalCondition_nid_no_patch(handle); -} - void APS5_VABI _Unlock_shared_ptr_spin_lock_nid_postfix(void) { sharedPtrSpinLock.clear(std::memory_order_release); } @@ -975,11 +964,6 @@ int APS5_VABI _Cnd_init_with_name_nid_postfix(void** handle, const char*) { return _Cnd_init_nid_postfix(handle); } -int APS5_VABI _ZTVN10__cxxabiv120__function_type_infoE_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI _ZTVSt7num_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; @@ -995,11 +979,6 @@ int APS5_VABI _ZNSt8time_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE2idE_ return 0; } -int APS5_VABI _ZTVN10__cxxabiv119__pointer_type_infoE_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI _ZNSt8time_getIcSt19istreambuf_iteratorIcSt11char_traitsIcEEE7_GetcatEPPKNSt6locale5facetEPKS5__nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index 8f2342a25..50a868268 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -48,6 +48,7 @@ target_sources(libc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/RealPath.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/CxxAbiSupport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Threads.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Backtrace.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/exception/Exports.cpp ) @@ -214,7 +215,7 @@ if(BUILD_TESTING) endif() add_test_executable(guest_libc_threads_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibcThreads.cpp) target_include_directories(guest_libc_threads_tests PRIVATE ${LIBS_INCLUDE_DIR}) - target_link_libraries(guest_libc_threads_tests PRIVATE libc) + target_link_libraries(guest_libc_threads_tests PRIVATE libc libkernel) configure_windows_unwind(guest_libc_threads_tests) add_test(NAME guest_libc_threads COMMAND guest_libc_threads_tests) set_tests_properties(guest_libc_threads PROPERTIES TIMEOUT 20) diff --git a/core/libs/prx/libc/include/specifics/itanium/CxxAbi.hpp b/core/libs/prx/libc/include/specifics/itanium/CxxAbi.hpp index 53d45d895..f4957fd7b 100644 --- a/core/libs/prx/libc/include/specifics/itanium/CxxAbi.hpp +++ b/core/libs/prx/libc/include/specifics/itanium/CxxAbi.hpp @@ -39,6 +39,8 @@ public: class __pointer_type_info : public __pbase_type_info {}; +class __function_type_info : public std::type_info {}; + class __pointer_to_member_type_info : public __pbase_type_info { public: const __class_type_info* __context; diff --git a/core/libs/prx/libc/src/Backtrace.cpp b/core/libs/prx/libc/src/Backtrace.cpp new file mode 100644 index 000000000..528622570 --- /dev/null +++ b/core/libs/prx/libc/src/Backtrace.cpp @@ -0,0 +1,16 @@ +#include +#include "prx/libc/include/General.hpp" + +extern "C" { + +int APS5_VABI sceLibcBacktraceGetBufferSize_nid_postfix(int, int*, std::size_t*) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceLibcBacktraceSelf_nid_postfix(int, void*, std::size_t, int*) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +} diff --git a/core/libs/prx/libc/src/Math.cpp b/core/libs/prx/libc/src/Math.cpp index 1a835e2fe..4b4a78f73 100644 --- a/core/libs/prx/libc/src/Math.cpp +++ b/core/libs/prx/libc/src/Math.cpp @@ -465,6 +465,19 @@ short APS5_VABI _FDtest_nid_postfix(const float* value) { if (exponent == 0) return fraction != 0 ? Denormal : Zero; return Finite; } + +short APS5_VABI _Dtest_nid_postfix(const double* value) { + constexpr short Denormal = -2, Finite = -1, Zero = 0, Infinite = 1, NotANumber = 2; + if (value == nullptr) throw std::invalid_argument("_Dtest: null value"); + std::uint64_t bits; + std::memcpy(&bits, value, sizeof(bits)); + const auto exponent = bits & 0x7ff0000000000000u; + const auto fraction = bits & 0x000fffffffffffffu; + if (exponent == 0x7ff0000000000000u) return fraction != 0 ? NotANumber : Infinite; + if (exponent == 0) return fraction != 0 ? Denormal : Zero; + return Finite; +} + int APS5_VABI __isnanf_nid_postfix(float x) { return std::isnan(x) ? 1 : 0; } int APS5_VABI isnanf_nid_postfix(float x) { return __isnanf_nid_postfix(x); } int APS5_VABI isnan_nid_postfix(double x) { return __isnan_nid_postfix(x); } @@ -526,4 +539,13 @@ int APS5_VABI _Fltrounds_nid_postfix(void) { return SonyFltrounds(); } +union SonyDconst { + unsigned short _Word[8]; + float _Float; + double _Double; + long double _Long_double; +}; + +static_assert(std::endian::native == std::endian::little); +SonyDconst _Inf_nid_postfix {{0, 0, 0, 0x7ff0, 0, 0, 0, 0}}; } diff --git a/core/libs/prx/libc/src/RuntimeSupport.cpp b/core/libs/prx/libc/src/RuntimeSupport.cpp index 56a0f8d49..4b7ae591d 100644 --- a/core/libs/prx/libc/src/RuntimeSupport.cpp +++ b/core/libs/prx/libc/src/RuntimeSupport.cpp @@ -192,4 +192,8 @@ unsigned __int128 APS5_VABI __umodti3_nid_postfix(unsigned __int128 dividend, un return SonyUmodti3(dividend, divisor); } +void APS5_VABI _ZNSt8ios_base7_AddstdEPS__nid_postfix(void*) { + NotImplemented_nid_no_patch(__func__); +} + } diff --git a/core/libs/prx/libc/src/Threads.cpp b/core/libs/prx/libc/src/Threads.cpp index e74bcb5f9..b1d85f9fa 100644 --- a/core/libs/prx/libc/src/Threads.cpp +++ b/core/libs/prx/libc/src/Threads.cpp @@ -294,4 +294,10 @@ int APS5_VABI _Thrd_sleep_nid_postfix(const Xtime* target, Xtime*) { } } + +int APS5_VABI _Cnd_signal_nid_postfix(void** handle) { + return SignalCondition_nid_no_patch(handle); } + +} + diff --git a/core/libs/prx/libc/src/exception/TypeInfo.cpp b/core/libs/prx/libc/src/exception/TypeInfo.cpp index c745eda72..49ba0e47e 100644 --- a/core/libs/prx/libc/src/exception/TypeInfo.cpp +++ b/core/libs/prx/libc/src/exception/TypeInfo.cpp @@ -13,6 +13,8 @@ void APS5_VABI DeleteTypeInfo(std::type_info*) { } bool APS5_VABI IsPointerTypeInfo(const std::type_info*) { return false; } +bool APS5_VABI IsPointerTypeInfoTrue(const std::type_info*) { return true; } +bool APS5_VABI IsFunctionTypeInfoTrue(const std::type_info*) { return true; } bool APS5_VABI CatchTypeInfo(const std::type_info* self, const std::type_info* thrown, void** object, unsigned) { if (!self || !thrown || !object) throw std::invalid_argument("invalid RTTI catch arguments"); @@ -65,4 +67,19 @@ extern "C" { LibcException::TypeInfoVtable _ZTVN10__cxxabiv117__class_type_infoE_nid_postfix {0, &typeid(__cxxabiv1::__class_type_info)}; LibcException::TypeInfoVtable _ZTVN10__cxxabiv120__si_class_type_infoE_nid_postfix {0, &typeid(__cxxabiv1::__si_class_type_info)}; LibcException::TypeInfoVtable _ZTVN10__cxxabiv121__vmi_class_type_infoE_nid_postfix {0, &typeid(__cxxabiv1::__vmi_class_type_info)}; +LibcException::TypeInfoVtable _ZTVN10__cxxabiv119__pointer_type_infoE_nid_postfix { + 0, + &typeid(__cxxabiv1::__pointer_type_info), + LibcException::DestroyTypeInfo, + LibcException::DeleteTypeInfo, + LibcException::IsPointerTypeInfoTrue +}; +LibcException::TypeInfoVtable _ZTVN10__cxxabiv120__function_type_infoE_nid_postfix { + 0, + &typeid(__cxxabiv1::__function_type_info), + LibcException::DestroyTypeInfo, + LibcException::DeleteTypeInfo, + LibcException::IsPointerTypeInfo, + LibcException::IsFunctionTypeInfoTrue +}; } diff --git a/core/libs/tests/GuestLibcThreads.cpp b/core/libs/tests/GuestLibcThreads.cpp index c0516c685..0aabfe21c 100644 --- a/core/libs/tests/GuestLibcThreads.cpp +++ b/core/libs/tests/GuestLibcThreads.cpp @@ -16,6 +16,7 @@ int APS5_VABI _Cnd_init_nid_postfix(void** handle); void APS5_VABI _Cnd_destroy_nid_postfix(void** handle); int APS5_VABI _Cnd_wait_nid_postfix(void** condition, void** mutex); int APS5_VABI _Cnd_broadcast_nid_postfix(void** handle); +int APS5_VABI _Cnd_signal_nid_postfix(void** handle); } static void Require(bool value) { if (!value) std::abort(); } @@ -175,9 +176,17 @@ static void ConditionBroadcast() { _Mtx_destroy_nid_postfix(&mutex); } +static void ConditionSignal() { + void* condition = nullptr; + Require(_Cnd_init_nid_postfix(&condition) == success); + Require(_Cnd_signal_nid_postfix(&condition) == success); + _Cnd_destroy_nid_postfix(&condition); +} + int main() { MutexBasics(); RecursiveMutex(); MutualExclusion(); ConditionBroadcast(); + ConditionSignal(); } diff --git a/core/libs/tests/GuestMath.cpp b/core/libs/tests/GuestMath.cpp index cad91f320..34d56fcca 100644 --- a/core/libs/tests/GuestMath.cpp +++ b/core/libs/tests/GuestMath.cpp @@ -47,6 +47,7 @@ struct LibcFloatConstant { std::uint32_t bits[4]; }; extern LibcFloatConstant _FInf_nid_postfix; extern LibcFloatConstant _FNan_nid_postfix; short APS5_VABI _FDtest_nid_postfix(const float*); +short APS5_VABI _Dtest_nid_postfix(const double*); int APS5_VABI __fpclassifyf_nid_postfix(float); int APS5_VABI isnanf_nid_postfix(float); int APS5_VABI __signbitl_nid_postfix(long double); @@ -696,6 +697,24 @@ static void CheckFloatMathExports() { Require(isinf_nid_postfix(nan) == 0 && isinf_nid_postfix(1.0) == 0); } +static void CheckDoubleClassification() { + const struct { std::uint64_t bits; short code; } cases[] = { + {0x0000000000000000ull, 0}, {0x8000000000000000ull, 0}, + {0x0000000000000001ull, -2}, {0x800fffffffffffffull, -2}, + {0x0010000000000000ull, -1}, {0xbff0000000000000ull, -1}, {0x7fefffffffffffffull, -1}, + {0x7ff0000000000000ull, 1}, {0xfff0000000000000ull, 1}, + {0x7ff0000000000001ull, 2}, {0x7ff8000000000000ull, 2}, {0xfff8000000000000ull, 2}, + }; + for (const auto& test : cases) { + double value; + std::memcpy(&value, &test.bits, sizeof(value)); + if (_Dtest_nid_postfix(&value) != test.code) { + std::fprintf(stderr, "Guest _Dtest failed for %016llx\n", static_cast(test.bits)); + std::abort(); + } + } +} + static void CheckDoubleRounding(double value, std::int64_t expected, bool invalid) { for (const bool useLlround : {false, true}) { std::feclearexcept(FE_ALL_EXCEPT); @@ -956,4 +975,5 @@ int main() { CheckAtanhf(); CheckHypotSpecialValue(); CheckFmodErrno(); + CheckDoubleClassification(); } diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index 124436d2a..740c9c59c 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -66,3 +66,4 @@ - [flock](../../../core/libs/prx/libc/src/FileLock.cpp) (libkernel) on Windows shares one lock state between duplicated descriptors, following FreeBSD 11.4's [flock(2)](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/sys/flock.2). Independent opens have separate states; closing a descriptor or reopening a stream drops only its reference. Lock transitions on one open file description are serialized. The host's [LockFileEx](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex) locks also restrict ordinary reads and writes through other handles, unlike FreeBSD's advisory locks, and do not restrict mapped views. Console behavior, signal interruption and concurrent closure of a descriptor while its lock call is pending are unverified. - [std::_Throw_Cpp_error](../../../core/libs/prx/libc/src/exception/StandardExceptions.cpp) (libSceAmpr) follows Dinkumware's `thread0.cpp` as shipped with [Visual C++ 2013](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/thread0.cpp) and in [microsoft/STL](https://github.com/microsoft/STL/blob/da52dc0fcadbd83690cfd1ed32408d2847439493/stl/src/thread0.cpp), without console verification. `_Cpp_error` codes 0 to 6 throw a guest `std::system_error` whose `error_code` holds the FreeBSD errno of the `errc` value (EBUSY 16, EINVAL 22, ESRCH 3, ENOMEM 12, EPERM 1, EDEADLK 11, EAGAIN 35) and the generic category, and whose `what()` is the table message, `": "` and the generic category's message for that errno, selected by the first matching errno entry in VC12's [`syserror.cpp`](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/syserror.cpp); another code throws `std::out_of_range` on the host instead of reading past the table. The guest `system_error` is taken to be [VC12's](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/system_error): the 16-byte `runtime_error` followed by `error_code {int, const error_category*}`, 32 bytes, so its copy constructor and assignment copy the code too. The generic category is a static libc object with VC12's `error_category` virtual order (destructors, `name`, `message`, `default_error_condition`, the two `equivalent`), not the console's `_Error_objects::_Generic_object`, and the new `generic_category()` export returns that object; a title's inline category object in a bundled libc can still have a different identity. Its `message` and direct message export construct the existing [GuestLocale::String](../../../core/libs/prx/libc/include/GuestLocale.hpp) model: a reserved eight-byte word, 16-byte inline buffer, length and capacity, with guest `operator new` storage above 15 bytes. This 40-byte string layout and virtual return ABI have not been verified against a console library. The deleting destructor still throws. `_Syserror_map` covers the 72 distinct Dinkumware `errc` values present in FreeBSD 11.4 [errno.h](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/sys/errno.h), returning null for other values; category messages use `unknown error` for those. First-match aliases are `EAGAIN`/`EWOULDBLOCK` -> `operation would block` and `ENOTSUP`/`EOPNOTSUPP` -> `not supported`. STREAMS-only errno assignments absent from that header (`ENODATA`, `ENOSR`, `ENOSTR`, `ETIME`) are not inferred - [Call tracing](../../../core/libs/prx/libc/src/specifics/windows/CallTrace.cpp) (`--trace-imports`, `APS5_TRACE_CALLS`) exists on Windows only and records the entry of a call, not its arguments or its result. One process-wide ring keeps the last 65536 calls, so a thread that calls often pushes the others out. Calls are also counted per traced target since start-up; an import bound to a target already traced under another name is counted under the first name. The thunk saves the integer argument registers and `xmm0` to `xmm7`; arguments in wider vector registers pass only as long as the recording code stays free of AVX instructions, which is not tested +- [_Dtest](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows Dinkumware double classification matching _FDtest: finite values return -1, subnormals -2, zeros 0, infinities 1, and NaNs 2.