diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20ba79446b..7a6bab95cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,10 +29,10 @@ jobs: include: - platform: linux runner: ubuntu-latest - timeout: 30 + timeout: 60 - platform: windows runner: windows-latest - timeout: 45 + timeout: 60 runs-on: ${{ matrix.runner }} timeout-minutes: ${{ matrix.timeout }} defaults: diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 914ad341bf..94c7c2631c 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -94,6 +94,7 @@ jobs: env: ASAN_OPTIONS: detect_leaks=0 UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 + APS5_SKIP_TIMING_TESTS: 1 run: ctest --output-on-failure --timeout 300 --parallel 4 --output-log ctest.log --output-junit ctest.xml || true - name: Summarize the sanitizer findings if: ${{ !cancelled() && steps.configure.outcome == 'success' }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 0559567c61..dcb148218d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -288,6 +288,8 @@ if(BUILD_TESTING) ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_sanitizer_report.py) add_test(NAME select_tests COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_select_tests.py) + add_test(NAME tools_self_inspect COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/tests/test_inspect_self.py) + set_tests_properties(tools_self_inspect PROPERTIES TIMEOUT 20) add_test(NAME check_main_sync COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_main_sync.py CheckMainSyncTests) set_tests_properties(check_main_sync PROPERTIES TIMEOUT 30) @@ -474,6 +476,8 @@ if(BUILD_TESTING) set_tests_properties(check_compile_${test_case} PROPERTIES TIMEOUT 30) endif() endforeach() + add_test(NAME tools_seed_check COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/tests/test_check_configure_seed.py) + set_tests_properties(tools_seed_check PROPERTIES TIMEOUT 20) add_test(NAME performance_compare COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_performance_compare.py) set_tests_properties(performance_compare PROPERTIES TIMEOUT 20) diff --git a/core/libs/prx/libSceAgcDriver/tests/GuardedMemoryLoads.cpp b/core/libs/prx/libSceAgcDriver/tests/GuardedMemoryLoads.cpp index 9cd66b426c..5a1996ddf4 100644 --- a/core/libs/prx/libSceAgcDriver/tests/GuardedMemoryLoads.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/GuardedMemoryLoads.cpp @@ -32,7 +32,7 @@ struct LoadCase { std::uint32_t components = 1; }; -std::vector emitLoad(const LoadCase& test, bool active, std::uint32_t offset, std::uint32_t records, bool robust, bool exact = false, bool valid = true) { +std::vector emitLoad(const LoadCase& test, bool active, std::uint32_t offset, std::uint32_t records, bool robust, bool exact = false, bool valid = true, bool coherent = true) { IrProgram program; program.SetWaveSize(32); program.Resources().stage = IrShaderStage::Compute; @@ -70,7 +70,7 @@ std::vector emitLoad(const LoadCase& test, bool active, std::uint auto& handle = emit(IrOpcode::GetScratchResource, IrType::AddressResource, {}); emit(test.opcode, test.type, {&handle, &address, &zero, &enabled}); } else if (test.kind == ResourceKind::Buffer) { - program.Resources().memoryInfo.front().coherent = true; + program.Resources().memoryInfo.front().coherent = coherent; program.Info().buffers.emplace_back().read = true; program.Metadata().bindings.descriptors.push_back({DescriptorBindingKind::Buffers, {0}}); program.Metadata().bindings.pushDataStartDword = 0; @@ -386,6 +386,23 @@ int main() { const auto plain = emitLoad(test, true, 8, 64, false, true); require(AccessTrace(plain, 0).Run() == std::vector({2}), "exact request without robustBufferAccess2 changed the plain variant"); }); + run("volatile robust buffer omits repeated zero selection", [] { + const LoadCase test{ResourceKind::Buffer, IrOpcode::LoadBufferU32, IrType::U32}; + const auto selections = [](const std::vector& module) { + unsigned count = 0; + for (std::size_t offset = 5; offset < module.size(); offset += module[offset] >> 16u) { + count += (module[offset] & 0xffffu) == spv::OpSelect; + } + return count; + }; + for (const bool robust : {false, true}) { + for (const bool exact : {false, true}) { + const auto plain = emitLoad(test, true, 8, 64, robust, exact, true, false); + const auto coherent = emitLoad(test, true, 8, 64, robust, exact); + require(selections(plain) == selections(coherent) + (robust && !exact ? 1u : 0u), "only volatile guarded robust loads may omit the repeated zero selection"); + } + } + }); run("private scratch selection", [] { const LoadCase test{ResourceKind::Scratch, IrOpcode::LoadAddressU32, IrType::U32}; for (const bool robust : {false, true}) { diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/BufferDescriptorRuntime.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/BufferDescriptorRuntime.cpp index 15aa7c77a2..e847bc1c62 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/BufferDescriptorRuntime.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/BufferDescriptorRuntime.cpp @@ -57,13 +57,17 @@ ShaderRecompiler::RecompileRequest Request(std::span userDa }; } -void Run(AgcDriver::VulkanDevice& device, bool registrationModules) { +void Run(AgcDriver::VulkanDevice& device, bool registrationModules, bool coherent = false) { + alignas(256) auto code = Code; + if (coherent) code[1] |= 0x4000u; const std::string mode = registrationModules ? "registration modules" : "specialized modules"; - const std::array memory{{{reinterpret_cast(Code.data()), std::as_bytes(std::span(Code))}}}; + const std::array memory{{{reinterpret_cast(code.data()), std::as_bytes(std::span(code))}}}; std::array userData{}; auto target = device.ComputeTarget(Lanes); target.registrationModules = registrationModules; - const auto request = Request(userData, target, memory); + auto request = Request(userData, target, memory); + request.shader.code = code; + request.shader.codeAddress = reinterpret_cast(code.data()); const auto handle = ShaderRecompiler::PrepareShader(request); const std::array cases{{ {"aligned", 0, 128, true}, @@ -258,6 +262,8 @@ int main() { for (std::uint32_t index = 0; index < InputBytes; ++index) Input[index] = static_cast(index * 41u + 3u + (index >> 3u)); Run(*device, false); Run(*device, true); + Run(*device, false, true); + Run(*device, true, true); ScalarReads(*device); DisabledDescriptors(*device); std::puts("buffer descriptor runtime tests passed"); diff --git a/core/libs/prx/libSceFontGs/Export.cpp b/core/libs/prx/libSceFontGs/Export.cpp index d81f4a5e32..c625cc94a1 100644 --- a/core/libs/prx/libSceFontGs/Export.cpp +++ b/core/libs/prx/libSceFontGs/Export.cpp @@ -1,3 +1,4 @@ +#include "prx/libSceFontGs/include/InventoryExports.hpp" #include #include #include "SceTypes.hpp" @@ -7,4 +8,32 @@ extern "C" { APS5_DUMMY_FUN +int APS5_VABI sceFontGraphicsGnmSurfaceInit(void) { + NotImplemented_nid_no_patch(__func__); +} + +int APS5_VABI sceFontSelectGraphicsGnm(void) { + NotImplemented_nid_no_patch(__func__); +} + +int APS5_VABI sceFontGsUnknown_DI4LrTUrD04_nid_no_patch() { + NotImplemented_nid_no_patch(__func__); +} +APS5_EXPORT("DI4LrTUrD04", sceFontGsUnknown_DI4LrTUrD04_nid_no_patch); + +int APS5_VABI sceFontGsUnknown_NtbMKwRg6qg_nid_no_patch() { + NotImplemented_nid_no_patch(__func__); +} +APS5_EXPORT("NtbMKwRg6qg", sceFontGsUnknown_NtbMKwRg6qg_nid_no_patch); + +int APS5_VABI sceFontGsUnknown_UsBLoyXsQZs_nid_no_patch() { + NotImplemented_nid_no_patch(__func__); +} +APS5_EXPORT("UsBLoyXsQZs", sceFontGsUnknown_UsBLoyXsQZs_nid_no_patch); + +int APS5_VABI sceFontGsUnknown_uksHKVEsxu4_nid_no_patch() { + NotImplemented_nid_no_patch(__func__); +} +APS5_EXPORT("uksHKVEsxu4", sceFontGsUnknown_uksHKVEsxu4_nid_no_patch); + } diff --git a/core/libs/prx/libSceFontGs/include/InventoryExports.hpp b/core/libs/prx/libSceFontGs/include/InventoryExports.hpp new file mode 100644 index 0000000000..c3ae4d3351 --- /dev/null +++ b/core/libs/prx/libSceFontGs/include/InventoryExports.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "SceTypes.hpp" + +extern "C" { + +int APS5_VABI sceFontGraphicsGnmSurfaceInit(void); +int APS5_VABI sceFontSelectGraphicsGnm(void); + +} diff --git a/core/libs/prx/libSceFontGs/libSceFontGs.hpp b/core/libs/prx/libSceFontGs/libSceFontGs.hpp index 6f70f09bee..3ec3b9d1c6 100644 --- a/core/libs/prx/libSceFontGs/libSceFontGs.hpp +++ b/core/libs/prx/libSceFontGs/libSceFontGs.hpp @@ -1 +1,3 @@ #pragma once + +#include "prx/libSceFontGs/include/InventoryExports.hpp" diff --git a/core/libs/prx/libSceLibreSsl3/Export.cpp b/core/libs/prx/libSceLibreSsl3/Export.cpp index 0f7cb0e894..a299a4c7a7 100644 --- a/core/libs/prx/libSceLibreSsl3/Export.cpp +++ b/core/libs/prx/libSceLibreSsl3/Export.cpp @@ -266,11 +266,6 @@ int APS5_VABI RSA_padding_add_PKCS1_PSS_mgf1_nid_postfix(void) { return 0; } -int APS5_VABI RSA_up_ref_nid_postfix(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI SSL_CTX_add0_chain_cert_nid_postfix(void) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/prx/libSceLibreSsl3/include/Api.hpp b/core/libs/prx/libSceLibreSsl3/include/Api.hpp index 771580b4fb..2bb0ee0930 100644 --- a/core/libs/prx/libSceLibreSsl3/include/Api.hpp +++ b/core/libs/prx/libSceLibreSsl3/include/Api.hpp @@ -59,7 +59,7 @@ int APS5_VABI PKCS8_PRIV_KEY_INFO_free_nid_postfix(void); int APS5_VABI RSA_check_key_nid_postfix(void); int APS5_VABI RSA_get0_key_nid_postfix(void); int APS5_VABI RSA_padding_add_PKCS1_PSS_mgf1_nid_postfix(void); -int APS5_VABI RSA_up_ref_nid_postfix(void); +int APS5_VABI RSA_up_ref_nid_postfix(LibreSsl3Rsa* rsa); int APS5_VABI SSL_CTX_add0_chain_cert_nid_postfix(void); int APS5_VABI SSL_CTX_clear_chain_certs_nid_postfix(void); int APS5_VABI SSL_CTX_set_max_proto_version_nid_postfix(void); diff --git a/core/libs/prx/libSceLibreSsl3/include/Key.hpp b/core/libs/prx/libSceLibreSsl3/include/Key.hpp index bc0a301686..4dac85fd57 100644 --- a/core/libs/prx/libSceLibreSsl3/include/Key.hpp +++ b/core/libs/prx/libSceLibreSsl3/include/Key.hpp @@ -24,7 +24,32 @@ struct LibreSsl3EcKey { void* extraData; }; +struct LibreSsl3Rsa { + std::int32_t pad; + std::int64_t version; + const void* method; + void* n; + void* e; + void* d; + void* p; + void* q; + void* dmp1; + void* dmq1; + void* iqmp; + void* pss; + void* extraData; + std::int32_t references; + std::int32_t flags; + void* methodModN; + void* methodModP; + void* methodModQ; + void* blinding; + void* mtBlinding; +}; + static_assert(sizeof(LibreSsl3EvpPkey) == 32); static_assert(offsetof(LibreSsl3EvpPkey, references) == 4); static_assert(sizeof(LibreSsl3EcKey) == 64); static_assert(offsetof(LibreSsl3EcKey, references) == 48); +static_assert(sizeof(LibreSsl3Rsa) == 152); +static_assert(offsetof(LibreSsl3Rsa, references) == 104); diff --git a/core/libs/prx/libSceLibreSsl3/src/Key.cpp b/core/libs/prx/libSceLibreSsl3/src/Key.cpp index d5c4b7a856..dfca8410ad 100644 --- a/core/libs/prx/libSceLibreSsl3/src/Key.cpp +++ b/core/libs/prx/libSceLibreSsl3/src/Key.cpp @@ -35,4 +35,10 @@ int APS5_VABI EC_KEY_up_ref_nid_postfix(LibreSsl3EcKey* key) { return Retain_nid_no_patch(key->references); } +int APS5_VABI RSA_up_ref_nid_postfix(LibreSsl3Rsa* rsa) { + if (!rsa) + throw std::invalid_argument("RSA_up_ref requires a key"); + return Retain_nid_no_patch(rsa->references); +} + } diff --git a/core/libs/prx/libSceNpCppWebApi/CMakeLists.txt b/core/libs/prx/libSceNpCppWebApi/CMakeLists.txt index d14676369f..c6c7e4e41d 100644 --- a/core/libs/prx/libSceNpCppWebApi/CMakeLists.txt +++ b/core/libs/prx/libSceNpCppWebApi/CMakeLists.txt @@ -26,6 +26,15 @@ if(BUILD_TESTING) target_link_libraries(guest_np_cpp_web_api_string_tests PRIVATE libSceNpCppWebApi libc) configure_windows_unwind(guest_np_cpp_web_api_string_tests) add_test(NAME guest_np_cpp_web_api_string COMMAND guest_np_cpp_web_api_string_tests) + add_test_executable(guest_np_cpp_web_api_title_cloud_storage_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpCppWebApiTitleCloudStorage.cpp) + target_include_directories(guest_np_cpp_web_api_title_cloud_storage_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_np_cpp_web_api_title_cloud_storage_tests PRIVATE libSceNpCppWebApi libc) + configure_windows_unwind(guest_np_cpp_web_api_title_cloud_storage_tests) + add_test(NAME guest_np_cpp_web_api_title_cloud_storage COMMAND guest_np_cpp_web_api_title_cloud_storage_tests) + if(WIN32) + set_tests_properties(guest_np_cpp_web_api_title_cloud_storage + PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") + endif() add_test(NAME guest_np_cpp_web_api_alias_append COMMAND guest_np_cpp_web_api_string_tests alias-append) add_test(NAME guest_np_cpp_web_api_alias_string_vector COMMAND guest_np_cpp_web_api_string_tests alias-vector) add_test(NAME guest_np_cpp_web_api_alias_intrusive_vector COMMAND guest_np_cpp_web_api_intrusive_ptr_tests alias-vector) diff --git a/core/libs/prx/libSceNpCppWebApi/Export.cpp b/core/libs/prx/libSceNpCppWebApi/Export.cpp index b575c4ee4f..cb18186259 100644 --- a/core/libs/prx/libSceNpCppWebApi/Export.cpp +++ b/core/libs/prx/libSceNpCppWebApi/Export.cpp @@ -791,6 +791,175 @@ int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi23ParameterToDow return 0; } +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V129SetDataInfoRequestBodyFactory6createEPNS1_6Common10LibContextEPvmPNS5_12IntrusivePtrINS3_22SetDataInfoRequestBodyEEE(void* self) { + (void)self; + return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi10uploadDataEiRKNS4_21ParameterToUploadDataERNS1_6Common19UpStreamTransactionINS8_12IntrusivePtrINS3_22UploadDataResponseBodyEEENSA_INS4_25UploadDataResponseHeadersEEEEE(void* self) { + (void)self; + return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi10uploadDataEiRKNS4_42ParameterWithBinaryRequestBodyToUploadDataERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_22UploadDataResponseBodyEEENSA_INS4_25UploadDataResponseHeadersEEEEE(void* self) { + (void)self; + return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi11setDataInfoEiRKNS4_22ParameterToSetDataInfoERNS1_6Common11TransactionINS8_15DefaultResponseENS8_12IntrusivePtrINS4_26SetDataInfoResponseHeadersEEEEE(void* self) { + (void)self; + return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi12downloadDataEiRKNS4_23ParameterToDownloadDataERNS1_6Common11TransactionINS8_12IntrusivePtrINS8_6BinaryEEENSA_INS4_27DownloadDataResponseHeadersEEEEE(void* self) { + (void)self; + return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData10initializeEPNS1_6Common10LibContextEPKci(void* self, const std::uint64_t* context, const char* text, int value) { + (void)value; + return InitializeParameter(self, context, text != nullptr); +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData21setxPsnNpServiceLabelEj(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData22setxPsnAtomicOperationENS5_19XPsnAtomicOperationE(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData9terminateEv(void* self) { + return TerminateParameter(self); +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadDataC1Ev(void* self) { + ConstructParameter(self); + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadDataD1Ev(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21deleteMultiDataByUserEiRKNS4_32ParameterToDeleteMultiDataByUserERNS1_6Common11TransactionINS8_15DefaultResponseENS8_12IntrusivePtrINS8_18ResponseHeaderBaseEEEEE(void* self) { + (void)self; + return SCE_NP_WEBAPI_ERROR_NOT_SIGNED_IN; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo10initializeEPNS1_6Common10LibContextEPKciNS6_12IntrusivePtrINS3_22SetDataInfoRequestBodyEEE(void* self, const std::uint64_t* context, const char* text, int value, const IntrusivePtr* body) { + (void)value; + return InitializeParameterWithBody(self, context, text != nullptr, body); +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo22setxPsnAtomicOperationENS5_19XPsnAtomicOperationE(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo24setxPsnAtomicOperationIdEPKc(void* self, const char* text) { + return SetParameterText(self, text); +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo9terminateEv(void* self) { + return TerminateParameter(self); +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfoC1Ev(void* self) { + ConstructParameter(self); + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfoD1Ev(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi23ParameterToDownloadData17setnpServiceLabelEj(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser10initializeEPNS1_6Common10LibContextEPKcSA_(void* self, const std::uint64_t* context, const char* first, const char* second) { + return InitializeParameter(self, context, first != nullptr && second != nullptr); +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser17setnpServiceLabelEj(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser9terminateEv(void* self) { + return TerminateParameter(self); +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUserC1Ev(void* self) { + ConstructParameter(self); + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUserD1Ev(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi37ParameterToGetMultiDataStatusesByUser17setnpServiceLabelEj(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData10initializeEPNS1_6Common10LibContextEPKci(void* self, const std::uint64_t* context, const char* text, int value) { + (void)value; + return InitializeParameter(self, context, text != nullptr); +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData14setRequestBodyEPKhm(void* self, const void* data, std::size_t size) { + if (static_cast(self)->implementation == nullptr) return ParameterNotInitialized; + if (data == nullptr && size != 0) return ParameterInvalidArgument; + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData9terminateEv(void* self) { + return TerminateParameter(self); +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadDataC1Ev(void* self) { + ConstructParameter(self); + return 0; +} + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadDataD1Ev(void* self) { + (void)self; + return 0; +} + +int APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V110DataStatus22getLastUpdatedDateTimeEv(void* self) { + (void)self; + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V110DataStatus7getInfoEv(void* self) { + (void)self; + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi25UploadDataResponseHeaders24getXPsnAtomicOperationIdEv(void* self) { + (void)self; + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi25UploadDataResponseHeaders24hasXPsnAtomicOperationIdEv(void* self) { + (void)self; + NotImplemented_nid_no_patch(__func__); + return 0; +} + int APS5_VABI _ZN3sce2Np9CppWebApi6Common10InitParamsC1Ev(void* self) { (void)self; return 0; diff --git a/core/libs/prx/libSceNpCppWebApi/include/Api.hpp b/core/libs/prx/libSceNpCppWebApi/include/Api.hpp index d940f9a416..074350a1bb 100644 --- a/core/libs/prx/libSceNpCppWebApi/include/Api.hpp +++ b/core/libs/prx/libSceNpCppWebApi/include/Api.hpp @@ -469,6 +469,41 @@ int APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V110DataStatus9getSlotIdE void* APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V132GetMultiDataStatusesResponseBody17getDataStatusListEv(void* self); std::uint32_t APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V132GetMultiDataStatusesResponseBody23getTotalDataStatusCountEv(const void* self); std::uint64_t APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V15Owner12getAccountIdEv(const void* self); + +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V129SetDataInfoRequestBodyFactory6createEPNS1_6Common10LibContextEPvmPNS5_12IntrusivePtrINS3_22SetDataInfoRequestBodyEEE(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi10uploadDataEiRKNS4_21ParameterToUploadDataERNS1_6Common19UpStreamTransactionINS8_12IntrusivePtrINS3_22UploadDataResponseBodyEEENSA_INS4_25UploadDataResponseHeadersEEEEE(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi10uploadDataEiRKNS4_42ParameterWithBinaryRequestBodyToUploadDataERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_22UploadDataResponseBodyEEENSA_INS4_25UploadDataResponseHeadersEEEEE(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi11setDataInfoEiRKNS4_22ParameterToSetDataInfoERNS1_6Common11TransactionINS8_15DefaultResponseENS8_12IntrusivePtrINS4_26SetDataInfoResponseHeadersEEEEE(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi12downloadDataEiRKNS4_23ParameterToDownloadDataERNS1_6Common11TransactionINS8_12IntrusivePtrINS8_6BinaryEEENSA_INS4_27DownloadDataResponseHeadersEEEEE(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData10initializeEPNS1_6Common10LibContextEPKci(void* self, const std::uint64_t* context, const char* text, int value); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData21setxPsnNpServiceLabelEj(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData22setxPsnAtomicOperationENS5_19XPsnAtomicOperationE(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData9terminateEv(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadDataC1Ev(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadDataD1Ev(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21deleteMultiDataByUserEiRKNS4_32ParameterToDeleteMultiDataByUserERNS1_6Common11TransactionINS8_15DefaultResponseENS8_12IntrusivePtrINS8_18ResponseHeaderBaseEEEEE(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo10initializeEPNS1_6Common10LibContextEPKciNS6_12IntrusivePtrINS3_22SetDataInfoRequestBodyEEE(void* self, const std::uint64_t* context, const char* text, int value, const IntrusivePtr* body); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo22setxPsnAtomicOperationENS5_19XPsnAtomicOperationE(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo24setxPsnAtomicOperationIdEPKc(void* self, const char* text); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo9terminateEv(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfoC1Ev(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfoD1Ev(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi23ParameterToDownloadData17setnpServiceLabelEj(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser10initializeEPNS1_6Common10LibContextEPKcSA_(void* self, const std::uint64_t* context, const char* first, const char* second); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser17setnpServiceLabelEj(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser9terminateEv(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUserC1Ev(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUserD1Ev(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi37ParameterToGetMultiDataStatusesByUser17setnpServiceLabelEj(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData10initializeEPNS1_6Common10LibContextEPKci(void* self, const std::uint64_t* context, const char* text, int value); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData14setRequestBodyEPKhm(void* self, const void* data, std::size_t size); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData9terminateEv(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadDataC1Ev(void* self); +int APS5_VABI _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadDataD1Ev(void* self); +int APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V110DataStatus22getLastUpdatedDateTimeEv(void* self); +int APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V110DataStatus7getInfoEv(void* self); +int APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi25UploadDataResponseHeaders24getXPsnAtomicOperationIdEv(void* self); +int APS5_VABI _ZNK3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi25UploadDataResponseHeaders24hasXPsnAtomicOperationIdEv(void* self); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi10getFriendsEiRKNS4_21ParameterToGetFriendsERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_18GetFriendsResponseEEENSA_INS8_18ResponseHeaderBaseEEEEE(); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFriends10initializeEPNS1_6Common10LibContextEPKc(void* self, const std::uint64_t* context, const char* accountId); int APS5_VABI _ZN3sce2Np9CppWebApi11UserProfile2V110FriendsApi21ParameterToGetFriends9terminateEv(void* self); diff --git a/core/libs/prx/libSceSystemService/CMakeLists.txt b/core/libs/prx/libSceSystemService/CMakeLists.txt index 2831f4653a..140da9cc88 100644 --- a/core/libs/prx/libSceSystemService/CMakeLists.txt +++ b/core/libs/prx/libSceSystemService/CMakeLists.txt @@ -131,6 +131,15 @@ if(BUILD_TESTING) "PATH=path_list_prepend:$;PATH=path_list_prepend:$") endif() endforeach() + add_test_executable(guest_system_service_activation_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemServiceActivation.cpp) + target_include_directories(guest_system_service_activation_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_system_service_activation_tests PRIVATE libSceSystemService libc) + configure_windows_unwind(guest_system_service_activation_tests) + add_test(NAME guest_system_service_activation COMMAND guest_system_service_activation_tests) + if(WIN32) + set_tests_properties(guest_system_service_activation PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_system_kill_app_tests ${LIBS_INCLUDE_DIR}/tests/GuestSystemKillApp.cpp) target_include_directories(guest_system_kill_app_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_system_kill_app_tests PRIVATE libSceSystemService libc) diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index 21179c6118..e7a82e1f5a 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -2,10 +2,22 @@ #include "prx/libSceSystemService/SystemService.hpp" #include "prx/libc/include/General.hpp" +#include + namespace { constexpr int SystemServiceUnavailable = static_cast(0x80A10002); +constexpr int SystemServiceInvalidArgument = static_cast(0x80A10003); constexpr int SystemServiceRejected = static_cast(0x80A10005); +constexpr int ShellCoreInvalidArgument = static_cast(0x80E30005); + +constexpr std::int32_t ActivationTypeMpeg2 = 0; +constexpr std::int32_t ActivationTypeHevc = 3; +constexpr std::int32_t ActivationTypeHevcSoft = 4; +constexpr std::int32_t ActivationStatus = 2; +constexpr std::intptr_t ActivationHandleStep = 4; + +std::atomic nextActivationHandle{0x1000}; } @@ -542,17 +554,21 @@ int APS5_VABI sceShellCoreUtilActivateAbort(std::intptr_t handle) { } int APS5_VABI sceShellCoreUtilActivateGetStatus(std::intptr_t handle, std::int32_t* status, std::int32_t* result) { - NotImplemented_nid_no_patch(__func__); + if (status == nullptr || result == nullptr) return ShellCoreInvalidArgument; + *status = ActivationStatus; + *result = 0; return 0; } int APS5_VABI sceShellCoreUtilActivateInit(std::int32_t type, std::intptr_t* handle) { - NotImplemented_nid_no_patch(__func__); + if (handle == nullptr) return ShellCoreInvalidArgument; + *handle = nextActivationHandle.fetch_add(ActivationHandleStep); return 0; } -int APS5_VABI sceShellCoreUtilActivateIsActivated(std::intptr_t handle, bool* activated) { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceShellCoreUtilActivateIsActivated(std::int32_t type, bool* activated) { + if (activated == nullptr) return ShellCoreInvalidArgument; + *activated = true; return 0; } @@ -1567,103 +1583,101 @@ int APS5_VABI sceSystemServiceAcquireFb0() { } int APS5_VABI sceSystemServiceActivateHevc() { - NotImplemented_nid_no_patch(__func__); - return 0; + return sceShellCoreUtilActivateRecordActivation(); } int APS5_VABI sceSystemServiceActivateHevcAbort(std::uintptr_t handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateAbort(static_cast(handle)); } int APS5_VABI sceSystemServiceActivateHevcGetStatus(std::uintptr_t handle, std::uint32_t* status, std::int32_t* result) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0 || status == nullptr || result == nullptr) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateGetStatus(static_cast(handle), reinterpret_cast(status), result); } int APS5_VABI sceSystemServiceActivateHevcInit(std::uintptr_t* handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == nullptr) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateInit(ActivationTypeHevc, reinterpret_cast(handle)); } int APS5_VABI sceSystemServiceActivateHevcIsActivated(std::uint8_t* activated) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (activated == nullptr) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateIsActivated(ActivationTypeHevc, reinterpret_cast(activated)); } int APS5_VABI sceSystemServiceActivateHevcSoft() { - NotImplemented_nid_no_patch(__func__); - return 0; + return sceShellCoreUtilActivateRecordActivation(); } int APS5_VABI sceSystemServiceActivateHevcSoftAbort(std::uintptr_t handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateAbort(static_cast(handle)); } int APS5_VABI sceSystemServiceActivateHevcSoftGetStatus(std::uintptr_t handle, std::uint32_t* status, std::int32_t* result) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0 || status == nullptr || result == nullptr) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateGetStatus(static_cast(handle), reinterpret_cast(status), result); } int APS5_VABI sceSystemServiceActivateHevcSoftInit(std::uintptr_t* handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == nullptr) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateInit(ActivationTypeHevcSoft, reinterpret_cast(handle)); } int APS5_VABI sceSystemServiceActivateHevcSoftIsActivated(std::uint8_t* activated) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (activated == nullptr) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateIsActivated(ActivationTypeHevcSoft, reinterpret_cast(activated)); } int APS5_VABI sceSystemServiceActivateHevcSoftStart(std::uintptr_t handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateStartAsync(static_cast(handle)); } int APS5_VABI sceSystemServiceActivateHevcSoftTerm(std::uintptr_t handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateTerm(static_cast(handle)); } int APS5_VABI sceSystemServiceActivateHevcStart(std::uintptr_t handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateStartAsync(static_cast(handle)); } int APS5_VABI sceSystemServiceActivateHevcTerm(std::uintptr_t handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateTerm(static_cast(handle)); } int APS5_VABI sceSystemServiceActivateMpeg2Abort(std::uintptr_t handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateAbort(static_cast(handle)); } int APS5_VABI sceSystemServiceActivateMpeg2GetStatus(std::uintptr_t handle, std::uint32_t* status, std::int32_t* result) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0 || status == nullptr || result == nullptr) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateGetStatus(static_cast(handle), reinterpret_cast(status), result); } int APS5_VABI sceSystemServiceActivateMpeg2Init(std::uintptr_t* handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == nullptr) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateInit(ActivationTypeMpeg2, reinterpret_cast(handle)); } int APS5_VABI sceSystemServiceActivateMpeg2IsActivated(std::uint8_t* activated) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (activated == nullptr) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateIsActivated(ActivationTypeMpeg2, reinterpret_cast(activated)); } int APS5_VABI sceSystemServiceActivateMpeg2Start(std::uintptr_t handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateStartAsync(static_cast(handle)); } int APS5_VABI sceSystemServiceActivateMpeg2Term(std::uintptr_t handle) { - NotImplemented_nid_no_patch(__func__); - return 0; + if (handle == 0) return SystemServiceInvalidArgument; + return sceShellCoreUtilActivateTerm(static_cast(handle)); } int APS5_VABI sceSystemServiceAddLocalProcess(std::int32_t appId, const char* path, const char* const* argv, const SystemServiceLocalProcessParam* param) { diff --git a/core/libs/prx/libSceSystemService/include/SystemService.hpp b/core/libs/prx/libSceSystemService/include/SystemService.hpp index 6c0120e66b..a62f21fcc8 100644 --- a/core/libs/prx/libSceSystemService/include/SystemService.hpp +++ b/core/libs/prx/libSceSystemService/include/SystemService.hpp @@ -353,7 +353,7 @@ int APS5_VABI sceShellCoreUtilAcquireSharePlayCpuBudget(); int APS5_VABI sceShellCoreUtilActivateAbort(std::intptr_t handle); int APS5_VABI sceShellCoreUtilActivateGetStatus(std::intptr_t handle, std::int32_t* status, std::int32_t* result); int APS5_VABI sceShellCoreUtilActivateInit(std::int32_t type, std::intptr_t* handle); -int APS5_VABI sceShellCoreUtilActivateIsActivated(std::intptr_t handle, bool* activated); +int APS5_VABI sceShellCoreUtilActivateIsActivated(std::int32_t type, bool* activated); int APS5_VABI sceShellCoreUtilActivateRecordActivation(); int APS5_VABI sceShellCoreUtilActivateStart(std::intptr_t handle); int APS5_VABI sceShellCoreUtilActivateStartAsync(std::intptr_t handle); diff --git a/core/libs/prx/libkernel_sys/Export.cpp b/core/libs/prx/libkernel_sys/Export.cpp index a26fd1347f..64af771c0f 100644 --- a/core/libs/prx/libkernel_sys/Export.cpp +++ b/core/libs/prx/libkernel_sys/Export.cpp @@ -350,7 +350,7 @@ extern "C" { int APS5_VABI sceKernelGetCompiledSdkVersion(std::int32_t* version) { if (version == nullptr) return SCE_KERNEL_ERROR_EINVAL; static const auto* parameters = static_cast(ApplicationProcessParameters_nid_no_patch()); - std::memcpy(version, parameters + 0x10, sizeof(*version)); + if (!GuestCopyOut_nid_no_patch(version, parameters + 0x10, sizeof(*version))) return SCE_KERNEL_ERROR_EFAULT; return 0; } diff --git a/core/libs/tests/GuestKernelSysSdkVersion.cpp b/core/libs/tests/GuestKernelSysSdkVersion.cpp index dc20a51ac8..0dc451e8f9 100644 --- a/core/libs/tests/GuestKernelSysSdkVersion.cpp +++ b/core/libs/tests/GuestKernelSysSdkVersion.cpp @@ -34,6 +34,7 @@ int main() { Require(ProcessParametersFixture::Install(process.data(), process.size()), "process parameters were not installed"); Require(sceKernelGetCompiledSdkVersion(nullptr) == static_cast(0x80020016), "null output is not EINVAL"); + Require(sceKernelGetCompiledSdkVersion(reinterpret_cast(0x10)) == static_cast(0x8002000e), "unwritable output is not EFAULT"); std::array output{-1, 0x5a5a5a5a}; Require(sceKernelGetCompiledSdkVersion(output.data()) == 0, "query failed"); Require(output[0] == 0x07008001, "version is not the 32-bit field at offset 0x10 of the process parameters"); diff --git a/core/libs/tests/GuestLibreSsl3KeyReference.cpp b/core/libs/tests/GuestLibreSsl3KeyReference.cpp index ca69aceb4e..1f814a8d68 100644 --- a/core/libs/tests/GuestLibreSsl3KeyReference.cpp +++ b/core/libs/tests/GuestLibreSsl3KeyReference.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include static void Require(bool condition, const char* message) { @@ -33,6 +34,25 @@ static void CheckKey(TRetain retain, std::size_t referenceOffset) { Require(retain(&guarded.key) == (next > 1), "retain return value"); Require(std::memcmp(&guarded, expected.data(), sizeof(guarded)) == 0, "retain changed bytes outside reference count"); } + std::memset(&guarded, 0x3C, sizeof(guarded)); + guarded.key.references = std::numeric_limits::max(); + std::array unchanged; + std::memcpy(unchanged.data(), &guarded, sizeof(guarded)); + bool overflowThrown = false; + try { + retain(&guarded.key); + } catch (const std::overflow_error&) { + overflowThrown = true; + } + Require(overflowThrown, "retain at maximum count did not throw"); + Require(std::memcmp(&guarded, unchanged.data(), sizeof(guarded)) == 0, "overflowing retain changed bytes"); + bool nullThrown = false; + try { + retain(nullptr); + } catch (const std::invalid_argument&) { + nullThrown = true; + } + Require(nullThrown, "retain of null did not throw"); std::memset(&guarded, 0x5A, sizeof(guarded)); guarded.key.references = 1; std::array expected; @@ -64,5 +84,7 @@ int main(int argc, char** argv) { CheckKey(EVP_PKEY_up_ref_nid_postfix, 4); if (argc == 1 || std::strcmp(argv[1], "ec") == 0) CheckKey(EC_KEY_up_ref_nid_postfix, 48); + if (argc == 1 || std::strcmp(argv[1], "rsa") == 0) + CheckKey(RSA_up_ref_nid_postfix, 104); return 0; } diff --git a/core/libs/tests/GuestNpCppWebApiTitleCloudStorage.cpp b/core/libs/tests/GuestNpCppWebApiTitleCloudStorage.cpp new file mode 100644 index 0000000000..da9a621cb3 --- /dev/null +++ b/core/libs/tests/GuestNpCppWebApiTitleCloudStorage.cpp @@ -0,0 +1,191 @@ +#include "prx/libSceNpCppWebApi/include/Api.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include +#include +#include + +namespace { + +void Require(bool value, std::source_location at = std::source_location::current()) { + if (!value) { + std::fprintf(stderr, "requirement failed at %s:%u\n", at.file_name(), at.line()); + std::abort(); + } +} + +constexpr int kNotSignedIn = static_cast(0x80552907); +constexpr int kParameterNotInitialized = static_cast(0x80553502); +constexpr int kInvalidArgument = static_cast(0x80553503); +constexpr int kAlreadyInitialized = static_cast(0x8055350f); +constexpr int kTerminatedUninitialized = static_cast(0x80553510); +constexpr int kBodyMissing = static_cast(0x80553512); +constexpr std::size_t kParameterSize = 0x10; +constexpr std::size_t kGuardSize = 0x10; +constexpr unsigned char kFill = 0xA5; + +struct Storage { + alignas(8) unsigned char bytes[kParameterSize + kGuardSize]; +}; + +bool GuardIntact(const Storage& storage) { + for (std::size_t index = kParameterSize; index < sizeof(storage.bytes); ++index) { + if (storage.bytes[index] != kFill) return false; + } + return true; +} + +void APS5_VABI NoopDelete(void*) {} + +using SimpleFn = int (APS5_VABI*)(void*); +using TextFn = int (APS5_VABI*)(void*, const char*); +using DataFn = int (APS5_VABI*)(void*, const void*, std::size_t); + +const SimpleFn kSignedOut[] = { + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V129SetDataInfoRequestBodyFactory6createEPNS1_6Common10LibContextEPvmPNS5_12IntrusivePtrINS3_22SetDataInfoRequestBodyEEE, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi10uploadDataEiRKNS4_21ParameterToUploadDataERNS1_6Common19UpStreamTransactionINS8_12IntrusivePtrINS3_22UploadDataResponseBodyEEENSA_INS4_25UploadDataResponseHeadersEEEEE, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi10uploadDataEiRKNS4_42ParameterWithBinaryRequestBodyToUploadDataERNS1_6Common11TransactionINS8_12IntrusivePtrINS3_22UploadDataResponseBodyEEENSA_INS4_25UploadDataResponseHeadersEEEEE, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi11setDataInfoEiRKNS4_22ParameterToSetDataInfoERNS1_6Common11TransactionINS8_15DefaultResponseENS8_12IntrusivePtrINS4_26SetDataInfoResponseHeadersEEEEE, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi12downloadDataEiRKNS4_23ParameterToDownloadDataERNS1_6Common11TransactionINS8_12IntrusivePtrINS8_6BinaryEEENSA_INS4_27DownloadDataResponseHeadersEEEEE, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21deleteMultiDataByUserEiRKNS4_32ParameterToDeleteMultiDataByUserERNS1_6Common11TransactionINS8_15DefaultResponseENS8_12IntrusivePtrINS8_18ResponseHeaderBaseEEEEE, +}; +const SimpleFn kNumericSetters[] = { + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData21setxPsnNpServiceLabelEj, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData22setxPsnAtomicOperationENS5_19XPsnAtomicOperationE, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo22setxPsnAtomicOperationENS5_19XPsnAtomicOperationE, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi23ParameterToDownloadData17setnpServiceLabelEj, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser17setnpServiceLabelEj, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi37ParameterToGetMultiDataStatusesByUser17setnpServiceLabelEj, +}; +const TextFn kTextSetters[] = { + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo24setxPsnAtomicOperationIdEPKc, +}; +const DataFn kDataSetters[] = { + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData14setRequestBodyEPKhm, +}; +const SimpleFn kConstructors[] = { + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadDataC1Ev, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfoC1Ev, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUserC1Ev, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadDataC1Ev, +}; +const SimpleFn kDestructors[] = { + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadDataD1Ev, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfoD1Ev, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUserD1Ev, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadDataD1Ev, +}; +const SimpleFn kTerminators[] = { + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData9terminateEv, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo9terminateEv, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser9terminateEv, + _ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData9terminateEv, +}; + +bool Constructed(SimpleFn constructor) { + Storage storage; + std::memset(storage.bytes, kFill, sizeof(storage.bytes)); + if (constructor(storage.bytes) != 0) return false; + if (!GuardIntact(storage)) return false; + for (std::size_t index = 0; index < kParameterSize; ++index) { + if (storage.bytes[index] != 0) return false; + } + return true; +} + +void TestInitializers() { + const std::uint64_t uninitialized = 0; + std::uint64_t context = 1; + + { + Storage parameter; + std::memset(parameter.bytes, kFill, sizeof(parameter.bytes)); + Require(kConstructors[0](parameter.bytes) == 0 && GuardIntact(parameter)); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData10initializeEPNS1_6Common10LibContextEPKci(parameter.bytes, nullptr, "label", 0) == kInvalidArgument); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData10initializeEPNS1_6Common10LibContextEPKci(parameter.bytes, &uninitialized, "label", 0) == kParameterNotInitialized); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData10initializeEPNS1_6Common10LibContextEPKci(parameter.bytes, &context, nullptr, 0) == kInvalidArgument); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData10initializeEPNS1_6Common10LibContextEPKci(parameter.bytes, &context, "label", 0) == 0); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi21ParameterToUploadData10initializeEPNS1_6Common10LibContextEPKci(parameter.bytes, &context, "label", 0) == kAlreadyInitialized); + Require(kTerminators[0](parameter.bytes) == 0); + Require(kTerminators[0](parameter.bytes) == kTerminatedUninitialized); + Require(kDestructors[0](parameter.bytes) == 0 && GuardIntact(parameter)); + } + { + Storage parameter; + std::memset(parameter.bytes, kFill, sizeof(parameter.bytes)); + Require(kConstructors[1](parameter.bytes) == 0 && GuardIntact(parameter)); + IntrusivePtr missing{}; + IntrusivePtr body{parameter.bytes, NoopDelete, nullptr}; + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo10initializeEPNS1_6Common10LibContextEPKciNS6_12IntrusivePtrINS3_22SetDataInfoRequestBodyEEE(parameter.bytes, &context, "label", 0, &missing) == kBodyMissing); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo10initializeEPNS1_6Common10LibContextEPKciNS6_12IntrusivePtrINS3_22SetDataInfoRequestBodyEEE(parameter.bytes, &context, "label", 0, &body) == 0); + Require(kTerminators[1](parameter.bytes) == 0); + Require(kDestructors[1](parameter.bytes) == 0 && GuardIntact(parameter)); + } + { + Storage parameter; + std::memset(parameter.bytes, kFill, sizeof(parameter.bytes)); + Require(kConstructors[2](parameter.bytes) == 0 && GuardIntact(parameter)); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser10initializeEPNS1_6Common10LibContextEPKcSA_(parameter.bytes, &context, "slot", nullptr) == kInvalidArgument); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi32ParameterToDeleteMultiDataByUser10initializeEPNS1_6Common10LibContextEPKcSA_(parameter.bytes, &context, "slot", "me") == 0); + Require(kTerminators[2](parameter.bytes) == 0); + Require(kDestructors[2](parameter.bytes) == 0 && GuardIntact(parameter)); + } + { + Storage parameter; + std::memset(parameter.bytes, kFill, sizeof(parameter.bytes)); + Require(kConstructors[3](parameter.bytes) == 0 && GuardIntact(parameter)); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData10initializeEPNS1_6Common10LibContextEPKci(parameter.bytes, &context, nullptr, 0) == kInvalidArgument); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData10initializeEPNS1_6Common10LibContextEPKci(parameter.bytes, &context, "slot", 0) == 0); + Require(kTerminators[3](parameter.bytes) == 0); + Require(kDestructors[3](parameter.bytes) == 0 && GuardIntact(parameter)); + } +} + +void TestSetters() { + for (const auto setter : kNumericSetters) Require(setter(nullptr) == 0); + Storage parameter; + std::memset(parameter.bytes, kFill, sizeof(parameter.bytes)); + Require(kConstructors[1](parameter.bytes) == 0); + for (const auto setter : kTextSetters) Require(setter(parameter.bytes, "id") == kParameterNotInitialized); + std::uint64_t context = 1; + IntrusivePtr missing{}; + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo10initializeEPNS1_6Common10LibContextEPKciNS6_12IntrusivePtrINS3_22SetDataInfoRequestBodyEEE(parameter.bytes, &context, "label", 0, &missing) == kBodyMissing); + IntrusivePtr body{parameter.bytes, NoopDelete, nullptr}; + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi22ParameterToSetDataInfo10initializeEPNS1_6Common10LibContextEPKciNS6_12IntrusivePtrINS3_22SetDataInfoRequestBodyEEE(parameter.bytes, &context, "label", 0, &body) == 0); + Require(kTextSetters[0](parameter.bytes, nullptr) == kInvalidArgument); + Require(kTextSetters[0](parameter.bytes, "id") == 0); + Require(kTerminators[1](parameter.bytes) == 0); + + Storage binary; + std::memset(binary.bytes, kFill, sizeof(binary.bytes)); + Require(kConstructors[3](binary.bytes) == 0); + for (const auto setter : kDataSetters) Require(setter(binary.bytes, nullptr, 0) == kParameterNotInitialized); + Require(_ZN3sce2Np9CppWebApi17TitleCloudStorage2V17DataApi42ParameterWithBinaryRequestBodyToUploadData10initializeEPNS1_6Common10LibContextEPKci(binary.bytes, &context, "slot", 0) == 0); + Require(kDataSetters[0](binary.bytes, nullptr, 1) == kInvalidArgument); + const unsigned char data[] = {1, 2, 3}; + Require(kDataSetters[0](binary.bytes, data, sizeof(data)) == 0); + Require(kTerminators[3](binary.bytes) == 0); +} + +} + +int main() { + for (const auto fn : kSignedOut) Require(fn(nullptr) == kNotSignedIn); + for (const auto constructor : kConstructors) Require(Constructed(constructor)); + for (const auto destructor : kDestructors) { + Storage storage; + std::memset(storage.bytes, kFill, sizeof(storage.bytes)); + Require(destructor(storage.bytes) == 0); + Require(GuardIntact(storage)); + } + for (const auto terminator : kTerminators) { + Storage storage; + std::memset(storage.bytes, 0, sizeof(storage.bytes)); + Require(terminator(storage.bytes) == kTerminatedUninitialized); + } + TestInitializers(); + TestSetters(); + return 0; +} diff --git a/core/libs/tests/GuestSystemServiceActivation.cpp b/core/libs/tests/GuestSystemServiceActivation.cpp new file mode 100644 index 0000000000..3fc4f9762c --- /dev/null +++ b/core/libs/tests/GuestSystemServiceActivation.cpp @@ -0,0 +1,83 @@ +#include "prx/libSceSystemService/include/SystemService.hpp" +#include "SceTypes.hpp" +#include +#include +#include + +namespace { + +constexpr int invalidArgument = static_cast(0x80A10003u); +constexpr int shellInvalidArgument = static_cast(0x80E30005u); +constexpr std::uintptr_t untouchedHandle = 0x5A5A5A5A5A5A5A5Au; +constexpr std::uint32_t untouchedStatus = 0x5A5A5A5Au; +constexpr std::int32_t untouchedResult = 0x5A5A5A5A; +constexpr std::uint8_t untouchedFlag = 0x5A; + +void Require(bool value) { if (!value) std::abort(); } + +struct Codec { + int (APS5_VABI* init)(std::uintptr_t*); + int (APS5_VABI* isActivated)(std::uint8_t*); + int (APS5_VABI* start)(std::uintptr_t); + int (APS5_VABI* getStatus)(std::uintptr_t, std::uint32_t*, std::int32_t*); + int (APS5_VABI* abort)(std::uintptr_t); + int (APS5_VABI* term)(std::uintptr_t); +}; + +void TestShellCalls(std::intptr_t expectedHandle) { + Require(sceShellCoreUtilActivateInit(7, nullptr) == shellInvalidArgument); + std::intptr_t handle = 0; + Require(sceShellCoreUtilActivateInit(7, &handle) == 0 && handle == expectedHandle); + std::int32_t status = static_cast(untouchedStatus); + std::int32_t result = untouchedResult; + Require(sceShellCoreUtilActivateGetStatus(0, nullptr, &result) == shellInvalidArgument && result == untouchedResult); + Require(sceShellCoreUtilActivateGetStatus(0, &status, nullptr) == shellInvalidArgument && status == static_cast(untouchedStatus)); + Require(sceShellCoreUtilActivateGetStatus(0, &status, &result) == 0 && status == 2 && result == 0); + Require(sceShellCoreUtilActivateIsActivated(7, nullptr) == shellInvalidArgument); + bool activated = false; + Require(sceShellCoreUtilActivateIsActivated(-1, &activated) == 0 && activated); + for (std::intptr_t value : {std::intptr_t{0}, handle, std::intptr_t{-1}}) { + Require(sceShellCoreUtilActivateStart(value) == 0); + Require(sceShellCoreUtilActivateStartAsync(value) == 0); + Require(sceShellCoreUtilActivateAbort(value) == 0); + Require(sceShellCoreUtilActivateTerm(value) == 0); + } + Require(sceShellCoreUtilActivateRecordActivation() == 0); +} + +void TestCodec(const Codec& codec, std::uintptr_t expectedHandle) { + Require(codec.init(nullptr) == invalidArgument); + std::uintptr_t handle = untouchedHandle; + Require(codec.init(&handle) == 0 && handle == expectedHandle); + Require(codec.isActivated(nullptr) == invalidArgument); + std::uint8_t activated = untouchedFlag; + Require(codec.isActivated(&activated) == 0 && activated == 1); + std::uint32_t status = untouchedStatus; + std::int32_t result = untouchedResult; + Require(codec.getStatus(0, &status, &result) == invalidArgument); + Require(codec.getStatus(handle, nullptr, &result) == invalidArgument); + Require(codec.getStatus(handle, &status, nullptr) == invalidArgument); + Require(status == untouchedStatus && result == untouchedResult); + for (auto call : {codec.start, codec.abort, codec.term}) { + Require(call(0) == invalidArgument); + Require(call(handle) == 0); + Require(call(untouchedHandle) == 0); + } + Require(codec.getStatus(handle, &status, &result) == 0 && status == 2 && result == 0); + Require(codec.getStatus(untouchedHandle, &status, &result) == 0 && status == 2 && result == 0); +} + +} + +int main() { + TestCodec({sceSystemServiceActivateHevcInit, sceSystemServiceActivateHevcIsActivated, sceSystemServiceActivateHevcStart, + sceSystemServiceActivateHevcGetStatus, sceSystemServiceActivateHevcAbort, sceSystemServiceActivateHevcTerm}, 0x1000); + TestCodec({sceSystemServiceActivateHevcSoftInit, sceSystemServiceActivateHevcSoftIsActivated, sceSystemServiceActivateHevcSoftStart, + sceSystemServiceActivateHevcSoftGetStatus, sceSystemServiceActivateHevcSoftAbort, sceSystemServiceActivateHevcSoftTerm}, 0x1004); + TestCodec({sceSystemServiceActivateMpeg2Init, sceSystemServiceActivateMpeg2IsActivated, sceSystemServiceActivateMpeg2Start, + sceSystemServiceActivateMpeg2GetStatus, sceSystemServiceActivateMpeg2Abort, sceSystemServiceActivateMpeg2Term}, 0x1008); + TestShellCalls(0x100C); + Require(sceSystemServiceActivateHevc() == 0); + Require(sceSystemServiceActivateHevcSoft() == 0); + return 0; +} diff --git a/core/relinker/cli/src/CliArgs.cpp b/core/relinker/cli/src/CliArgs.cpp index 697a5de1aa..8a6f7b2aef 100644 --- a/core/relinker/cli/src/CliArgs.cpp +++ b/core/relinker/cli/src/CliArgs.cpp @@ -110,14 +110,7 @@ Args ParseArgs(int argc, char* argv[]) { if (args.windowsFixedBase) throw std::runtime_error("--windows-fixed-base cannot be repeated"); if (i + 1 >= argc || std::string(argv[i + 1]).empty() || std::string(argv[i + 1]).starts_with("--")) throw std::runtime_error("--windows-fixed-base requires an address"); - std::string_view value(argv[++i]); - const bool hexadecimal = value.starts_with("0x") || value.starts_with("0X"); - if (hexadecimal) value.remove_prefix(2); - std::uint64_t address = 0; - const auto parsed = std::from_chars(value.data(), value.data() + value.size(), address, hexadecimal ? 16 : 10); - if (parsed.ec != std::errc{} || parsed.ptr != value.data() + value.size() || address < 0x20000 || address > 0x7fffffff0000 || (address & 0xffff) != 0) - throw std::runtime_error("--windows-fixed-base requires a 64 KiB-aligned address from 0x20000 through 0x7fffffff0000"); - args.windowsFixedBase = address; + args.windowsFixedBase = ParseFixedBase(argv[++i], 0x20000, "--windows-fixed-base requires a 64 KiB-aligned address from 0x20000 through 0x7fffffff0000"); } else if (arg == "--linux-fixed-base") { if (args.linuxFixedBase) throw std::runtime_error("--linux-fixed-base cannot be repeated"); if (i + 1 >= argc || std::string_view(argv[i + 1]).empty() || std::string_view(argv[i + 1]).starts_with("--")) diff --git a/core/relinker/cli/tests/CliArgsTests.cpp b/core/relinker/cli/tests/CliArgsTests.cpp index a56f25be56..b7e0a7d218 100644 --- a/core/relinker/cli/tests/CliArgsTests.cpp +++ b/core/relinker/cli/tests/CliArgsTests.cpp @@ -223,6 +223,8 @@ void windowsFixedBase() { const auto args = parse({"--windows-fixed-base", value, "--windows", "input.elf", "output.exe"}); require(args.windowsFixedBase == 0x400000, "fixed base was parsed incorrectly"); } + require(parse({"--windows", "--windows-fixed-base", "0x20000", "input.elf", "output.exe"}).windowsFixedBase == 0x20000, "lowest fixed base was rejected"); + require(parse({"--windows", "--windows-fixed-base", "0x7fffffff0000", "input.elf", "output.exe"}).windowsFixedBase == 0x7fffffff0000, "highest fixed base was rejected"); require(parse({"--windows", "--windows-fixed-base", "0x400000000", "input.elf", "output.exe"}).windowsFixedBase == 0x400000000, "fixed base was truncated to 32 bits"); for (const auto& value : {"0", "0x10000", "0x400001", "0x", "-4194304", "+4194304", " 4194304", "0x400000junk", "18446744073709551616", "0x800000000000"}) expectThrows({"--windows", "--windows-fixed-base", value, "input.elf", "output.exe"}, "--windows-fixed-base requires a 64 KiB-aligned address from 0x20000 through 0x7fffffff0000"); diff --git a/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp b/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp index 648b8cbd79..b48555e99d 100644 --- a/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp +++ b/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp @@ -218,6 +218,8 @@ inline constexpr std::uint8_t ModRmRmRipRelative = 0x05; inline constexpr std::uint8_t Grp3RegTestMax = 0x01; inline constexpr std::uint8_t SibBaseMask = 0x07; inline constexpr std::uint8_t SibBaseDisp32 = 0x05; +inline constexpr std::uint8_t SibIndexMask = 0x38; +inline constexpr std::uint8_t SibIndexNone = 0x20; inline constexpr std::size_t Disp8Size = 1; inline constexpr std::size_t Disp32Size = 4; diff --git a/core/relinker/codegen/src/x86/WideStoreOperands.cpp b/core/relinker/codegen/src/x86/WideStoreOperands.cpp index 478c4108ed..529d39596b 100644 --- a/core/relinker/codegen/src/x86/WideStoreOperands.cpp +++ b/core/relinker/codegen/src/x86/WideStoreOperands.cpp @@ -37,8 +37,11 @@ void _appendU32(std::vector& bytes, const std::uint32_t value) { std::optional DecodeVexWideStore(const std::uint8_t* data, const std::size_t length) { using namespace X64OpcodeConstants; std::size_t pos = 0; - while (pos < length && _legacyPrefixAllowed(data[pos])) + bool addressSize32 = false; + while (pos < length && _legacyPrefixAllowed(data[pos])) { + addressSize32 = addressSize32 || data[pos] == PrefixAddressSize; ++pos; + } const auto vex = pos; if (vex + 2 > length) return std::nullopt; @@ -76,6 +79,7 @@ std::optional DecodeVexWideStore(const std::uint8_t* data, co } const bool ripRelative = mod == ModRmModIndirect && rm == ModRmRmRipRelative; const bool absolute = mod == ModRmModIndirect && sib.has_value() && (*sib & SibBaseMask) == SibBaseDisp32; + const bool absoluteNoIndex = absolute && (*sib & SibIndexMask) == SibIndexNone && (inverted & 0x40) != 0; const std::size_t displacementSize = mod == ModRmModDisp8 ? Disp8Size : (mod == ModRmModDisp32 || ripRelative || absolute) ? Disp32Size : 0; if (cursor + displacementSize != length) return std::nullopt; @@ -110,8 +114,16 @@ std::optional DecodeVexWideStore(const std::uint8_t* data, co highDisplacementSize = highMod == ModRmModDisp8 ? Disp8Size : Disp32Size; } } - if (!ripRelative && highDisplacement > std::numeric_limits::max()) - throw CodegenException("256-bit store displacement leaves no room for its upper half"); + const bool address32Absolute = addressSize32 && absoluteNoIndex; + const auto unsignedHighDisplacement = static_cast(static_cast(displacement)) + kHalfBytes; + if (!ripRelative) { + if (address32Absolute) { + if (unsignedHighDisplacement > std::numeric_limits::max()) + throw CodegenException("256-bit store displacement leaves no room for its upper half"); + } else if (highDisplacement > std::numeric_limits::max()) { + throw CodegenException("256-bit store displacement leaves no room for its upper half"); + } + } high.push_back(static_cast((highMod << ModRmModShift) | reg | rm)); if (sib) high.push_back(*sib); @@ -121,7 +133,7 @@ std::optional DecodeVexWideStore(const std::uint8_t* data, co else if (ripRelative) _appendU32(high, 0); else if (highDisplacementSize == Disp32Size) - _appendU32(high, static_cast(static_cast(highDisplacement))); + _appendU32(high, static_cast(highDisplacement)); high.push_back(kUpperHalf); if (ripRelative) { operands.High.RipDisplacementOffset = highDisplacementOffset; diff --git a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp index 451340b35f..f0165db65f 100644 --- a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp +++ b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp @@ -839,6 +839,19 @@ void wideStoreSplit() { require(farRipSplit && farRipSplit->High.RipTarget == std::int64_t{8} + 0x7FFFFFF8 + 0x10, "A RIP-relative 256-bit store near the end of its displacement range was not split"); const Bytes farBase = {0xC5, 0xFC, 0x11, 0x80, 0xF8, 0xFF, 0xFF, 0x7F}; requireFailure([&] { (void)Codegen::DecodeVexWideStore(farBase.data(), farBase.size()); }, "A 256-bit store whose upper half has no 32-bit displacement was kept"); + const Bytes absolute32 = {0x67, 0xC5, 0xFC, 0x11, 0x04, 0x25, 0xF8, 0xFF, 0xFF, 0x7F}; + const auto absolute32Split = Codegen::DecodeVexWideStore(absolute32.data(), absolute32.size()); + require(absolute32Split && absolute32Split->Low.Bytes == Bytes{0x67, 0xC5, 0xF8, 0x11, 0x04, 0x25, 0xF8, 0xFF, 0xFF, 0x7F} && absolute32Split->High.Bytes == Bytes{0x67, 0xC4, 0xE3, 0x7D, 0x19, 0x04, 0x25, 0x08, 0x00, 0x00, 0x80, 0x01}, "A 32-bit absolute 256-bit store was not split across the signed displacement boundary"); + const Bytes disp32 = {0x67, 0xC5, 0xFC, 0x11, 0x80, 0xF8, 0xFF, 0xFF, 0x7F}; + requireFailure([&] { (void)Codegen::DecodeVexWideStore(disp32.data(), disp32.size()); }, "A 32-bit base-relative 256-bit store whose upper half can wrap was split"); + const Bytes indexed32 = {0x67, 0xC5, 0xFC, 0x11, 0x04, 0x0D, 0xF8, 0xFF, 0xFF, 0x7F}; + requireFailure([&] { (void)Codegen::DecodeVexWideStore(indexed32.data(), indexed32.size()); }, "A 32-bit indexed 256-bit store whose upper half can wrap was split"); + const Bytes extendedIndex32 = {0x67, 0xC4, 0xA1, 0x7C, 0x11, 0x04, 0x25, 0xF8, 0xFF, 0xFF, 0x7F}; + requireFailure([&] { (void)Codegen::DecodeVexWideStore(extendedIndex32.data(), extendedIndex32.size()); }, "A 32-bit store indexed by an extended register through VEX.X was split as absolute"); + const Bytes absolute32Overflow = {0x67, 0xC5, 0xFC, 0x11, 0x04, 0x25, 0xF8, 0xFF, 0xFF, 0xFF}; + requireFailure([&] { (void)Codegen::DecodeVexWideStore(absolute32Overflow.data(), absolute32Overflow.size()); }, "A 32-bit absolute 256-bit store whose upper half crosses the address boundary was split"); + const Bytes absolute64 = {0xC5, 0xFC, 0x11, 0x04, 0x25, 0xF8, 0xFF, 0xFF, 0x7F}; + requireFailure([&] { (void)Codegen::DecodeVexWideStore(absolute64.data(), absolute64.size()); }, "A 64-bit absolute 256-bit store whose upper half has no signed displacement was kept"); requireFailure([] { Codegen::StubBodyBuilder body; body.RipRelative(Bytes{0x90}, 0, 0); }, "A RIP-relative displacement outside its instruction was relocated"); const auto rosetta = Codegen::MakeAmd64OnlyInstructionMatcher(Codegen::Amd64OnlyTarget::Rosetta); const Bytes store = {0xC4, 0xC1, 0x7C, 0x11, 0x47, 0x68}; diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp index 65cef1375d..1f8522303e 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvRuntimeBuffer.cpp @@ -282,7 +282,8 @@ std::uint32_t readBuffer(SpirvValueEmitContext& context, const IrValue& instruct const auto highIndex = Select(state, TypeU32(state), both(state, inBounds, unaligned), next, lowIndex); const auto low = Binary(state, spv::OpShiftRightLogical, TypeU32(state), load(lowIndex), shift); const auto high = EmitValueOrZero(state, unaligned, Binary(state, spv::OpShiftLeftLogical, TypeU32(state), load(highIndex), highShift)); - return EmitValueOrZero(state, inBounds, combine(low, high)); + const auto value = combine(low, high); + return (access.memoryAccess & spv::MemoryAccessVolatileMask) != 0u ? value : EmitValueOrZero(state, inBounds, value); } std::uint32_t byteBuffers(SpirvEmitterState& state) { diff --git a/docs/dev/debt/libSceFontGs.md b/docs/dev/debt/libSceFontGs.md new file mode 100644 index 0000000000..5405d4e1ad --- /dev/null +++ b/docs/dev/debt/libSceFontGs.md @@ -0,0 +1,5 @@ +# libSceFontGs technical debt + +### Unknown function info + +- [`sceFontGraphicsGnmSurfaceInit`, `sceFontSelectGraphicsGnm` and four unnamed exports](../../../core/libs/prx/libSceFontGs/Export.cpp) (libSceFontGs) - parameter lists, return types and behaviour are unverified, so every entry throws through `NotImplemented_nid_no_patch`; the parameterless `int` declarations only resolve imports. The [PS5 13.60 export inventory](https://github.com/xdCloudy/PS5-13.60-Function-Stub-Library/blob/226383a7fa4b1275d6cebf61eb1c1e7ca7b58bcb/data/firmware-13.60/libscefontgs-b42f55c1c1.jsonl) names two of them and lists `DI4LrTUrD04`, `NtbMKwRg6qg`, `UsBLoyXsQZs` and `uksHKVEsxu4` without names; the pinned [module map](https://github.com/boykopovar/ps5-nid-map/blob/cc1d586fddab576a520f226da6b2632b031e6d09/data/Modules/libSceFontGs.inc) assigns all six to `libSceFontGs`. The six exports shared with `libSceFontGs.native` are left to that library. diff --git a/docs/dev/debt/libSceLibreSsl3.md b/docs/dev/debt/libSceLibreSsl3.md index f1d9e2c548..a6c1edbe5c 100644 --- a/docs/dev/debt/libSceLibreSsl3.md +++ b/docs/dev/debt/libSceLibreSsl3.md @@ -2,14 +2,14 @@ ### Unknown function info -- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 81 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref` and `EC_KEY_up_ref`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. +- [libSceLibreSsl3 pending exports](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) - 80 named callable exports, excluding `CRYPTO_memcmp`, `MD5`, `SHA1`, `d2i_ASN1_INTEGER`, `EVP_PKEY_up_ref`, `EC_KEY_up_ref` and `RSA_up_ref`, remain unimplemented from the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl3.c), cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`. ### Functional - [ASN.1 integer decoding](../../../core/libs/prx/libSceLibreSsl3/src/IntegerDecoder.cpp) throws on rejected encodings/allocation failures instead of matching native NULL/error handling. Redundant-sign/empty-content rejection retains old output that the module frees and clears. The decoder does not populate the legacy queue; remaining LibreSSL3 ERR exports throw. [13.42 native-PC evidence](https://github.com/boykopovar/AnyPS5/pull/10948#issuecomment-6412137728), using a recording allocator, confirms tested replacement/free order, allocation cleanup and negative-length NULL returns. Nonnegative null input faults natively and throws here. Console/application-heap ABI and untested error-precedence/overflow combinations remain unverified. Contracts follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/asn1). Closes when: error/replacement semantics and console/allocator interoperability are verified. - [CRYPTO_memcmp](../../../core/libs/prx/libSceLibreSsl3/Export.cpp) follows the public [LibreSSL signature and equality contract](https://man.openbsd.org/CRYPTO_memcmp.3), without PS5 verification. The exact nonzero result for unequal buffers is unspecified by that contract. Content-independent execution of its volatile byte reads still depends on the compiler; inspect optimized code when changing toolchains. Closes when: measured on a PS5 and the supported toolchains' generated code is checked. - [MD5 and SHA1](../../../core/libs/prx/libSceLibreSsl3/src/Digest.cpp) (libSceLibreSsl3) - follow LibreSSL's [md5.c, sha1.c](https://github.com/libressl/openbsd/tree/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto) and hash through libSceLibreSsl's `MD5_*` and `SHA1_*`. A null digest buffer, which LibreSSL 4.0.0 does not check, throws. Nothing is checked on the console -- [EVP_PKEY_up_ref and EC_KEY_up_ref](../../../core/libs/prx/libSceLibreSsl3/src/Key.cpp) - console layouts and retain/release interoperability are unverified. The 32-byte EVP and 64-byte EC layouts, with counters at offsets 4 and 48, follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto) `evp/evp_local.h`, `evp/p_lib.c`, `ec/ec_local.h` and `ec/ec_key.c`. Atomic retains replace its internal mutex; future releases must synchronize with the same counters. With no key constructors/destructors in this module, tests use caller-owned objects without validating allocator or destruction compatibility. Null keys and signed-counter overflow, undefined upstream, throw without mutation. Closes when: console layouts and lifetime interoperability are measured. +- [EVP_PKEY_up_ref, EC_KEY_up_ref and RSA_up_ref](../../../core/libs/prx/libSceLibreSsl3/src/Key.cpp) - console layouts and retain/release interoperability are unverified. The 32-byte EVP, 64-byte EC and 152-byte RSA layouts, with counters at offsets 4, 48 and 104, follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/tree/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto) `evp/evp_local.h`, `evp/p_lib.c`, `ec/ec_local.h`, `ec/ec_key.c`, `rsa/rsa_local.h` and `rsa/rsa_lib.c`; the RSA layout has no `engine` field, unlike libSceLibreSsl's `LibreSslRsa`. Atomic retains replace its internal mutex; future releases must synchronize with the same counters. With no key constructors/destructors in this module, tests use caller-owned objects without validating allocator or destruction compatibility. Null keys and signed-counter overflow, undefined upstream, throw without mutation. Closes when: console layouts and lifetime interoperability are measured. - [Parent export forwarding](../../../core/libs/prx/libSceLibreSsl3/ParentExports.cpp) exposes 395 parent-library exports on Windows through IAT tail jumps without changing their ABI. Non-Windows hosts use their existing dependency lookup. Static COFF inspection verifies names, parent imports and tail jumps; Windows loading and runtime behavior remain unverified. Parent functions retain their existing implementation limitations. - [sk_new, sk_push and sk_free](../../../core/libs/prx/libSceLibreSsl3/src/Stack.cpp) (libSceLibreSsl3) - follow [LibreSSL 4.0.0](https://github.com/libressl/openbsd/blob/ce488b78e0bd994780569df718596b9776ff681a/src/lib/libcrypto/stack/stack.c) `stack.c` with libSceLibreSsl's 32-byte `_STACK` layout, which `stack_local.h` keeps there. Stacks and element arrays come from the guest heap, so the forwarded `sk_pop_free` can release them. Nothing is checked on the console. Where LibreSSL faults or overflows, they throw: `sk_push` on a null stack, on a negative element count, on a null element array it does not regrow, or when the element array cannot double without overflowing `int` diff --git a/docs/dev/debt/libSceNpCppWebApi.md b/docs/dev/debt/libSceNpCppWebApi.md index 385c7ec818..05266d03b5 100644 --- a/docs/dev/debt/libSceNpCppWebApi.md +++ b/docs/dev/debt/libSceNpCppWebApi.md @@ -5,7 +5,7 @@ - [sce::Np::CppWebApi::Common](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - the `finish` overrides of `DownStreamTransaction<...>` (2 instantiations) and `Vector>::pushBack` return success without doing work; a failed request never produces a transaction to finish. `Transaction<...>::setOnFinishedCallback` and `TransactionBase<...>::setResponseInformationOption` (5 instantiations each, the `UserProfile::V1`, `ProfanityFilter::V2` and `CommunicationRestrictionStatus::V3` responses) return success without storing the callback or the option: `start` fails, so neither is ever used. The constructors and destructors of the same five `Transaction<...>` instantiations succeed without keeping state - [sce::Np::CppWebApi::Leaderboards::V1](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - request-body bookkeeping succeeds without storing state: `GetRankingRequestBody::set{StartSerialRank,Group,Limit,Users,Offset}`, `RecordScoreRequestBody::set{Comment,SmallData,NeedsTmpRank,ComparedDateTime,PcId}` and `User::{setAccountId,setPcId}`. `BoardsApi::ParameterToGetBoardDefinition`, `RecordApi::ParameterToRecordLargeData`, `RecordApi::ParameterToRecordScore`, `ViewApi::ParameterToGetLargeDataByObjectId` and `ViewApi::ParameterToGetRanking` keep only whether they are initialized, as the SessionManager::V1 parameters do: `initialize` and `terminate` return the codes of the module bundled with PPSA21564, 0x80553512 for an empty `RecordScoreRequestBody` included. They store none of the arguments, and `setxPsnAtomicOperation` and `setgetRankingRequestBody` store nothing either - [sce::Np::CppWebApi::SessionManager::V1](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - `RequestPlayerSession::set{MaxSpectators,InvitableUserType,JoinableUserType,ExclusiveLeaderPrivileges}` and `RequestPlayerSessionPlayer::setPushContexts` succeed without storing state. The `PlayerSessionsApi` parameters `ParameterToCreatePlayerSessions`, `ParameterToGetPlayerSessionInvitations`, `ParameterToJoinPlayerSessionAsPlayer` and `ParameterToLeavePlayerSession` keep only whether they are initialized, as the TitleCloudStorage ones do: `initialize`, `terminate` and `setinvitationInvalidFilter` return the codes of the module bundled with PPSA21564, plus 0x80553512 when the request body of `ParameterToCreatePlayerSessions` or `ParameterToJoinPlayerSessionAsPlayer` is empty. They store none of the strings or bodies, and `setfields` stores nothing either: only a request would read them -- [sce::Np::CppWebApi::TitleCloudStorage::V1 and InGameCatalog::V5](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - `DataApi::ParameterToDownloadData`, `DataApi::ParameterToGetMultiDataStatusesByUser` and `ContainerApi::ParameterToGetContainer` keep only whether they are initialized. `initialize`, `terminate` and the string setters return the codes of the module bundled with PPSA21564: 0x80553503 for a null `LibContext` or string, 0x80553502 for a `LibContext` that `Common::initialize` did not set or a parameter not yet initialized, 0x8055350f for a second `initialize` and 0x80553510 for `terminate` without one. They store none of the values, and `ParameterToGetContainer::set{sort,offset,limit,serviceLabel}` store nothing either: only a request would read them +- [sce::Np::CppWebApi::TitleCloudStorage::V1 and InGameCatalog::V5](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - `DataApi::ParameterToDownloadData`, `DataApi::ParameterToGetMultiDataStatusesByUser` and `ContainerApi::ParameterToGetContainer` keep only whether they are initialized. `initialize`, `terminate` and the string setters return the codes of the module bundled with PPSA21564: 0x80553503 for a null `LibContext` or string, 0x80553502 for a `LibContext` that `Common::initialize` did not set or a parameter not yet initialized, 0x8055350f for a second `initialize` and 0x80553510 for `terminate` without one; an empty `SetDataInfo` body returns 0x80553512. `ParameterToUploadData`, `ParameterToSetDataInfo`, `ParameterToDeleteMultiDataByUser` and `ParameterWithBinaryRequestBodyToUploadData` add state-less setters and keep only the initialized flag. None store values; only a request would read them. `DataStatus::{getLastUpdatedDateTime,getInfo}` and `UploadDataResponseHeaders::{getXPsnAtomicOperationId,hasXPsnAtomicOperationId}` throw, as a failed request never produces those objects. The console behaviour is not measured. - [sce::Np::CppWebApi::UserProfile::V1, ProfanityFilter::V2, CommunicationRestrictionStatus::V3](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - `FriendsApi::ParameterToGetFriends`, `PresenceApi::ParameterToGetBasicPresences`, `BlocksApi::ParameterToGetBlockingUsers`, `ProfanityApi::ParameterToFilterProfanity` and `CommunicationRestrictionStatusApi::ParameterToGetCommunicationRestrictionStatus` keep only whether they are initialized, as the SessionManager::V1 parameters do: `initialize` and `terminate` return the codes of the module bundled with PPSA21564, 0x80553512 for an empty `WebApiFilterRequest` included. They store none of the arguments: only a request would read them ### Unknown function info diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index baf22a6271..c3730089a9 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -2,10 +2,6 @@ ### Silent stubs -- [sceShellCoreUtilActivateAbort, sceShellCoreUtilActivateStart, sceShellCoreUtilActivateStartAsync, sceShellCoreUtilActivateTerm](../../../core/libs/prx/libSceSystemService/PendingExports.cpp) return zero with the recovered handle argument. Activation service state is not modeled. - -- [sceShellCoreUtilActivateRecordActivation](../../../core/libs/prx/libSceSystemService/PendingExports.cpp) - activation service state is not modeled; this parameterless export returns zero without recording an activation. - - [libSceSystemService](../../../core/libs/prx/libSceSystemService/Export.cpp) - `sceSystemServicePowerTick`, `sceSystemServiceReportAbnormalTermination`, `sceSystemServiceDisableMusicPlayer`, `sceSystemServiceReenableMusicPlayer`, `sceSystemServiceDisableMediaPlay` and `sceSystemServiceReenableMediaPlay` return success and do nothing: the host has no auto power-off timer to reset, no system crash reporter to notify, no system music player to pause or resume and no system media playback to disable. PPSA01521 calls `sceSystemServiceDisableMediaPlay` during start-up, and its wrappers for both pass no arguments - [sceSystemServiceLaunchWebBrowser](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - AnyPS5 launches no web browser, so the call fails and nothing is shown. RommPS PPSA76677 then tells the player to scan its QR code instead - [sceSystemServiceShowControllerSettings](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - returns success without showing the controller settings: AnyPS5 has no system UI to open. Cat Quest III (PPSA15065) imports it @@ -17,8 +13,6 @@ ### Unknown function info -- [sceShellCoreUtilActivateGetStatus, sceShellCoreUtilActivateInit, sceShellCoreUtilActivateIsActivated](../../../core/libs/prx/libSceSystemService/PendingExports.cpp) - the activation contract is unresolved: handle values and lifecycle, the activation flag, status/result fields, null-output errors and repeated initialization are unverified. All calls throw `NotImplemented_nid_no_patch`, including calls with null outputs. - - [sceSystemServiceAddLocalProcessForPsmKit, sceSystemServiceKillLocalProcessForPsmKit](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - AnyPS5 runs one process and starts or stops no other, so both throw without reading their arguments. The parameters follow Forever Launcher's own firmware 5.50 probes (`ps5/probes/localproc` in [bizkut/ps5-forever](https://github.com/bizkut/ps5-forever)): a path, a null-terminated argv and a 0x90-byte parameter block in, a handle out; the kill takes that handle. Their results on the console are unknown. Forever Launcher (PPSA99030) imports both and stops at load without them. - [sceSystemServiceGetAppId, sceSystemServiceGetAppTitleId, sceLncUtilGetAppTitleId](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the console's results for a title ID or app ID other than the running title are unknown, and so are its errors for null buffers, so those calls throw. A null title ID, or one whose length isn't 9, makes `sceSystemServiceGetAppId` return -1 before any request, as firmware 13.42's `libSceSystemService.sprx` does ([emulator run](https://github.com/boykopovar/AnyPS5/pull/10801#issuecomment-6403075212)); otherwise it compares the title ID exactly with the one in `param.json` or `param.sfo`. The title ID buffer is 10 bytes, so a running title ID of 10 or more characters throws too. The signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L236-L260), except that `sceLncUtilGetAppTitleId` returns an `int`, as declared in the pending exports, and 0 on success like `sceSystemServiceGetAppTitleId`; SharpProspero names `ErrorNoAppInfo` (0x80A10009) only for `sceSystemServiceGetAppFocusedAppStatus`. - [sceLncUtilGetAppId, sceLncUtilIsAppLaunched, sceLncUtilGetAppIdListOfBigApp, sceSystemServiceGetAppIdListOfBigApp](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - only the running title answers. `sceLncUtilGetAppId` returns its app ID as `sceSystemServiceGetAppId` does, `sceLncUtilIsAppLaunched` reports it launched, and both big-app lists write its app ID as their only entry with a count of 1, because AnyPS5 runs one title and cannot start another. A null or non-9-character title ID returns -1 from `sceLncUtilGetAppId` and 0x80940005 from `sceLncUtilIsAppLaunched`, and a null output, list or count returns 0x80940005, before any request, as firmware 13.42's `libSceSystemService.sprx` does ([emulator run](https://github.com/boykopovar/AnyPS5/pull/10801#issuecomment-6403075212)). These calls answer as if the LncUtil client were initialized; for valid arguments without it, 13.42 returns 0x80940001. Another 9-character title ID and a list capacity of 0 throw: the module passes them on as a request, so whether the console reports another installed title as not launched, and what it answers for capacity 0, are unknown. The signatures are the ones the pending exports recovered @@ -145,7 +139,8 @@ - [ShellCoreUtil dialog parameters](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the 24-byte error-dialog parameter contains a message pointer at offset zero, a null-terminated vector of string arguments at offset eight and a byte flag at offset 16. Field offsets and size are asserted; the flag meaning remains unresolved. Start/stop streaming-game and unmount requests take strings. Bus-transfer testing writes one byte after a string input. -- [SystemService activation and process signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): activation wrappers validate a machine-sized handle and expose 32-bit status/result outputs or a byte activation flag. Shell activation initialization also receives a type selector; managed callers confirm the machine-sized handle consumed by constant-return start/abort/terminate exports. SystemService local-process parameters differ from LncUtil parameters and are kept as distinct types; general creation reads through offset 71, JVM/PS2 creation reads two 32-bit fields at offsets four and eight. Install status writes 12 bytes and application status writes 16 bytes; their remaining field meanings are unresolved. +- [Codec activation](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): the `sceShellCoreUtilActivate*` calls and the HEVC, HEVC soft and MPEG-2 `sceSystemServiceActivate*` wrappers follow firmware 13.42's libSceSystemService, read from disassembly, not measured on a console. There they ask no other process: initialization hands out handles counted from 0x1000 in steps of 4, shared by every codec; the status is always 2 with result 0; a codec always reads as activated; start, abort, terminate and recording return 0. The wrappers return 0x80A10003 for a null handle or output, the shell calls 0x80E30005 for a null output. The name of status 2 and whether older firmware asked the shell are unknown. +- [SystemService process signatures](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): SystemService local-process parameters differ from LncUtil parameters and are kept as distinct types; general creation reads through offset 71, JVM/PS2 creation reads two 32-bit fields at offsets four and eight. Install status writes 12 bytes and application status writes 16 bytes; their remaining field meanings are unresolved. - [SystemService query payloads](../../../core/libs/prx/libSceSystemService/PendingExports.cpp): daemon events write 12 bytes; local-process entries occupy 36 bytes, consisting of a 32-bit value and 32-byte name. PS-button extraction reads a 16-byte event and rearranges its fields into a distinct 16-byte output; semantic names remain unresolved. Privacy definition/version queries take string keys and distinct output types; their layouts remain unresolved. Workaround information writes four bytes at offset zero and 16 bytes at offset eight. diff --git a/docs/dev/debt/libkernel_sys.md b/docs/dev/debt/libkernel_sys.md index fc859388db..8466f8a45e 100644 --- a/docs/dev/debt/libkernel_sys.md +++ b/docs/dev/debt/libkernel_sys.md @@ -340,7 +340,7 @@ | `Unknown` | `zAloppPJxy0` | - [sceKernelGetHwModelName, sceKernelIsProspero](../../../core/libs/prx/libkernel_sys/Export.Discovery.cpp) (libkernel_sys) - both throw. On a CFI-2014 PS5 (firmware 13.60) a payload process got 0 from `sceKernelGetHwModelName(char*)`, which wrote "CFI-2014 A01X" and a terminator (14 bytes), and 0 from `sceKernelIsProspero()`. The model string belongs to one console and the buffer size and null behaviour are not known, and 0 from `sceKernelIsProspero` on a PS5 suggests it answers something other than the model, so neither is implemented -- [sceKernelGetCompiledSdkVersion](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - which SDK version the console returns is unmeasured. The [PS5 payload SDK](https://github.com/ps5-payload-dev/sdk/blob/a7685ce8e97ce1f8a4f38253a41d54241c9124f1/crt/kernel.c#L257-L275) declares the process parameters with a 32-bit PS4 SDK version at offset `0x10` and a 32-bit PS5 SDK version at `0x14`; this export returns the field at `0x10`, the one [libSceHmd2](../../../core/libs/prx/libSceHmd2/Export.cpp) reads and the one [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/615bcba5e21b82dc59588575f7ecbce8da09ca2c/src/emulator.cpp#L110-L138) (PS4, a single 64-bit `sdk_version` there) reads from the eboot. The `int (int32_t*)` signature and `EINVAL` (`0x80020016`) for a null output follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/615bcba5e21b82dc59588575f7ecbce8da09ca2c/src/core/libraries/kernel/process.cpp#L56-L62). Closes when: measured on a PS5 +- [sceKernelGetCompiledSdkVersion](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - which SDK version the console returns is unmeasured. The [PS5 payload SDK](https://github.com/ps5-payload-dev/sdk/blob/a7685ce8e97ce1f8a4f38253a41d54241c9124f1/crt/kernel.c#L257-L275) declares the process parameters with a 32-bit PS4 SDK version at offset `0x10` and a 32-bit PS5 SDK version at `0x14`; this export returns the field at `0x10`, which [libSceHmd2](../../../core/libs/prx/libSceHmd2/Export.cpp) reads and [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/615bcba5e21b82dc59588575f7ecbce8da09ca2c/src/emulator.cpp#L110-L138) reads from the eboot. The `int (int32_t*)` signature and `EINVAL` (`0x80020016`) for null follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/615bcba5e21b82dc59588575f7ecbce8da09ca2c/src/core/libraries/kernel/process.cpp#L56-L62); `SCE_KERNEL_ERROR_EFAULT` for an unwritable output pointer is a host-safety fallback so the process does not crash, not a measured PS5 result, and the console's behaviour for such a pointer is unmeasured. Closes when: measured on a PS5 - [scePthreadCondattrGetclock](../../../core/libs/prx/libkernel_sys/Export.cpp) (libkernel_sys) - the `int (const PthreadCondattr*, KernelClockid*)` signature and checks follow FreeBSD 11.4 libthr's [`_pthread_condattr_getclock`](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libthr/thread/thr_condattr.c#L82-L90) with the error code of libkernel's `scePthreadCondattrSetclock`: a null or destroyed attribute returns `SCE_KERNEL_ERROR_EINVAL`, and a null clock pointer throws, since libthr writes through it unchecked. Console behaviour is unverified ### Functional diff --git a/tools/annotate_module.py b/tools/annotate_module.py index 1ed6c4c37b..b520a6b718 100644 --- a/tools/annotate_module.py +++ b/tools/annotate_module.py @@ -25,6 +25,7 @@ DT_JMPREL = 23 DT_PLTRELSZ = 2 DT_PLTREL = 20 DT_STRSZ = 10 +DT_SYMENT = 11 DT_RELAENT = 9 DT_REL = 17 DT_RELA_KIND = 7 @@ -35,7 +36,7 @@ RELA_SIZE = 24 R_X86_64_NONE = 0 LINE_ADDRESS = re.compile(r"^\s*([0-9a-f]+):") -STUB = re.compile(r"^\s*([0-9a-f]+):\s+(?:\w+\s+)?jmpq?\s+\*(?:-?0x[0-9a-f]+)?\(%rip\)\s+# 0x([0-9a-f]+)") +STUB = re.compile(r"^\s*([0-9a-f]+):\s+(?:\w+\s+)*jmpq?\s+\*(?:-?0x[0-9a-f]+)?\(%(?:rip|eip)\)\s+# 0x([0-9a-f]+)") BRANCH = re.compile(r"\b(?:call|jmp)q?\s+0x([0-9a-f]+)") REFERENCE = re.compile(r"# 0x([0-9a-f]+)") @@ -57,6 +58,8 @@ class Module: if self.strtab + self.strsz > len(self.data): raise ModuleError("the string table of DT_STRSZ %#x extends past the end of the file" % self.strsz) self.address(self.tags[DT_STRTAB], self.strsz) + if self.require_tag(DT_SYMENT, "DT_SYMENT") != SYMBOL_SIZE: + raise ModuleError("DT_SYMENT is not %d" % SYMBOL_SIZE) self.symbol_count = self.count_symbols() self.address(self.tags[DT_SYMTAB], self.symbol_count * SYMBOL_SIZE) self.check_relocation_tags() @@ -195,12 +198,12 @@ class Module: def code_segments(self): segments = [] for vaddr, offset, size, flags in sorted(self.loads): - if flags & PF_X: + if flags & PF_X and size: if offset + size > len(self.data): raise ModuleError("the executable PT_LOAD segment at %#x extends past the end of the file" % vaddr) segments.append((vaddr, self.data[offset:offset + size])) if not segments: - raise ModuleError("no executable PT_LOAD segment") + raise ModuleError("no executable PT_LOAD segment with file-backed data") return segments @@ -243,12 +246,23 @@ def disassemble(code, vaddr): return result.stdout.splitlines() +def referenced_address(line): + if "%fs:" in line or "%gs:" in line: + return None + reference = REFERENCE.search(line) + if reference is None: + return None + address = int(reference.group(1), 16) + return address & 0xFFFFFFFF if "(%eip)" in line else address + + def find_stubs(listing, slots, namer): stubs = {} for line in listing: match = STUB.match(line) - if match and int(match.group(2), 16) in slots: - stubs[int(match.group(1), 16)] = namer.resolve(slots[int(match.group(2), 16)]) + target = referenced_address(line) + if match and target in slots: + stubs[int(match.group(1), 16)] = namer.resolve(slots[target]) return stubs @@ -257,9 +271,9 @@ def line_note(line, slots, stubs, namer): branch = BRANCH.search(line) if branch and int(branch.group(1), 16) in stubs: note = " ; -> " + stubs[int(branch.group(1), 16)] - reference = REFERENCE.search(line) - if reference and int(reference.group(1), 16) in slots: - note = " ; GOT " + namer.resolve(slots[int(reference.group(1), 16)]) + reference = referenced_address(line) + if reference in slots: + note = " ; GOT " + namer.resolve(slots[reference]) return note diff --git a/tools/audit_dump.py b/tools/audit_dump.py index 0d2414e620..3fa9d57a10 100644 --- a/tools/audit_dump.py +++ b/tools/audit_dump.py @@ -72,7 +72,7 @@ def convert(relinker, source, output, extra, windows): for relative in extra: command += ["--module-dir", relative] command += [str(source / "eboot.elf"), str(output)] - return subprocess.run(command, capture_output=True, text=True) + return subprocess.run(command, capture_output=True, text=True, encoding="utf-8", errors="replace") def comparison_path(path, links=frozenset()): @@ -155,7 +155,11 @@ def main(argv=None): if generated.exists() or generated.is_symlink(): print(f"FAIL: cannot empty {generated}: it still exists", file=sys.stderr) return 2 - output.mkdir(parents=True, exist_ok=True) + try: + output.mkdir(parents=True, exist_ok=True) + except OSError as error: + print(f"FAIL: cannot create {output}: {error}", file=sys.stderr) + return 2 try: module_directories, extra = prepare(dump, source) except (unwrap_self.Refused, OSError) as error: diff --git a/tools/check_configure_seed.py b/tools/check_configure_seed.py new file mode 100644 index 0000000000..c2175732d6 --- /dev/null +++ b/tools/check_configure_seed.py @@ -0,0 +1,35 @@ +import argparse +import sys +from pathlib import Path + +from configure_seed import checks, quote + + +def main(argv=None): + for stream in (sys.stdout, sys.stderr): + if hasattr(stream, "reconfigure"): + stream.reconfigure(encoding="utf-8", errors="backslashreplace") + parser = argparse.ArgumentParser(description="Check an unfiltered configure seed against its source CMake cache without writing") + parser.add_argument("cache", type=Path) + parser.add_argument("seed", type=Path) + args = parser.parse_args(argv) + try: + found = checks(args.cache) + expected = "".join(f"set({quote(name)} {quote(value)} CACHE INTERNAL {quote(doc)})\n" + for name, (value, doc) in sorted(found.items())).encode("utf-8") + try: + actual = args.seed.read_bytes() + except FileNotFoundError: + print(f"missing seed: {args.seed}") + return 1 + except (OSError, UnicodeError, ValueError) as error: + parser.error(str(error)) + if actual != expected: + print(f"stale seed: {args.seed}") + return 1 + print(f"{len(found)} check results match") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/inspect_self.py b/tools/inspect_self.py new file mode 100644 index 0000000000..667040951d --- /dev/null +++ b/tools/inspect_self.py @@ -0,0 +1,36 @@ +import argparse +import json +import struct +import sys +from pathlib import Path + +from unwrap_self import FIRST_REGION, SELF_MAGIC, SELF_MAGIC_KERNEL, Refused, parse_elf, read_records + + +def main(argv=None): + for stream in (sys.stdout, sys.stderr): + if hasattr(stream, "reconfigure"): + stream.reconfigure(encoding="utf-8", errors="backslashreplace") + parser = argparse.ArgumentParser(description="Inspect SELF record and original inner ELF header metadata as JSON; this does not validate conversion or decrypt segments") + parser.add_argument("input", type=Path) + args = parser.parse_args(argv) + try: + data = args.input.read_bytes() + if len(data) < FIRST_REGION: + raise Refused("file is smaller than the SELF header area") + magic = struct.unpack_from("= functions: + raise ValueError("PE export ordinal is outside the export address table") address = struct.unpack_from(" " + IMPORT_NAME)) + def test_eip_relative_stubs_and_calls_use_wrapped_got_addresses(self): + for address in (CODE_ADDRESS, 0xFFFFF000, 0x100001000): + for prefix in (b"\x67", b"\xf2\x67", b"\x3e\x67", b"\xf2\x3e\x67"): + with self.subTest(address=address, prefix=prefix): + length = len(prefix) + 6 + code = prefix + b"\xff\x25" + struct.pack(" " + IMPORT_NAME)) + + def test_eip_references_do_not_name_the_unwrapped_address(self): + address = 0x100001000 + for opcode in (b"\xff\x15", b"\x48\x8b\x05"): + with self.subTest(opcode=opcode): + length = 1 + len(opcode) + 4 + code = b"\x67" + opcode + struct.pack(" ", self.read(".asm")) + def test_none_relocation_does_not_annotate_a_stub_or_call(self): module = with_relocation(build_module(), 0x2280, 0, SLOT, 1, 0) summary = self.annotate(module) @@ -341,6 +388,34 @@ class AnnotateModuleTests(unittest.TestCase): self.assertLess(first, second) self.assertTrue(any(re.match(r"^\s*3000:\s+ret\b", line) for line in listing[second:])) + def test_empty_executable_segments_do_not_abort_other_disassembly(self): + for memory_size in (0, 0x1000): + for address in (0, 0x3000): + with self.subTest(memory_size=memory_size, address=address): + module = bytearray(build_module(extra_code=b"")) + struct.pack_into(" " + IMPORT_NAME, listing) + self.assertIn("; ===== export " + EXPORT_NAME, listing) + + def test_only_empty_executable_segments_fail_before_writing_reports(self): + self.assert_fails(build_module(code=b"", extra_code=b"", symbols=[DEFAULT_SYMBOLS[0]]), + "no executable PT_LOAD segment with file-backed data") + for suffix in (".asm", ".imports.txt", ".exports.txt"): + self.assertFalse((self.root / ("out" + suffix)).exists()) + + def test_nonempty_executable_segment_past_end_of_file_is_still_rejected(self): + module = bytearray(build_module(extra_code=b"\xc3")) + struct.pack_into("