mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-05 13:20:11 +00:00
Merge remote-tracking branch 'origin/main' into fix/json2-refresh-20261030
This commit is contained in:
commit
fd37cb7143
47 files changed
+1138
-98
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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' }}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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("--"))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,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`
|
||||
|
||||
@@ -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,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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in new issue
Block a user