Merge remote-tracking branch 'origin/main' into fix/json2-refresh-20261030

This commit is contained in:
Kyon committed 2026-10-30 18:24:54 +00:00
commit fd37cb7143
47 files changed
+1138 -98

No files matched your search

+2 -2
View File
@@ -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:
+1
View File
@@ -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' }}
+4
View File
@@ -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)
@@ -32,7 +32,7 @@ struct LoadCase {
std::uint32_t components = 1;
};
std::vector<std::uint32_t> emitLoad(const LoadCase& test, bool active, std::uint32_t offset, std::uint32_t records, bool robust, bool exact = false, bool valid = true) {
std::vector<std::uint32_t> 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<std::uint32_t> 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<std::uint32_t>({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<std::uint32_t>& 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}) {
@@ -57,13 +57,17 @@ ShaderRecompiler::RecompileRequest Request(std::span<const std::uint32_t> 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<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(Code.data()), std::as_bytes(std::span(Code))}}};
const std::array<ShaderRecompiler::MemoryRegion, 1> memory{{{reinterpret_cast<std::uintptr_t>(code.data()), std::as_bytes(std::span(code))}}};
std::array<std::uint32_t, 8> 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<std::uintptr_t>(code.data());
const auto handle = ShaderRecompiler::PrepareShader(request);
const std::array<Case, 7> cases{{
{"aligned", 0, 128, true},
@@ -258,6 +262,8 @@ int main() {
for (std::uint32_t index = 0; index < InputBytes; ++index) Input[index] = static_cast<std::uint8_t>(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");
+29
View File
@@ -1,3 +1,4 @@
#include "prx/libSceFontGs/include/InventoryExports.hpp"
#include <cstddef>
#include <cstdint>
#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);
}
@@ -0,0 +1,10 @@
#pragma once
#include "SceTypes.hpp"
extern "C" {
int APS5_VABI sceFontGraphicsGnmSurfaceInit(void);
int APS5_VABI sceFontSelectGraphicsGnm(void);
}
@@ -1 +1,3 @@
#pragma once
#include "prx/libSceFontGs/include/InventoryExports.hpp"
-5
View File
@@ -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;
@@ -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);
@@ -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);
@@ -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);
}
}
@@ -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:$<TARGET_FILE_DIR:libc>")
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)
+169
View File
@@ -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<const ParameterObject*>(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;
@@ -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);
@@ -131,6 +131,15 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>;PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
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:$<TARGET_FILE_DIR:libc>")
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)
@@ -2,10 +2,22 @@
#include "prx/libSceSystemService/SystemService.hpp"
#include "prx/libc/include/General.hpp"
#include <atomic>
namespace {
constexpr int SystemServiceUnavailable = static_cast<int>(0x80A10002);
constexpr int SystemServiceInvalidArgument = static_cast<int>(0x80A10003);
constexpr int SystemServiceRejected = static_cast<int>(0x80A10005);
constexpr int ShellCoreInvalidArgument = static_cast<int>(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<std::intptr_t> 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<std::intptr_t>(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<std::intptr_t>(handle), reinterpret_cast<std::int32_t*>(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<std::intptr_t*>(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<bool*>(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<std::intptr_t>(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<std::intptr_t>(handle), reinterpret_cast<std::int32_t*>(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<std::intptr_t*>(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<bool*>(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<std::intptr_t>(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<std::intptr_t>(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<std::intptr_t>(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<std::intptr_t>(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<std::intptr_t>(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<std::intptr_t>(handle), reinterpret_cast<std::int32_t*>(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<std::intptr_t*>(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<bool*>(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<std::intptr_t>(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<std::intptr_t>(handle));
}
int APS5_VABI sceSystemServiceAddLocalProcess(std::int32_t appId, const char* path, const char* const* argv, const SystemServiceLocalProcessParam* param) {
@@ -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);
+1 -1
View File
@@ -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<const std::byte*>(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;
}
@@ -34,6 +34,7 @@ int main() {
Require(ProcessParametersFixture::Install(process.data(), process.size()), "process parameters were not installed");
Require(sceKernelGetCompiledSdkVersion(nullptr) == static_cast<int>(0x80020016), "null output is not EINVAL");
Require(sceKernelGetCompiledSdkVersion(reinterpret_cast<std::int32_t*>(0x10)) == static_cast<int>(0x8002000e), "unwritable output is not EFAULT");
std::array<std::int32_t, 2> 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");
@@ -7,6 +7,7 @@
#include <cstdlib>
#include <cstring>
#include <limits>
#include <stdexcept>
#include <thread>
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<std::int32_t>::max();
std::array<unsigned char, sizeof(guarded)> 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<unsigned char, sizeof(guarded)> expected;
@@ -64,5 +84,7 @@ int main(int argc, char** argv) {
CheckKey<LibreSsl3EvpPkey>(EVP_PKEY_up_ref_nid_postfix, 4);
if (argc == 1 || std::strcmp(argv[1], "ec") == 0)
CheckKey<LibreSsl3EcKey>(EC_KEY_up_ref_nid_postfix, 48);
if (argc == 1 || std::strcmp(argv[1], "rsa") == 0)
CheckKey<LibreSsl3Rsa>(RSA_up_ref_nid_postfix, 104);
return 0;
}
@@ -0,0 +1,191 @@
#include "prx/libSceNpCppWebApi/include/Api.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <source_location>
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<int>(0x80552907);
constexpr int kParameterNotInitialized = static_cast<int>(0x80553502);
constexpr int kInvalidArgument = static_cast<int>(0x80553503);
constexpr int kAlreadyInitialized = static_cast<int>(0x8055350f);
constexpr int kTerminatedUninitialized = static_cast<int>(0x80553510);
constexpr int kBodyMissing = static_cast<int>(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;
}
@@ -0,0 +1,83 @@
#include "prx/libSceSystemService/include/SystemService.hpp"
#include "SceTypes.hpp"
#include <cstdint>
#include <cstdlib>
#include <initializer_list>
namespace {
constexpr int invalidArgument = static_cast<int>(0x80A10003u);
constexpr int shellInvalidArgument = static_cast<int>(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<std::int32_t>(untouchedStatus);
std::int32_t result = untouchedResult;
Require(sceShellCoreUtilActivateGetStatus(0, nullptr, &result) == shellInvalidArgument && result == untouchedResult);
Require(sceShellCoreUtilActivateGetStatus(0, &status, nullptr) == shellInvalidArgument && status == static_cast<std::int32_t>(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;
}
+1 -8
View File
@@ -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("--"))
+2
View File
@@ -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");
@@ -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;
@@ -37,8 +37,11 @@ void _appendU32(std::vector<std::uint8_t>& bytes, const std::uint32_t value) {
std::optional<WideStoreOperands> 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<WideStoreOperands> 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<WideStoreOperands> DecodeVexWideStore(const std::uint8_t* data, co
highDisplacementSize = highMod == ModRmModDisp8 ? Disp8Size : Disp32Size;
}
}
if (!ripRelative && highDisplacement > std::numeric_limits<std::int32_t>::max())
throw CodegenException("256-bit store displacement leaves no room for its upper half");
const bool address32Absolute = addressSize32 && absoluteNoIndex;
const auto unsignedHighDisplacement = static_cast<std::uint64_t>(static_cast<std::uint32_t>(displacement)) + kHalfBytes;
if (!ripRelative) {
if (address32Absolute) {
if (unsignedHighDisplacement > std::numeric_limits<std::uint32_t>::max())
throw CodegenException("256-bit store displacement leaves no room for its upper half");
} else if (highDisplacement > std::numeric_limits<std::int32_t>::max()) {
throw CodegenException("256-bit store displacement leaves no room for its upper half");
}
}
high.push_back(static_cast<std::uint8_t>((highMod << ModRmModShift) | reg | rm));
if (sib)
high.push_back(*sib);
@@ -121,7 +133,7 @@ std::optional<WideStoreOperands> DecodeVexWideStore(const std::uint8_t* data, co
else if (ripRelative)
_appendU32(high, 0);
else if (highDisplacementSize == Disp32Size)
_appendU32(high, static_cast<std::uint32_t>(static_cast<std::int32_t>(highDisplacement)));
_appendU32(high, static_cast<std::uint32_t>(highDisplacement));
high.push_back(kUpperHalf);
if (ripRelative) {
operands.High.RipDisplacementOffset = highDisplacementOffset;
@@ -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};
@@ -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) {
+5
View File
@@ -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.
+2 -2
View File
@@ -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`
+1 -1
View File
@@ -5,7 +5,7 @@
- [sce::Np::CppWebApi::Common](../../../core/libs/prx/libSceNpCppWebApi/Export.cpp) (libSceNpCppWebApi) - the `finish` overrides of `DownStreamTransaction<...>` (2 instantiations) and `Vector<IntrusivePtr<Leaderboards::V1::User>>::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
+2 -7
View File
@@ -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.
+1 -1
View File
@@ -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
+22 -8
View File
@@ -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
+6 -2
View File
@@ -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:
+35
View File
@@ -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())
+36
View File
@@ -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("<I", data)[0]
if magic not in (SELF_MAGIC, SELF_MAGIC_KERNEL):
raise Refused("not a SELF container")
records = read_records(data)
inner, _, headers = parse_elf(data)
except (OSError, ValueError, Refused, struct.error) as error:
parser.error(str(error))
fields = ("index", "type", "flags", "offset", "vaddr", "size", "memory", "align")
print(json.dumps({"size": len(data), "magic": magic, "inner_elf": inner,
"records": [{"flags": flags, "offset": offset, "size": size} for flags, offset, size in records],
"program_headers": [{field: header[field] for field in fields} for header in headers]}, indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())
+10 -1
View File
@@ -75,6 +75,15 @@ def validate(summary):
return summary
def unique_json_object(pairs):
result = {}
for name, value in pairs:
if name in result:
raise ValueError(f"duplicate JSON field: {name!r}")
result[name] = value
return result
def load_summary(path, trace=None):
records = []
with Path(path).open(encoding="utf-8-sig") as source:
@@ -82,7 +91,7 @@ def load_summary(path, trace=None):
if not line.strip():
continue
try:
record = json.loads(line)
record = json.loads(line, object_pairs_hook=unique_json_object)
if not isinstance(record, dict):
raise ValueError("summary must be an object")
except (ValueError, RecursionError) as error:
+3 -1
View File
@@ -111,7 +111,7 @@ class PeModule:
def export_symbols(self, data, rva, size):
directory = self.offset(rva)
_, names, function_rva, name_rva, ordinal_rva = struct.unpack_from("<IIIII", data, directory + 20)
functions, names, function_rva, name_rva, ordinal_rva = struct.unpack_from("<IIIII", data, directory + 20)
function_table = self.offset(function_rva)
result = []
if names:
@@ -119,6 +119,8 @@ class PeModule:
ordinal_table = self.offset(ordinal_rva)
for index in range(names):
ordinal = struct.unpack_from("<H", data, ordinal_table + index * 2)[0]
if ordinal >= functions:
raise ValueError("PE export ordinal is outside the export address table")
address = struct.unpack_from("<I", data, function_table + ordinal * 4)[0]
if rva <= address < rva + size:
continue
+76 -1
View File
@@ -93,7 +93,7 @@ def build_module(tags=DEFAULT_TAGS, values=None, symbols=DEFAULT_SYMBOLS, reloca
def instruction(listing, mnemonic):
pattern = re.compile(r"^\s*[0-9a-f]+:\s+(?:\w+\s+)?%s\b" % mnemonic)
pattern = re.compile(r"^\s*[0-9a-f]+:\s+(?:\w+\s+)*%s\b" % mnemonic)
return next(line for line in listing if pattern.search(line))
@@ -104,6 +104,14 @@ def with_relocation(module, table_offset, index, slot, symbol_index, kind):
class ImportRelocationTests(unittest.TestCase):
def test_symbol_entry_size_must_match_elf64(self):
for size in (None, 0, 16, 32):
with self.subTest(size=size):
tags = tuple(tag for tag in DEFAULT_TAGS if tag != 11 or size is not None)
data = build_module(tags=tags, values={} if size is None else {11: size})
with self.assertRaisesRegex(annotate_module.ModuleError, "DT_SYMENT"):
annotate_module.Module(data)
def test_tables_cannot_read_file_padding_past_the_load_segment(self):
for tag in (5, 6, 23):
with self.subTest(tag=tag):
@@ -261,6 +269,45 @@ class AnnotateModuleTests(unittest.TestCase):
self.annotate(build_module(code=code_bytes(b"\xf2")))
self.assertTrue(instruction(self.read(".asm").splitlines(), "call").endswith("; -> " + 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("<I", (SLOT - address - length) & 0xFFFFFFFF)
code += b"\xe8" + struct.pack("<i", -length - 5) + b"\xc3"
module = bytearray(build_module(code=code))
struct.pack_into("<Q", module, 64 + 16, address)
summary = self.annotate(bytes(module))
self.assertIn("1 imports, 1 exports, 0 tls, 1 stubs", summary)
listing = self.read(".asm").splitlines()
self.assertTrue(instruction(listing, "jmp").endswith("; GOT " + IMPORT_NAME))
self.assertTrue(instruction(listing, "call").endswith("; -> " + 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("<I", (SLOT - address - length) & 0xFFFFFFFF) + b"\xc3"
symbols = DEFAULT_SYMBOLS + [(SECOND_IMPORT_NAME, 0, 0x12, 0)]
module = bytearray(build_module(code=code, symbols=symbols,
relocations=((SLOT, 1), (0x100000000 + SLOT, 3))))
struct.pack_into("<Q", module, 64 + 16, address)
self.annotate(bytes(module))
line = next(line for line in self.read(".asm").splitlines() if "(%eip)" in line)
self.assertTrue(line.endswith("; GOT " + IMPORT_NAME), line)
self.assertNotIn("; GOT " + SECOND_IMPORT_NAME, self.read(".asm"))
def test_segment_relative_operands_do_not_name_flat_import_slots(self):
for segment in (b"\x64", b"\x65"):
for address_prefix in (b"", b"\x67"):
with self.subTest(segment=segment, address_prefix=address_prefix):
summary = self.annotate(build_module(code=code_bytes(segment + address_prefix)))
self.assertIn("0 stubs", summary)
self.assertNotIn("; GOT ", self.read(".asm"))
self.assertNotIn("; -> ", 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("<Q", module, 64 + 3 * 56 + 16, address)
struct.pack_into("<Q", module, 64 + 3 * 56 + 40, memory_size)
if address == 0:
module[64:64 + 4 * 56] = module[64 + 3 * 56:64 + 4 * 56] + module[64:64 + 3 * 56]
summary = self.annotate(bytes(module))
self.assertIn("1 imports, 1 exports, 0 tls, 1 stubs", summary)
listing = self.read(".asm")
self.assertEqual(listing.count("; ===== segment"), 1)
self.assertIn("; ===== segment 0x1000", listing)
self.assertIn("; -> " + 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("<Q", module, 64 + 3 * 56 + 32, 2)
self.assert_fails(bytes(module), "extends past the end of the file")
def test_zero_run_preserves_instructions_and_export_label(self):
code = b"\x90" + bytes(16) + b"\xc3"
address = CODE_ADDRESS + 9
+61 -2
View File
@@ -1,5 +1,6 @@
import contextlib
import io
import locale
import os
import subprocess
import sys
@@ -83,11 +84,48 @@ class AuditDumpTests(unittest.TestCase):
code = audit_dump.main([str(argument) for argument in arguments])
return code, errors.getvalue()
def test_conversion_decodes_both_streams_as_utf8_under_a_legacy_locale(self):
message = "module-\u754c-\U0001f3ae.prx"
payload = message.encode("utf-8")
child = f"import os; os.write(1, {payload!r}); os.write(2, {payload!r})"
run = subprocess.run
def relink(command, **options):
return run([sys.executable, "-c", child], timeout=5, **options)
with mock.patch.object(locale, "getpreferredencoding", return_value="cp1252"), \
mock.patch.object(locale, "getencoding", return_value="cp1252", create=True), \
mock.patch.object(subprocess, "run", side_effect=relink):
result = audit_dump.convert(self.relinker, self.root / "source", self.root / "app.elf", [], False)
self.assertEqual(result.returncode, 0)
self.assertEqual(result.stdout, message)
self.assertEqual(result.stderr, message)
def test_non_utf8_diagnostic_bytes_preserve_the_conversion_failure(self):
payload = b"FAIL: native diagnostic \xff\n"
run = subprocess.run
for stream in (1, 2):
with self.subTest(stream=stream):
child = f"import os, sys; os.write({stream}, {payload!r}); sys.exit(3)"
def relink(command, **options):
return run([sys.executable, "-c", child], timeout=5, **options)
with mock.patch.object(locale, "getpreferredencoding", return_value="utf-8"), \
mock.patch.object(locale, "getencoding", return_value="utf-8", create=True), \
mock.patch.object(subprocess, "run", side_effect=relink), \
mock.patch.object(audit_dump.import_audit, "main") as audit:
code, errors = self.run_main(self.dump, "--relinker", self.relinker, "--libs", "libs")
self.assertEqual(code, 2)
self.assertIn("relinker exited with 3", errors)
self.assertIn("FAIL: native diagnostic \ufffd", errors)
audit.assert_not_called()
def test_the_audit_gets_every_registry_library_and_module_directory(self):
self.module("sce_module/libc.prx")
work = self.root / "work"
def relink(command, capture_output, text):
def relink(command, **options):
output = Path(command[-1]).parent
(output / "app.registry.json").write_text("[]")
(output / "app.libc.prx.guest.prx.registry.json").write_text("[]")
@@ -110,7 +148,7 @@ class AuditDumpTests(unittest.TestCase):
(work / "source" / "sce_module").mkdir(parents=True)
(work / "source" / "sce_module" / "gone.prx").write_bytes(ELF)
def relink(command, capture_output, text):
def relink(command, **options):
(Path(command[-1]).parent / "app.registry.json").write_text("[]")
return subprocess.CompletedProcess(command, 0, "", "")
@@ -143,6 +181,27 @@ class AuditDumpTests(unittest.TestCase):
audit.assert_not_called()
self.assertTrue(stale.exists())
def test_output_directory_creation_failure_stops_before_conversion(self):
work = self.root / "work"
original_mkdir = Path.mkdir
def mkdir(path, *args, **kwargs):
if path == work / "output":
raise PermissionError("permission denied")
return original_mkdir(path, *args, **kwargs)
with mock.patch.object(Path, "mkdir", mkdir), mock.patch.object(audit_dump, "prepare") as prepare, \
mock.patch.object(audit_dump, "convert") as convert, mock.patch.object(audit_dump.import_audit, "main") as audit:
code, errors = self.run_main(self.dump, "--relinker", self.relinker, "--libs", "libs", "--work", work)
self.assertEqual(code, 2)
self.assertIn("cannot create", errors)
self.assertIn(str(work / "output"), errors)
self.assertIn("permission denied", errors)
prepare.assert_not_called()
convert.assert_not_called()
audit.assert_not_called()
self.assertEqual((self.dump / "eboot.bin").read_bytes(), SELF)
@unittest.skipIf(os.name == "nt" or (hasattr(os, "geteuid") and os.geteuid() == 0), "needs POSIX permissions and a non-root user")
def test_a_non_writable_stale_directory_stops_before_conversion(self):
work = self.root / "work"
+51
View File
@@ -0,0 +1,51 @@
import json
import os
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
SCRIPT = 'check_configure_seed.py'
TOOLS = Path(__file__).resolve().parents[1]
class Tests(unittest.TestCase):
def setUp(self):
self.directory = tempfile.TemporaryDirectory()
self.addCleanup(self.directory.cleanup)
self.root = Path(self.directory.name)
def run_tool(self, *args):
return subprocess.run([sys.executable, str(TOOLS / SCRIPT), *map(str, args)], capture_output=True,
encoding="utf-8", timeout=10, env={**os.environ, "PYTHONIOENCODING": "ascii"})
def test_exact_stale_and_missing_are_read_only(self):
cache, seed = self.root / "缓存.txt", self.root / "seed.cmake"
cache.write_text("//Have include stdint.h\nHAVE_STDINT_H:INTERNAL=1\n//User option\nOPT:BOOL=ON\n", encoding="utf-8-sig")
expected = b'set("HAVE_STDINT_H" "1" CACHE INTERNAL "Have include stdint.h")\n'
seed.write_bytes(expected)
before = seed.stat().st_mtime_ns
result = self.run_tool(cache, seed)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(seed.stat().st_mtime_ns, before)
seed.write_bytes(expected.replace(b'"1"', b'"0"'))
before = seed.read_bytes()
self.assertEqual(self.run_tool(cache, seed).returncode, 1)
self.assertEqual(seed.read_bytes(), before)
missing = self.root / "absent" / "seed.cmake"
self.assertEqual(self.run_tool(cache, missing).returncode, 1)
self.assertFalse(missing.parent.exists())
def test_invalid_cache_and_empty_seed(self):
cache, seed = self.root / "cache", self.root / "seed"
cache.write_bytes(b"\xff")
self.assertEqual(self.run_tool(cache, seed).returncode, 2)
self.assertFalse(seed.exists())
cache.write_text("OPT:BOOL=ON\n", encoding="utf-8")
seed.write_bytes(b"")
self.assertEqual(self.run_tool(cache, seed).returncode, 0)
if __name__ == "__main__":
unittest.main()
+61
View File
@@ -0,0 +1,61 @@
import json
import os
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
SCRIPT = 'inspect_self.py'
TOOLS = Path(__file__).resolve().parents[1]
class Tests(unittest.TestCase):
def setUp(self):
self.directory = tempfile.TemporaryDirectory()
self.addCleanup(self.directory.cleanup)
self.root = Path(self.directory.name)
def run_tool(self, *args):
return subprocess.run([sys.executable, str(TOOLS / SCRIPT), *map(str, args)], capture_output=True,
encoding="utf-8", timeout=10, env={**os.environ, "PYTHONIOENCODING": "ascii"})
def fixture(self):
import struct
data = bytearray(0x500)
struct.pack_into("<I", data, 0, 0x1d3d154f)
struct.pack_into("<QQQQ", data, 0x40, 0x800, 0x400, 16, 16)
data[0x180:0x187] = b"\x7fELF\x02\x01\x01"
struct.pack_into("<HHIQQQIHHHHHH", data, 0x190, 0xfe10, 62, 1, 0, 64, 0, 0, 64, 56, 1, 0, 0, 0)
struct.pack_into("<IIQQQQQQ", data, 0x1c0, 1, 5, 0x9000, 0x1000, 0, 16, 32, 0x1000)
return data
def test_reports_original_offsets_and_records_without_writes(self):
source = self.root / "模块.self"
data = self.fixture()
source.write_bytes(data)
before = source.stat().st_mtime_ns
result = self.run_tool(source)
self.assertEqual(result.returncode, 0, result.stderr)
report = json.loads(result.stdout)
self.assertEqual(report, {"size": 0x500, "magic": 0x1d3d154f, "inner_elf": 0x180,
"records": [{"flags": 0x800, "offset": 0x400, "size": 16}],
"program_headers": [{"index": 0, "type": 1, "flags": 5, "offset": 0x9000,
"vaddr": 0x1000, "size": 16, "memory": 32, "align": 0x1000}]})
self.assertEqual(source.read_bytes(), data)
self.assertEqual(source.stat().st_mtime_ns, before)
self.assertEqual(list(self.root.iterdir()), [source])
def test_rejects_malformed_metadata(self):
source = self.root / "bad.self"
valid = self.fixture()
for data in (b"", bytes(0x500), valid[:0x300], valid[:0x184] + bytes([1]) + valid[0x185:]):
source.write_bytes(data)
result = self.run_tool(source)
self.assertEqual(result.returncode, 2, result.stdout)
self.assertEqual(result.stdout, "")
self.assertNotIn("Traceback", result.stderr)
if __name__ == "__main__":
unittest.main()
+17
View File
@@ -245,6 +245,23 @@ class PerformanceCompareTests(unittest.TestCase):
self.assertEqual(result.stdout, "")
self.assertNotIn("Traceback", result.stderr)
def test_duplicate_json_fields_are_rejected_at_every_object_level(self):
record = json.dumps(self.baseline)
duplicates = (
(record.replace('"commit": "abc123"', '"commit": "wrong", "commit": "abc123"', 1), "commit"),
(record.replace('"spec": "frames 600:4"', '"spec": "seconds 0:1", "spec": "frames 600:4"', 1), "spec"),
(record.replace('"Driver.Packet": 12.0', '"Driver.Packet": 1.0, "Driver.Packet": 12.0', 1), "Driver.Packet"),
)
for content, field in duplicates:
with self.subTest(field=field):
self.before.write_text(content + "\n", encoding="utf-8")
original = self.before.read_bytes()
with self.assertRaises(ValueError) as raised:
performance_compare.load_summary(self.before)
self.assertIn(f"before.jsonl:1: duplicate JSON field: {field!r}", str(raised.exception))
self.assertIsInstance(raised.exception.__cause__, ValueError)
self.assertEqual(self.before.read_bytes(), original)
def test_percentage_overflow_is_an_error(self):
self.baseline["gpu_busy_ms_per_present"] = 1e-308
self.candidate["gpu_busy_ms_per_present"] = 1e308
+13
View File
@@ -93,6 +93,19 @@ class PerformanceCsvTests(unittest.TestCase):
self.assertEqual(result.stdout, b"")
self.assertNotIn(b"Traceback", result.stderr)
def test_duplicate_json_field_does_not_emit_an_ambiguous_csv(self):
baseline = json.dumps(self.before).replace(
'"commit": "abc123"', '"commit": "wrong", "commit": "abc123"', 1)
self.baseline.write_text(baseline + "\n", encoding="utf-8")
self.candidate.write_text(json.dumps(self.after) + "\n", encoding="utf-8")
result = subprocess.run(
[sys.executable, str(Path(performance_csv.__file__)), str(self.baseline), str(self.candidate)],
capture_output=True, timeout=5)
self.assertEqual(result.returncode, 2)
self.assertEqual(result.stdout, b"")
self.assertIn(b"duplicate JSON field: 'commit'", result.stderr)
self.assertNotIn(b"Traceback", result.stderr)
def test_trace_selection_uses_the_existing_jsonl_selection_contract(self):
self.baseline.write_text(json.dumps(self.before) + "\n" + json.dumps(self.after) + "\n", encoding="utf-8")
self.candidate.write_bytes(self.baseline.read_bytes())
+13
View File
@@ -317,6 +317,19 @@ class SymbolizeSamplesTests(unittest.TestCase):
self.assertEqual(module.name(0x1030), "~first_func")
self.assertEqual(module.name(0x1070), "~second_func")
def test_export_ordinals_stay_inside_the_declared_address_table(self):
path = self.libs / "libwinpthread-1.dll"
data = bytearray(path.read_bytes())
struct.pack_into("<I", data, 0x600 + 20, 1)
path.write_bytes(data)
with self.assertRaisesRegex(ValueError, "export ordinal"):
symbolize_samples.PeModule(path)
warnings = []
symbolizer = symbolize_samples.Symbolizer([self.libs], {}, unverified=True, warn=warnings.append)
frame = "libwinpthread-1.dll+0x1070"
self.assertEqual(symbolizer.frame(frame), frame)
self.assertTrue(any("export ordinal" in warning for warning in warnings))
def test_frames_keep_unknown_modules_and_out_of_range_addresses(self):
symbolizer = symbolize_samples.Symbolizer([self.libs], {}, warn=lambda text: None, unverified=True)
self.assertEqual(symbolizer.frame("libSceAgcDriver.prx+0x1046"), "libSceAgcDriver.prx!AgcDriver_long_function_name")