Merge remote-tracking branch 'origin/main' into feat/demons-souls-boot
Brings in main up to 9958d26 (homebrew runtime, POSIX wrappers in
libkernel, sce_module guest modules and --skip-sce-module, the --to-intel
general pipeline, libSceRandom, libSceRtc, libScePosixForWebKit,
libRenoirCore, test wiring).
Resolutions where both sides implemented the same thing:
- libkernel pthread: main's structure (lazy static initialisation,
destroyed markers, MutexOperations/CondOperations, mutex protocol and
condattr clock) with this branch's TimedWait deadlines and condition
(precise timed lock/wait on Windows), wait tracing, the adaptive mutex
type 4, POSIX default ERRORCHECK mutexes, EDEADLK/EPERM results instead
of throws, and condition clocks honoured for absolute timeouts.
- libc locale: the guest Dinkumware ABI (_Locimp, facet vtables, short
ctype/case tables, locale ids, stream storage) is kept because guest code
inlines those layouts; main's C character functions are kept on top.
- libc stdio/strings/math: main's stream functions (guest FILE status
sync), strerror/perror, remove, formatting, string and math exports win
over this branch's duplicates.
- libc paths: main's working directory with this branch's path aliases
resolved on the normalised guest path.
- libkernel file: this branch's sceKernel* results as SCE errors, main's
POSIX wrappers and both fstat helpers.
- relinker: this branch's stub lowering keeps its trampolines through the
main executable path; guest modules refuse lowerings that need stubs.
- AGC driver: main's rendering wait is executed by this branch's workers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177gthrWEBBfBu5xJDXgBfo
This commit is contained in:
commit
a503d0b617
|
|
@ -28,7 +28,10 @@ set(SDL_HAPTIC OFF CACHE BOOL "" FORCE)
|
|||
set(SDL_HIDAPI OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_SENSOR OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_STATIC_PIC ON CACHE BOOL "" FORCE)
|
||||
set(ANYPS5_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "")
|
||||
add_subdirectory(3rdparty/SDL2)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${ANYPS5_EXE_LINKER_FLAGS}")
|
||||
|
||||
add_subdirectory(core/shader/recompiler)
|
||||
add_subdirectory(core/libs)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Releases will be published after the first full successful launch of at least on
|
|||
|
||||
Execution reaches `_start`, [stack unwinding](core/libs/prx/libc/src/exception/Unwind.cpp) and exception handling tables are built, reaches main. Unsupported or unexpected states strictly throw `std::runtime_error`. `what()` is printed to stderr and the process terminates.
|
||||
|
||||
The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully produces validated via [Spirv-Tools](3rdparty/SPIRV-Tools) SPIR-V.
|
||||
The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully produces SPIR-V, validated via [Spirv-Tools](3rdparty/SPIRV-Tools) when built with `ANYPS5_ENABLE_SPIRV_TOOLS`.
|
||||
|
||||
The real game reaches the logo, main menu, and [gameplay](https://gist.github.com/user-attachments/assets/81d28e9b-c237-4545-b2ca-720071129816) with audio.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
if(NOT WIN32)
|
||||
string(REPLACE "-static " "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ")
|
||||
string(STRIP "${CMAKE_EXE_LINKER_FLAGS}" CMAKE_EXE_LINKER_FLAGS)
|
||||
endif()
|
||||
|
||||
add_executable(nid_patcher
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/nid/src/Sha1.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/nid/src/NidCompute.cpp
|
||||
|
|
@ -103,6 +108,255 @@ endforeach()
|
|||
|
||||
add_custom_target(libs DEPENDS ${PATCHED_OUTPUTS})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_executable(guest_rendering_wait_tests tests/GuestRenderingWait.cpp)
|
||||
target_include_directories(guest_rendering_wait_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_rendering_wait_tests PRIVATE libSceAgcDriver libc)
|
||||
configure_windows_unwind(guest_rendering_wait_tests)
|
||||
add_test(NAME guest_rendering_wait COMMAND guest_rendering_wait_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_rendering_wait PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_buffer_reuse_tests tests/GuestBufferReuse.cpp)
|
||||
target_include_directories(guest_buffer_reuse_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_buffer_reuse_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_buffer_reuse_tests)
|
||||
add_test(NAME guest_buffer_reuse COMMAND guest_buffer_reuse_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_buffer_reuse PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_shader_link_tests tests/GuestShaderLink.cpp)
|
||||
target_include_directories(guest_shader_link_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_shader_link_tests PRIVATE libSceAgc libc)
|
||||
configure_windows_unwind(guest_shader_link_tests)
|
||||
add_test(NAME guest_shader_link COMMAND guest_shader_link_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_shader_link PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_system_exit_tests tests/GuestSystemExit.cpp)
|
||||
target_include_directories(guest_system_exit_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_system_exit_tests PRIVATE libSceSystemService libc)
|
||||
configure_windows_unwind(guest_system_exit_tests)
|
||||
add_test(NAME guest_system_exit_parameters COMMAND guest_system_exit_tests)
|
||||
add_test(NAME guest_system_exit COMMAND guest_system_exit_tests --exit)
|
||||
add_test(NAME guest_immediate_exit COMMAND guest_system_exit_tests --immediate)
|
||||
set_tests_properties(guest_system_exit PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "Guest shutdown and atexit completed")
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_system_exit guest_system_exit_parameters guest_immediate_exit PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_character_tests tests/GuestCharacter.cpp)
|
||||
target_include_directories(guest_character_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_character_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_character_tests)
|
||||
add_test(NAME guest_character COMMAND guest_character_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_character PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_math_tests tests/GuestMath.cpp)
|
||||
target_include_directories(guest_math_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_math_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_math_tests)
|
||||
add_test(NAME guest_math COMMAND guest_math_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_math PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_working_directory_tests tests/GuestWorkingDirectory.cpp)
|
||||
target_include_directories(guest_working_directory_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_working_directory_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_working_directory_tests)
|
||||
add_test(NAME guest_working_directory COMMAND guest_working_directory_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_working_directory PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_library(guest_module_fixture SHARED tests/GuestModuleFixture.cpp)
|
||||
target_include_directories(guest_module_fixture PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_custom_command(TARGET guest_module_fixture POST_BUILD
|
||||
COMMAND $<TARGET_FILE:nid_patcher> guest_module_fixture $<TARGET_FILE:guest_module_fixture>
|
||||
VERBATIM)
|
||||
add_dependencies(guest_module_fixture nid_patcher)
|
||||
add_executable(guest_dynamic_loader_tests tests/GuestDynamicLoader.cpp)
|
||||
target_include_directories(guest_dynamic_loader_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_dynamic_loader_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_dynamic_loader_tests)
|
||||
add_dependencies(guest_dynamic_loader_tests guest_module_fixture)
|
||||
add_test(NAME guest_dynamic_loader COMMAND guest_dynamic_loader_tests $<TARGET_FILE_NAME:guest_module_fixture>)
|
||||
set_tests_properties(guest_dynamic_loader PROPERTIES
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:guest_module_fixture>")
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_dynamic_loader PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_executable(guest_filesystem_tests tests/GuestFilesystem.cpp)
|
||||
target_include_directories(guest_filesystem_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_filesystem_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_filesystem_tests)
|
||||
add_test(NAME guest_filesystem COMMAND guest_filesystem_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_filesystem PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_directory_tests tests/GuestDirectory.cpp)
|
||||
target_include_directories(guest_directory_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_directory_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_directory_tests)
|
||||
add_test(NAME guest_directory COMMAND guest_directory_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_directory PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_environment_tests tests/GuestEnvironment.cpp)
|
||||
target_include_directories(guest_environment_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_environment_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_environment_tests)
|
||||
add_test(NAME guest_environment COMMAND guest_environment_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_environment PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_standard_stream_tests tests/GuestStandardStreams.cpp)
|
||||
target_include_directories(guest_standard_stream_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_standard_stream_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_standard_stream_tests)
|
||||
add_test(NAME guest_standard_streams COMMAND guest_standard_stream_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_standard_streams PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_once_tests tests/GuestOnce.cpp)
|
||||
target_include_directories(guest_once_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_once_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_once_tests)
|
||||
add_test(NAME guest_once COMMAND guest_once_tests)
|
||||
set_tests_properties(guest_once PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_once PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_executable(guest_signal_tests tests/GuestSignals.cpp)
|
||||
target_include_directories(guest_signal_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_signal_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_signal_tests)
|
||||
add_test(NAME guest_signals COMMAND guest_signal_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_signals PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_executable(guest_system_configuration_tests tests/GuestSystemConfiguration.cpp)
|
||||
target_include_directories(guest_system_configuration_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_system_configuration_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_system_configuration_tests)
|
||||
add_test(NAME guest_system_configuration COMMAND guest_system_configuration_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_system_configuration PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_executable(guest_udp_tests tests/GuestUdp.cpp)
|
||||
target_include_directories(guest_udp_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_udp_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_udp_tests)
|
||||
add_test(NAME guest_udp COMMAND guest_udp_tests)
|
||||
set_tests_properties(guest_udp PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_udp PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_executable(guest_address_tests tests/GuestAddressConversion.cpp)
|
||||
target_include_directories(guest_address_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_address_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_address_tests)
|
||||
add_test(NAME guest_addresses COMMAND guest_address_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_addresses PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_executable(guest_error_tests tests/GuestErrors.cpp)
|
||||
target_include_directories(guest_error_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_error_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_error_tests)
|
||||
add_test(NAME guest_errors COMMAND guest_error_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_errors PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_random_tests tests/GuestRandom.cpp)
|
||||
target_include_directories(guest_random_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_random_tests PRIVATE libSceRandom)
|
||||
add_test(NAME guest_random COMMAND guest_random_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_random PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceRandom>")
|
||||
endif()
|
||||
add_executable(guest_resolver_tests tests/GuestResolver.cpp)
|
||||
target_include_directories(guest_resolver_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_resolver_tests PRIVATE libScePosixForWebKit)
|
||||
add_test(NAME guest_resolver COMMAND guest_resolver_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_resolver PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libScePosixForWebKit>")
|
||||
endif()
|
||||
add_executable(guest_rtc_tests tests/GuestRtc.cpp)
|
||||
target_include_directories(guest_rtc_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_rtc_tests PRIVATE libSceRtc)
|
||||
add_test(NAME guest_rtc COMMAND guest_rtc_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_rtc PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceRtc>")
|
||||
endif()
|
||||
add_executable(mspace_tests tests/Mspace.cpp)
|
||||
target_include_directories(mspace_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(mspace_tests PRIVATE libc)
|
||||
configure_windows_unwind(mspace_tests)
|
||||
add_test(NAME mspace COMMAND mspace_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(mspace PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_memory_tests tests/GuestMemory.cpp)
|
||||
target_include_directories(guest_memory_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_memory_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_memory_tests)
|
||||
add_test(NAME guest_memory COMMAND guest_memory_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_memory PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_executable(guest_search_tests tests/GuestSearch.cpp)
|
||||
target_include_directories(guest_search_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_search_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_search_tests)
|
||||
add_test(NAME guest_search COMMAND guest_search_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_search PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(guest_string_tests tests/GuestStrings.cpp)
|
||||
target_include_directories(guest_string_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_string_tests PRIVATE libc libScePosixForWebKit)
|
||||
configure_windows_unwind(guest_string_tests)
|
||||
add_test(NAME guest_strings COMMAND guest_string_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_strings PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
add_executable(exception_runtime_tests tests/ExceptionRuntime.cpp)
|
||||
target_include_directories(exception_runtime_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(exception_runtime_tests PRIVATE libc)
|
||||
configure_windows_unwind(exception_runtime_tests)
|
||||
add_test(NAME exception_runtime COMMAND exception_runtime_tests)
|
||||
if(WIN32)
|
||||
set_tests_properties(exception_runtime PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_executable(application_heap_tests EXCLUDE_FROM_ALL tests/ApplicationHeap.cpp prx/libc/src/ApplicationHeap.cpp)
|
||||
target_include_directories(application_heap_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set_target_properties(application_heap_tests PROPERTIES CXX_EXTENSIONS OFF)
|
||||
|
|
@ -115,11 +369,6 @@ set_target_properties(guest_locale_tests PROPERTIES CXX_EXTENSIONS OFF)
|
|||
target_link_libraries(guest_locale_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_locale_tests)
|
||||
|
||||
add_executable(guest_rtc_tests EXCLUDE_FROM_ALL tests/GuestRtc.cpp)
|
||||
target_include_directories(guest_rtc_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set_target_properties(guest_rtc_tests PROPERTIES CXX_EXTENSIONS OFF)
|
||||
target_link_libraries(guest_rtc_tests PRIVATE libSceRtc)
|
||||
|
||||
if(MINGW)
|
||||
foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static)
|
||||
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
|
||||
|
|
|
|||
|
|
@ -173,9 +173,9 @@ using PthreadCondattr = PthreadCondattrPrivate*;
|
|||
using PthreadKey = int;
|
||||
using pthread_entry_func_t = void* (*)(void*);
|
||||
using pthread_key_destructor_func_t = void (*)(void*);
|
||||
using thread_dtors_func_t = void (*)();
|
||||
using get_thread_atexit_count_func_t = int (*)(KernelModule);
|
||||
using thread_atexit_report_func_t = void (*)(KernelModule);
|
||||
using thread_dtors_func_t = void (APS5_VABI *)();
|
||||
using get_thread_atexit_count_func_t = int (APS5_VABI *)(KernelModule);
|
||||
using thread_atexit_report_func_t = void (APS5_VABI *)(KernelModule);
|
||||
|
||||
struct FileStat {
|
||||
std::uint32_t st_dev;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ inline std::string StripNidPostfix(const std::string& name) {
|
|||
std::string result = name;
|
||||
if (result.size() >= kNidPostfixLen &&
|
||||
result.compare(result.size() - kNidPostfixLen, kNidPostfixLen, kNidPostfix) == 0) {
|
||||
result = result.substr(0u, result.size() - kNidPostfixLen);
|
||||
result.resize(result.size() - kNidPostfixLen);
|
||||
}
|
||||
const auto disambigPos = result.rfind(kNidDisambigMarker);
|
||||
if (disambigPos != std::string::npos) {
|
||||
|
|
@ -54,7 +54,7 @@ inline std::string StripNidPostfix(const std::string& name) {
|
|||
[](unsigned char c) { return std::isdigit(c) != 0; }
|
||||
);
|
||||
if (allDigits && suffixStart < result.size())
|
||||
result = result.substr(0u, disambigPos);
|
||||
result.resize(disambigPos);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
#include <cstdint>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
APS5_EXPORT("0DsDTr534gc", renoirStub00);
|
||||
uint64_t APS5_VABI renoirStub00(void) {
|
||||
NotImplemented_nid_no_patch("0DsDTr534gc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("2T56HgbPW08", renoirStub01);
|
||||
uint64_t APS5_VABI renoirStub01(void) {
|
||||
NotImplemented_nid_no_patch("2T56HgbPW08");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("3HnCozh8Az8", renoirStub02);
|
||||
uint64_t APS5_VABI renoirStub02(void) {
|
||||
NotImplemented_nid_no_patch("3HnCozh8Az8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("5Ysce+s-CWU", renoirStub03);
|
||||
uint64_t APS5_VABI renoirStub03(void) {
|
||||
NotImplemented_nid_no_patch("5Ysce+s-CWU");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("AdlSqA4lGS8", renoirStub04);
|
||||
uint64_t APS5_VABI renoirStub04(void) {
|
||||
NotImplemented_nid_no_patch("AdlSqA4lGS8");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("bC37hk7rHqs", renoirStub05);
|
||||
uint64_t APS5_VABI renoirStub05(void) {
|
||||
NotImplemented_nid_no_patch("bC37hk7rHqs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("dfPK9R6M0qM", renoirStub06);
|
||||
uint64_t APS5_VABI renoirStub06(void) {
|
||||
NotImplemented_nid_no_patch("dfPK9R6M0qM");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("fZiBbVa2ZDU", renoirStub07);
|
||||
uint64_t APS5_VABI renoirStub07(void) {
|
||||
NotImplemented_nid_no_patch("fZiBbVa2ZDU");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("hkWBH8-WfW4", renoirStub08);
|
||||
uint64_t APS5_VABI renoirStub08(void) {
|
||||
NotImplemented_nid_no_patch("hkWBH8-WfW4");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("hUxvGdi64Io", renoirStub09);
|
||||
uint64_t APS5_VABI renoirStub09(void) {
|
||||
NotImplemented_nid_no_patch("hUxvGdi64Io");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("NxqsZxGisrA", renoirStub10);
|
||||
uint64_t APS5_VABI renoirStub10(void) {
|
||||
NotImplemented_nid_no_patch("NxqsZxGisrA");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("OgZVIkaNmb0", renoirStub11);
|
||||
uint64_t APS5_VABI renoirStub11(void) {
|
||||
NotImplemented_nid_no_patch("OgZVIkaNmb0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("ojLQdRUnVgA", renoirStub12);
|
||||
uint64_t APS5_VABI renoirStub12(void) {
|
||||
NotImplemented_nid_no_patch("ojLQdRUnVgA");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("s0qDfgsAgVg", renoirStub13);
|
||||
uint64_t APS5_VABI renoirStub13(void) {
|
||||
NotImplemented_nid_no_patch("s0qDfgsAgVg");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("sgrd-r1Nw60", renoirStub14);
|
||||
uint64_t APS5_VABI renoirStub14(void) {
|
||||
NotImplemented_nid_no_patch("sgrd-r1Nw60");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("sjTRb8YD7dQ", renoirStub15);
|
||||
uint64_t APS5_VABI renoirStub15(void) {
|
||||
NotImplemented_nid_no_patch("sjTRb8YD7dQ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("tG86XGc2VtE", renoirStub16);
|
||||
uint64_t APS5_VABI renoirStub16(void) {
|
||||
NotImplemented_nid_no_patch("tG86XGc2VtE");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("umlGxOcNzgc", renoirStub17);
|
||||
uint64_t APS5_VABI renoirStub17(void) {
|
||||
NotImplemented_nid_no_patch("umlGxOcNzgc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("vPxA9OFW0os", renoirStub18);
|
||||
uint64_t APS5_VABI renoirStub18(void) {
|
||||
NotImplemented_nid_no_patch("vPxA9OFW0os");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("X2qEBlOLkNo", renoirStub19);
|
||||
uint64_t APS5_VABI renoirStub19(void) {
|
||||
NotImplemented_nid_no_patch("X2qEBlOLkNo");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("ynh4E+BIaqY", renoirStub20);
|
||||
uint64_t APS5_VABI renoirStub20(void) {
|
||||
NotImplemented_nid_no_patch("ynh4E+BIaqY");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("YXe6ABbHZPs", renoirStub21);
|
||||
uint64_t APS5_VABI renoirStub21(void) {
|
||||
NotImplemented_nid_no_patch("YXe6ABbHZPs");
|
||||
return 0;
|
||||
}
|
||||
|
||||
APS5_EXPORT("z0jsXZVz75I", renoirStub22);
|
||||
uint64_t APS5_VABI renoirStub22(void) {
|
||||
NotImplemented_nid_no_patch("z0jsXZVz75I");
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ add_library(libSceAgc SHARED EXCLUDE_FROM_ALL
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/Predication/src/Predication.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/CreateShader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/InterpolantMapping.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/LinkShaders.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/PrimState.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/ShaderUtils.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ std::uint32_t* APS5_VABI sceAgcCbReleaseMem(CommandBuffer* buf, std::uint8_t act
|
|||
} else if (dataSelect == 5) {
|
||||
Agc::Command::Require(data == 0, __func__, "GDS release cannot use immediate data");
|
||||
value = gdsOffset | (static_cast<std::uint64_t>(gdsSize) << 16u);
|
||||
} else {
|
||||
Agc::Command::Require(gdsOffset == 0 && gdsSize <= 1, __func__, "GDS parameters supplied for a non-GDS release");
|
||||
}
|
||||
// A null label is accepted: games emit releases whose destination is never read.
|
||||
if (dataSelect != 0 && interrupt != 4 && guestAddress != 0) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
void* APS5_VABI sceAgcGetRegisterDefaults() {
|
||||
// The legacy API has no version argument; use the baseline public table.
|
||||
return Agc::Command::GetRegisterDefaults(0, false, __func__);
|
||||
}
|
||||
|
||||
void* APS5_VABI sceAgcGetRegisterDefaults2(std::uint32_t version) {
|
||||
return Agc::Command::GetRegisterDefaults(version, false, __func__);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
#include "SceShaders.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "prx/libSceAgc/Shader/include/ShaderConstants.hpp"
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
|
||||
extern "C" int APS5_VABI sceAgcCreatePrimState(ShaderRegister*, ShaderRegister*, const Shader*, const Shader*, std::uint32_t);
|
||||
extern "C" int APS5_VABI sceAgcCreateInterpolantMapping(ShaderRegister*, const Shader*, const Shader*);
|
||||
|
||||
extern "C" int APS5_VABI sceAgcLinkShaders(ShaderRegister* context, ShaderRegister* primitive,
|
||||
const void* reserved, const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType) {
|
||||
using namespace ShaderRegs;
|
||||
if (!context || !primitive || reserved || !vertex ||
|
||||
vertex->type != static_cast<std::uint8_t>(ShaderBinaryType::Gs) ||
|
||||
(vertex->num_output_semantics && !vertex->output_semantics) ||
|
||||
(pixel && (pixel->type != static_cast<std::uint8_t>(ShaderBinaryType::Ps) ||
|
||||
pixel->num_input_semantics > 32 ||
|
||||
(pixel->num_input_semantics && !pixel->input_semantics))))
|
||||
return GRAPHICS5_ERROR_INVALID_SHADER_PROGRAM;
|
||||
|
||||
// The simple vertex/pixel pipeline combines two primitive context registers
|
||||
// and 32 interpolant registers. Publish only after both helpers succeed.
|
||||
std::array<ShaderRegister, 34> contextValues{};
|
||||
std::array<ShaderRegister, 3> primitiveValues{};
|
||||
sceAgcCreatePrimState(contextValues.data(), primitiveValues.data(), nullptr, vertex, primitiveType);
|
||||
sceAgcCreateInterpolantMapping(contextValues.data() + 2, vertex, pixel);
|
||||
std::copy(contextValues.begin(), contextValues.end(), context);
|
||||
std::copy(primitiveValues.begin(), primitiveValues.end(), primitive);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -218,6 +218,8 @@ add_executable(agc_driver_graphics_tests EXCLUDE_FROM_ALL
|
|||
tests/GuestTextureResource.cpp
|
||||
tests/GuestSamplerResource.cpp
|
||||
tests/TextureDetiler.cpp
|
||||
Graphics/src/Draw.cpp
|
||||
Graphics/src/Pipeline.cpp
|
||||
Graphics/src/State.cpp
|
||||
Graphics/src/ColorTargetLayout.cpp
|
||||
Graphics/src/ColorTargetTransfer.cpp
|
||||
|
|
@ -253,6 +255,7 @@ target_include_directories(agc_driver_graphics_tests PRIVATE
|
|||
)
|
||||
|
||||
target_link_libraries(agc_driver_graphics_tests PRIVATE
|
||||
shader_recompiler_agc_driver
|
||||
libc
|
||||
)
|
||||
|
||||
|
|
@ -278,6 +281,18 @@ target_link_libraries(agc_driver_bda_device_tests PRIVATE SDL2-static libc shade
|
|||
set_target_properties(agc_driver_bda_device_tests PROPERTIES CXX_EXTENSIONS OFF)
|
||||
configure_windows_unwind(agc_driver_bda_device_tests)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
foreach(agcTest IN ITEMS agc_driver_tests agc_shader_memory_tests agc_driver_flip_tests agc_driver_pm4_tests)
|
||||
string(REGEX REPLACE "_tests$" "" agcTestName "${agcTest}")
|
||||
set_target_properties(${agcTest} PROPERTIES EXCLUDE_FROM_ALL OFF)
|
||||
add_test(NAME ${agcTestName} COMMAND ${agcTest})
|
||||
if(WIN32)
|
||||
set_tests_properties(${agcTestName} PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
option(AGC_BUILD_VISUAL_TEST "Enable the standalone AGC SPIR-V visual test" OFF)
|
||||
|
||||
if(AGC_BUILD_VISUAL_TEST)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace AgcDriver {
|
||||
|
||||
|
|
@ -11,6 +12,15 @@ class FrameTiming;
|
|||
|
||||
inline constexpr std::uint32_t FlipPacketHeader = 0xc004105cu;
|
||||
inline constexpr std::uint32_t FlipPacketWords = 6;
|
||||
// AnyPS5's internal encoding for the driver-generated rendering wait.
|
||||
inline constexpr std::uint32_t RenderingWaitPacketHeader = 0xc0021018u;
|
||||
inline constexpr std::uint32_t RenderingWaitPacketWords = 4;
|
||||
|
||||
class IRenderingWait {
|
||||
public:
|
||||
virtual ~IRenderingWait() = default;
|
||||
virtual void Wait() = 0;
|
||||
};
|
||||
|
||||
struct FlipInfo {
|
||||
std::uint32_t handle;
|
||||
|
|
@ -30,6 +40,9 @@ class IVideoOutput {
|
|||
public:
|
||||
virtual ~IVideoOutput() = default;
|
||||
virtual std::shared_ptr<IFlipRequest> Reserve(const FlipInfo& info) = 0;
|
||||
virtual std::shared_ptr<IRenderingWait> CaptureRenderingWait(std::uint32_t index) {
|
||||
throw std::runtime_error("VideoOut: rendering waits are unsupported by this output");
|
||||
}
|
||||
virtual void Fail(std::exception_ptr error) noexcept = 0;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ struct Submission {
|
|||
// copies it first when a submission still holds it).
|
||||
std::shared_ptr<const ShaderRegistry> shaders;
|
||||
std::map<std::size_t, std::shared_ptr<IFlipRequest>> flips;
|
||||
std::map<std::size_t, std::shared_ptr<IRenderingWait>> renderingWaits;
|
||||
bool suspend = false;
|
||||
// Record-order stamp (Driver::eventSerial) taken when the game submitted: a WAIT_REG_MEM of this
|
||||
// submission trusts only labels the recorder noted with a newer stamp (see Recorder::NoteLabel).
|
||||
|
|
@ -508,6 +509,13 @@ public:
|
|||
require(accepted != std::numeric_limits<std::uint64_t>::max(), "submission serial overflow");
|
||||
for (std::size_t cursor = 0; cursor < submission.commands.size();) {
|
||||
const auto* words = submission.commands.data() + cursor;
|
||||
if (words[0] == RenderingWaitPacketHeader) {
|
||||
const auto output = outputs.find(words[1]);
|
||||
require(output != outputs.end(), "rendering wait references an unregistered video output");
|
||||
auto wait = output->second->CaptureRenderingWait(words[2]);
|
||||
require(wait != nullptr, "video output returned a null rendering wait");
|
||||
submission.renderingWaits.emplace(cursor, std::move(wait));
|
||||
}
|
||||
if (words[0] == FlipPacketHeader) {
|
||||
const auto output = outputs.find(words[1]);
|
||||
require(output != outputs.end(), "flip references an unregistered video output");
|
||||
|
|
@ -5870,7 +5878,7 @@ private:
|
|||
}
|
||||
traceLabel(packet, submission.queue);
|
||||
// Waits do not count as progress; every other packet does, including while it runs.
|
||||
const bool waitPacket = opcode == 0x3c || opcode == 0x93;
|
||||
const bool waitPacket = opcode == 0x3c || opcode == 0x93 || header == RenderingWaitPacketHeader;
|
||||
struct Progress {
|
||||
Driver& driver;
|
||||
bool counted;
|
||||
|
|
@ -5882,7 +5890,9 @@ private:
|
|||
} progress{*this, !waitPacket};
|
||||
if (!waitPacket) ++packetsInFlight;
|
||||
recent.Record(cursor);
|
||||
if (header == FlipPacketHeader) {
|
||||
if (header == RenderingWaitPacketHeader) {
|
||||
timed(&WorkerProfile::waitMs, [&] { submission.renderingWaits.at(cursor)->Wait(); });
|
||||
} else if (header == FlipPacketHeader) {
|
||||
CheckFailure();
|
||||
std::uint64_t batchesAtFlip = 0, unsignaledAtFlip = 0;
|
||||
if (!drains) {
|
||||
|
|
|
|||
|
|
@ -131,8 +131,7 @@ std::string_view UnsupportedReason(std::uint32_t header) {
|
|||
const auto opcode = (header >> 8u) & 0xffu;
|
||||
if (opcode == 0x10) {
|
||||
switch ((header >> 2u) & 0x3fu) {
|
||||
case 0: case 0x09: case 0x0b: case 0x0c: case 0x17: case 0x1a: return {};
|
||||
case 0x06: return "VideoOut buffer-completion wait interface is not implemented";
|
||||
case 0: case 0x06: case 0x09: case 0x0b: case 0x0c: case 0x17: case 0x1a: return {};
|
||||
case 0x14: case 0x18: return "guest cache actions and GPU release events are not implemented";
|
||||
default: return "custom packet has no implemented contract in the reference dispatch table";
|
||||
}
|
||||
|
|
@ -187,6 +186,7 @@ void Validate(std::span<const std::uint32_t> packet, std::uint32_t queue) {
|
|||
require((packet[1] & 0xffff0000u) != 0x68750000u, "typed user-data and legacy flip markers are not implemented");
|
||||
break;
|
||||
case 0x09: size(2); break;
|
||||
case 0x06: graphics(); size(4); require(packet[3] == 0, "unsupported rendering wait mode"); break;
|
||||
case 0x0b: {
|
||||
const auto data = std::as_bytes(packet.subspan(1));
|
||||
require(std::find(data.begin(), data.end(), std::byte{}) != data.end(), "unterminated marker text");
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
#include "prx/libSceAgcDriver/Submit/include/Dcb.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/VideoOutput.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
|
@ -8,6 +12,22 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcDriverGetWaitRenderingPacketSizeInDwords() {
|
||||
return AgcDriver::RenderingWaitPacketWords;
|
||||
}
|
||||
|
||||
std::uint32_t APS5_VABI sceAgcDriverWaitUntilSafeForRendering(std::uint32_t** command,
|
||||
std::uint32_t capacity, std::uint32_t mode, std::uint32_t handle, int index) {
|
||||
if (!command || capacity < AgcDriver::RenderingWaitPacketWords || mode != 0 || index < 0)
|
||||
throw std::invalid_argument("AGC driver: invalid rendering wait arguments");
|
||||
AgcDriver::GuestMemory::CheckRange(command, sizeof(*command), alignof(std::uint32_t*), true);
|
||||
AgcDriver::GuestMemory::CheckRange(*command, AgcDriver::RenderingWaitPacketWords * sizeof(std::uint32_t), alignof(std::uint32_t), true);
|
||||
const std::uint32_t words[] = {AgcDriver::RenderingWaitPacketHeader, handle, static_cast<std::uint32_t>(index), mode};
|
||||
std::copy(std::begin(words), std::end(words), *command);
|
||||
*command += AgcDriver::RenderingWaitPacketWords;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceAgcDriverSubmitDcb(const Packet* packet) {
|
||||
AgcDriver::Submit(packet, 0);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,9 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/HeapDiagnostics.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
|
||||
void APS5_VABI sceLibcHeapGetTraceInfo_nid_postfix(Info* info) {
|
||||
(void)info;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
LibcHeapTraceInfo_nid_no_patch(info);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
add_library(libScePosixForWebKit SHARED EXCLUDE_FROM_ALL Export.cpp Resolver.cpp)
|
||||
target_include_directories(libScePosixForWebKit PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
if(WIN32)
|
||||
target_link_libraries(libScePosixForWebKit PRIVATE ws2_32)
|
||||
endif()
|
||||
configure_windows_unwind(libScePosixForWebKit)
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cctype>
|
||||
|
||||
extern "C" {
|
||||
|
||||
char* APS5_VABI strcasestr_nid_postfix(const char* text, const char* needle) {
|
||||
if (*needle == '\0') return const_cast<char*>(text);
|
||||
for (; *text != '\0'; ++text) {
|
||||
const char* candidate = text;
|
||||
const char* match = needle;
|
||||
while (*candidate != '\0' && *match != '\0' &&
|
||||
std::tolower(static_cast<unsigned char>(*candidate)) ==
|
||||
std::tolower(static_cast<unsigned char>(*match))) {
|
||||
++candidate;
|
||||
++match;
|
||||
}
|
||||
if (*match == '\0') return const_cast<char*>(text);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace GuestResolver {
|
||||
struct AddressInfo {
|
||||
std::int32_t flags;
|
||||
std::int32_t family;
|
||||
std::int32_t socketType;
|
||||
std::int32_t protocol;
|
||||
std::uint32_t addressLength;
|
||||
char* canonicalName;
|
||||
void* address;
|
||||
AddressInfo* next;
|
||||
};
|
||||
static_assert(sizeof(AddressInfo) == 48);
|
||||
static_assert(offsetof(AddressInfo, canonicalName) == 24);
|
||||
static_assert(offsetof(AddressInfo, address) == 32);
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include "GuestResolver.hpp"
|
||||
|
||||
namespace {
|
||||
bool Ready() {
|
||||
#ifdef _WIN32
|
||||
static const int status = [] { WSADATA data{}; return WSAStartup(MAKEWORD(2, 2), &data); }();
|
||||
return status == 0;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
int GuestError(int error) {
|
||||
if (error == 0) return 0;
|
||||
if (error == EAI_AGAIN) return 2;
|
||||
if (error == EAI_BADFLAGS) return 3;
|
||||
if (error == EAI_FAMILY) return 5;
|
||||
if (error == EAI_MEMORY) return 6;
|
||||
if (error == EAI_NONAME) return 8;
|
||||
if (error == EAI_SERVICE) return 9;
|
||||
if (error == EAI_SOCKTYPE) return 10;
|
||||
#ifdef _WIN32
|
||||
if (error == WSAEFAULT) return 14;
|
||||
#else
|
||||
if (error == EAI_OVERFLOW) return 14;
|
||||
#endif
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void APS5_VABI freeaddrinfo_nid_postfix(GuestResolver::AddressInfo* first) {
|
||||
while (first) {
|
||||
auto* next = first->next;
|
||||
std::free(first->canonicalName);
|
||||
std::free(first->address);
|
||||
std::free(first);
|
||||
first = next;
|
||||
}
|
||||
}
|
||||
|
||||
int APS5_VABI getaddrinfo_nid_postfix(const char* node, const char* service,
|
||||
const GuestResolver::AddressInfo* hints, GuestResolver::AddressInfo** output) {
|
||||
if (!output) return 4;
|
||||
*output = nullptr;
|
||||
addrinfo nativeHints{};
|
||||
if (hints) {
|
||||
if (hints->addressLength || hints->address || hints->canonicalName || hints->next) return 12;
|
||||
if ((hints->flags & ~0xd0f) != 0) return 3;
|
||||
if (hints->family != 0 && hints->family != 2 && hints->family != 28) return 5;
|
||||
if (hints->socketType != 0 && hints->socketType != 1 && hints->socketType != 2) return 10;
|
||||
nativeHints.ai_family = hints->family == 28 ? AF_INET6 : hints->family == 2 ? AF_INET : AF_UNSPEC;
|
||||
nativeHints.ai_socktype = hints->socketType == 1 ? SOCK_STREAM : hints->socketType == 2 ? SOCK_DGRAM : 0;
|
||||
nativeHints.ai_protocol = hints->protocol;
|
||||
if (hints->flags & 1) nativeHints.ai_flags |= AI_PASSIVE;
|
||||
if (hints->flags & 2) nativeHints.ai_flags |= AI_CANONNAME;
|
||||
if (hints->flags & 4) nativeHints.ai_flags |= AI_NUMERICHOST;
|
||||
if (hints->flags & 8) nativeHints.ai_flags |= AI_NUMERICSERV;
|
||||
if (hints->flags & 0x100) nativeHints.ai_flags |= AI_ALL;
|
||||
if (hints->flags & 0x400) nativeHints.ai_flags |= AI_ADDRCONFIG;
|
||||
if (hints->flags & 0x800) nativeHints.ai_flags |= AI_V4MAPPED;
|
||||
}
|
||||
if (!Ready()) return 4;
|
||||
addrinfo* native = nullptr;
|
||||
const auto error = ::getaddrinfo(node, service, &nativeHints, &native);
|
||||
if (error) return GuestError(error);
|
||||
GuestResolver::AddressInfo* first = nullptr;
|
||||
auto** tail = &first;
|
||||
int failure = 0;
|
||||
for (auto* item = native; item; item = item->ai_next) {
|
||||
if (item->ai_family != AF_INET && item->ai_family != AF_INET6) { failure = 5; break; }
|
||||
auto* converted = static_cast<GuestResolver::AddressInfo*>(std::calloc(1, sizeof(GuestResolver::AddressInfo)));
|
||||
if (!converted) { failure = 6; break; }
|
||||
*tail = converted;
|
||||
tail = &converted->next;
|
||||
converted->flags = hints ? hints->flags : 0;
|
||||
converted->family = item->ai_family == AF_INET ? 2 : 28;
|
||||
converted->socketType = item->ai_socktype == SOCK_STREAM ? 1 : item->ai_socktype == SOCK_DGRAM ? 2 : 0;
|
||||
converted->protocol = item->ai_protocol;
|
||||
converted->addressLength = item->ai_family == AF_INET ? 16 : 28;
|
||||
auto* bytes = static_cast<unsigned char*>(std::calloc(1, converted->addressLength));
|
||||
converted->address = bytes;
|
||||
if (!bytes) { failure = 6; break; }
|
||||
bytes[0] = static_cast<unsigned char>(converted->addressLength);
|
||||
bytes[1] = static_cast<unsigned char>(converted->family);
|
||||
if (item->ai_family == AF_INET) {
|
||||
const auto* v4 = reinterpret_cast<const sockaddr_in*>(item->ai_addr);
|
||||
std::memcpy(bytes + 2, &v4->sin_port, 2);
|
||||
std::memcpy(bytes + 4, &v4->sin_addr, 4);
|
||||
} else {
|
||||
const auto* v6 = reinterpret_cast<const sockaddr_in6*>(item->ai_addr);
|
||||
std::memcpy(bytes + 2, &v6->sin6_port, 2);
|
||||
std::memcpy(bytes + 4, &v6->sin6_flowinfo, 4);
|
||||
std::memcpy(bytes + 8, &v6->sin6_addr, 16);
|
||||
std::memcpy(bytes + 24, &v6->sin6_scope_id, 4);
|
||||
}
|
||||
if (item->ai_canonname) {
|
||||
const auto size = std::strlen(item->ai_canonname) + 1;
|
||||
converted->canonicalName = static_cast<char*>(std::malloc(size));
|
||||
if (!converted->canonicalName) { failure = 6; break; }
|
||||
std::memcpy(converted->canonicalName, item->ai_canonname, size);
|
||||
}
|
||||
}
|
||||
::freeaddrinfo(native);
|
||||
if (failure) { freeaddrinfo_nid_postfix(first); return failure; }
|
||||
*output = first;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI getnameinfo_nid_postfix(const void* address, std::uint32_t length,
|
||||
char* host, std::uint32_t hostLength, char* service, std::uint32_t serviceLength, int flags) {
|
||||
// PS5 sockaddr begins with byte-sized length and family. The host's does not.
|
||||
if (!address || length < 2) return 5;
|
||||
if ((!host && hostLength) || (!service && serviceLength)) return 4;
|
||||
if ((flags & ~0x1f) != 0) return 3;
|
||||
const auto* bytes = static_cast<const unsigned char*>(address);
|
||||
sockaddr_storage native{};
|
||||
socklen_t nativeLength;
|
||||
if (bytes[1] == 2) {
|
||||
if (length < 16 || bytes[0] != 16) return 5;
|
||||
auto& v4 = reinterpret_cast<sockaddr_in&>(native);
|
||||
v4.sin_family = AF_INET;
|
||||
std::memcpy(&v4.sin_port, bytes + 2, 2);
|
||||
std::memcpy(&v4.sin_addr, bytes + 4, 4);
|
||||
nativeLength = sizeof(v4);
|
||||
} else if (bytes[1] == 28) {
|
||||
if (length < 28 || bytes[0] != 28) return 5;
|
||||
auto& v6 = reinterpret_cast<sockaddr_in6&>(native);
|
||||
v6.sin6_family = AF_INET6;
|
||||
std::memcpy(&v6.sin6_port, bytes + 2, 2);
|
||||
std::memcpy(&v6.sin6_flowinfo, bytes + 4, 4);
|
||||
std::memcpy(&v6.sin6_addr, bytes + 8, 16);
|
||||
std::memcpy(&v6.sin6_scope_id, bytes + 24, 4);
|
||||
nativeLength = sizeof(v6);
|
||||
} else return 5;
|
||||
int nativeFlags = 0;
|
||||
if (flags & 1) nativeFlags |= NI_NOFQDN;
|
||||
if (flags & 2) nativeFlags |= NI_NUMERICHOST;
|
||||
if (flags & 4) nativeFlags |= NI_NAMEREQD;
|
||||
if (flags & 8) nativeFlags |= NI_NUMERICSERV;
|
||||
if (flags & 16) nativeFlags |= NI_DGRAM;
|
||||
if (!Ready()) return 4;
|
||||
// Stage results so host-specific short-buffer errors do not leak into the
|
||||
// guest ABI, and neither guest output is partially modified on overflow.
|
||||
char resolvedHost[NI_MAXHOST]{};
|
||||
char resolvedService[NI_MAXSERV]{};
|
||||
const bool wantHost = host && hostLength;
|
||||
const bool wantService = service && serviceLength;
|
||||
const int error = ::getnameinfo(reinterpret_cast<const sockaddr*>(&native), nativeLength,
|
||||
wantHost ? resolvedHost : nullptr, wantHost ? sizeof(resolvedHost) : 0,
|
||||
wantService ? resolvedService : nullptr, wantService ? sizeof(resolvedService) : 0, nativeFlags);
|
||||
if (error) return GuestError(error);
|
||||
if ((wantHost && std::strlen(resolvedHost) >= hostLength) ||
|
||||
(wantService && std::strlen(resolvedService) >= serviceLength)) return 14;
|
||||
if (wantHost) std::memcpy(host, resolvedHost, std::strlen(resolvedHost) + 1);
|
||||
if (wantService) std::memcpy(service, resolvedService, std::strlen(resolvedService) + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* APS5_VABI gai_strerror_nid_postfix(int error) {
|
||||
static const char* messages[] = {
|
||||
"Success", "Address family for hostname not supported", "Temporary failure in name resolution",
|
||||
"Invalid flags", "Name resolution failed", "Address family not supported",
|
||||
"Memory allocation failed", "No address for hostname", "Name or service not known",
|
||||
"Service not supported for socket type", "Socket type not supported", "System error",
|
||||
"Invalid hints", "Unknown protocol", "Buffer too small"
|
||||
};
|
||||
if (error < 0 || error >= static_cast<int>(sizeof(messages) / sizeof(messages[0])))
|
||||
return "Unknown address resolution error";
|
||||
return messages[error];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,29 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int SCE_RANDOM_ERROR_INVALID = static_cast<int>(0x817C0016);
|
||||
constexpr std::size_t SCE_RANDOM_MAX_SIZE = 64;
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceRandomGetRandomNumber(void* buf, size_t size) {
|
||||
(void)buf;
|
||||
(void)size;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (!buf || size > SCE_RANDOM_MAX_SIZE) return SCE_RANDOM_ERROR_INVALID;
|
||||
static thread_local std::random_device device;
|
||||
auto* bytes = static_cast<unsigned char*>(buf);
|
||||
for (std::size_t offset = 0; offset < size; offset += sizeof(unsigned int)) {
|
||||
const unsigned int value = device();
|
||||
std::memcpy(bytes + offset, &value, std::min(sizeof(value), size - offset));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ bool fillModuleInfoForUnwind(std::uint64_t addr, ModuleInfoForUnwind* info) {
|
|||
std::uint64_t end = 0;
|
||||
char perms[8] = {};
|
||||
std::uint64_t offset = 0;
|
||||
int devMajor = 0;
|
||||
int devMinor = 0;
|
||||
unsigned int devMajor = 0;
|
||||
unsigned int devMinor = 0;
|
||||
std::uint64_t inode = 0;
|
||||
char path[4096] = {};
|
||||
int parsed = std::sscanf(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,24 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include "prx/libc/include/Shutdown.hpp"
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libSceSystemService/SystemService.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceSystemServiceLoadExec(const char* path, const char* const* arguments) {
|
||||
if (!path || !*path) return SYSTEM_SERVICE_ERROR_PARAMETER;
|
||||
if (std::strcmp(path, "exit") != 0) {
|
||||
NotImplemented_nid_no_patch("sceSystemServiceLoadExec: executable replacement");
|
||||
}
|
||||
(void)arguments;
|
||||
LibcRunShutdown_nid_postfix();
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
int APS5_VABI sceSystemServiceDisableNoticeScreenSkipFlagAutoSet(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
|
|
@ -95,9 +107,4 @@ int APS5_VABI sceSystemServiceSetNoticeScreenSkipFlag(void) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceSystemServiceLoadExec() {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <exception>
|
||||
|
||||
// Caller serializes access with the owning VideoOutConfig mutex.
|
||||
class BufferReuseTracker {
|
||||
std::uint64_t _issued = 0;
|
||||
std::set<std::uint64_t> _pending;
|
||||
public:
|
||||
std::uint64_t Reserve() {
|
||||
if (_issued == std::numeric_limits<std::uint64_t>::max())
|
||||
throw std::overflow_error("VideoOut: buffer reservation sequence exhausted");
|
||||
const auto ticket = _issued + 1;
|
||||
_pending.insert(ticket);
|
||||
_issued = ticket;
|
||||
return ticket;
|
||||
}
|
||||
std::uint64_t Capture() const { return _issued; }
|
||||
bool IsComplete(std::uint64_t through) const {
|
||||
if (through > _issued) throw std::invalid_argument("VideoOut: unissued buffer fence");
|
||||
return _pending.empty() || *_pending.begin() > through;
|
||||
}
|
||||
void Complete(std::uint64_t ticket) noexcept {
|
||||
if (_pending.erase(ticket) != 1) std::terminate();
|
||||
}
|
||||
};
|
||||
|
|
@ -29,7 +29,7 @@ private:
|
|||
void removeSubclass() noexcept;
|
||||
void applyAspectRatio(void* hwnd, std::uintptr_t edge, void* rect) const;
|
||||
|
||||
static std::intptr_t __stdcall windowProc(void* hwnd, unsigned int message, std::uintptr_t wParam, std::intptr_t lParam, std::uintptr_t subclassId, std::uintptr_t referenceData);
|
||||
static std::intptr_t windowProc(void* hwnd, unsigned int message, std::uintptr_t wParam, std::intptr_t lParam, std::uintptr_t subclassId, std::uintptr_t referenceData);
|
||||
|
||||
SDL_Window* window = nullptr;
|
||||
std::uint32_t aspectWidth = 0;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "prx/libSceAgcDriver/Execution/include/VideoOutput.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/DisplayBuffer.hpp"
|
||||
#include "prx/libSceVideoOut/include/DisplayWindow.hpp"
|
||||
#include "prx/libSceVideoOut/include/BufferReuseTracker.hpp"
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SceTypes.hpp"
|
||||
|
|
@ -111,6 +112,7 @@ struct VideoOutConfig {
|
|||
|
||||
std::array<VideoOutBuffer, VIDEO_OUT_BUFFER_NUM_MAX> buffers{};
|
||||
std::array<uint32_t, VIDEO_OUT_BUFFER_NUM_MAX> bufferPending{};
|
||||
std::array<BufferReuseTracker, VIDEO_OUT_BUFFER_NUM_MAX> bufferReuse;
|
||||
std::array<BufferAttributeGroup, VIDEO_OUT_BUFFER_ATTRIBUTE_NUM_MAX> groups{};
|
||||
|
||||
void Check() const {
|
||||
|
|
@ -122,6 +124,7 @@ struct VideoOutConfig {
|
|||
struct FlipQueue;
|
||||
|
||||
struct FlipRequest final : AgcDriver::IFlipRequest, std::enable_shared_from_this<FlipRequest> {
|
||||
std::uint64_t reuseTicket = 0;
|
||||
std::shared_ptr<VideoOutConfig> cfg;
|
||||
std::shared_ptr<FlipQueue> queue;
|
||||
uint64_t generation = 0;
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ void DisplayWindow::applyAspectRatio(void* hwnd, std::uintptr_t edge, void* rect
|
|||
#endif
|
||||
}
|
||||
|
||||
std::intptr_t __stdcall DisplayWindow::windowProc(void* hwnd, unsigned int message, std::uintptr_t wParam, std::intptr_t lParam, std::uintptr_t subclassId, std::uintptr_t referenceData) {
|
||||
std::intptr_t DisplayWindow::windowProc(void* hwnd, unsigned int message, std::uintptr_t wParam, std::intptr_t lParam, std::uintptr_t subclassId, std::uintptr_t referenceData) {
|
||||
#ifdef _WIN32
|
||||
static_cast<void>(subclassId);
|
||||
if (message == WM_SIZING) {
|
||||
|
|
|
|||
|
|
@ -29,10 +29,34 @@ void checkConfig(const VideoOutConfig& cfg) {
|
|||
cfg.Check();
|
||||
}
|
||||
|
||||
class RenderingWait final : public AgcDriver::IRenderingWait {
|
||||
std::shared_ptr<VideoOutConfig> _config;
|
||||
std::uint32_t _index;
|
||||
std::uint64_t _ticket;
|
||||
public:
|
||||
RenderingWait(std::shared_ptr<VideoOutConfig> config, std::uint32_t index, std::uint64_t ticket)
|
||||
: _config(std::move(config)), _index(index), _ticket(ticket) {}
|
||||
void Wait() override {
|
||||
std::unique_lock lock(_config->mutex);
|
||||
_config->vblankCond.wait(lock, [&] {
|
||||
return _config->failure || _config->closing || !_config->opened ||
|
||||
_config->bufferReuse[_index].IsComplete(_ticket);
|
||||
});
|
||||
checkConfig(*_config);
|
||||
}
|
||||
};
|
||||
|
||||
class VideoOutput final : public AgcDriver::IVideoOutput {
|
||||
public:
|
||||
VideoOutput(std::shared_ptr<VideoOutConfig> config, std::shared_ptr<FlipQueue> requests) : cfg(std::move(config)), queue(std::move(requests)) {}
|
||||
|
||||
std::shared_ptr<AgcDriver::IRenderingWait> CaptureRenderingWait(std::uint32_t index) override {
|
||||
std::lock_guard lock(cfg->mutex);
|
||||
checkConfig(*cfg);
|
||||
require(index < VIDEO_OUT_BUFFER_NUM_MAX && cfg->buffers[index].Occupied(), "wait buffer is not registered");
|
||||
return std::make_shared<RenderingWait>(cfg, index, cfg->bufferReuse[index].Capture());
|
||||
}
|
||||
|
||||
std::shared_ptr<AgcDriver::IFlipRequest> Reserve(const AgcDriver::FlipInfo& info) override {
|
||||
// All flip modes are presented at the next vsync.
|
||||
require(info.mode >= VIDEO_OUT_FLIP_MODE_VSYNC && info.mode <= 6, "unsupported flip mode");
|
||||
|
|
@ -76,6 +100,7 @@ public:
|
|||
}
|
||||
request->generation = cfg->generation;
|
||||
request->flipRate = cfg->flipRate;
|
||||
if (info.index >= 0) request->reuseTicket = cfg->bufferReuse[info.index].Reserve();
|
||||
++queue->reservations;
|
||||
++cfg->flipStatus.flipPendingNum;
|
||||
if (info.index >= 0) ++cfg->bufferPending[info.index];
|
||||
|
|
@ -110,7 +135,10 @@ FlipRequest::~FlipRequest() {
|
|||
if (!terminal) {
|
||||
--cfg->flipStatus.flipPendingNum;
|
||||
--queue->reservations;
|
||||
if (index >= 0) --cfg->bufferPending[index];
|
||||
if (index >= 0) {
|
||||
--cfg->bufferPending[index];
|
||||
cfg->bufferReuse[index].Complete(reuseTicket);
|
||||
}
|
||||
cfg->vblankCond.notify_all();
|
||||
}
|
||||
}
|
||||
|
|
@ -154,7 +182,10 @@ void FlipRequest::Fail(std::exception_ptr error) noexcept {
|
|||
if (reserved && !terminal) {
|
||||
--cfg->flipStatus.flipPendingNum;
|
||||
--queue->reservations;
|
||||
if (index >= 0) --cfg->bufferPending[index];
|
||||
if (index >= 0) {
|
||||
--cfg->bufferPending[index];
|
||||
cfg->bufferReuse[index].Complete(reuseTicket);
|
||||
}
|
||||
terminal = true;
|
||||
}
|
||||
cfg->vblankCond.notify_all();
|
||||
|
|
@ -421,7 +452,10 @@ void VideoOutDriver::processFlip(FlipRequest& req) {
|
|||
req.cfg->height = req.height;
|
||||
--req.cfg->flipStatus.flipPendingNum;
|
||||
--req.queue->reservations;
|
||||
if (req.index >= 0) --req.cfg->bufferPending[req.index];
|
||||
if (req.index >= 0) {
|
||||
--req.cfg->bufferPending[req.index];
|
||||
req.cfg->bufferReuse[req.index].Complete(req.reuseTicket);
|
||||
}
|
||||
req.terminal = true;
|
||||
req.cfg->vblankCond.notify_all();
|
||||
timing.Mark("notify_game");
|
||||
|
|
|
|||
|
|
@ -5,13 +5,17 @@ target_sources(libc PRIVATE
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/specifics/windows/CrashReport.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/specifics/linux/DlIteratePhdr.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Strings.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/StringExtensions.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Math.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Time.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/FileAndHeap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/StdioExtras.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/StandardStreams.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GuestAllocations.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GuestArena.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/GuestHeap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/HeapDiagnostics.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Mspace.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ApplicationHeap.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ApplicationProcessParameters.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/JumpBuffer.cpp
|
||||
|
|
@ -19,6 +23,10 @@ target_sources(libc PRIVATE
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/RuntimeSupport.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/LocaleSupport.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Process.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Environment.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Directory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Filesystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Errors.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/CxxAbiSupport.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/exception/Exports.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/HeapDiagnostics.hpp"
|
||||
|
||||
uint32_t Need_sceLibc = 1;
|
||||
|
||||
|
|
@ -39,8 +40,7 @@ extern "C" {
|
|||
}
|
||||
|
||||
void APS5_VABI LibcHeapGetTraceInfo_nid_postfix(LibcHeapInfo* info) {
|
||||
(void)info;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
LibcHeapTraceInfo_nid_no_patch(info);
|
||||
}
|
||||
|
||||
int APS5_VABI LibcInternalExtCxaThreadAtexit_nid_postfix(void (*destructor)(void*), void* object, void* module_id) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,28 @@
|
|||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
struct GuestFilePrefix {
|
||||
unsigned char* position = nullptr;
|
||||
std::int32_t readRemaining = 0;
|
||||
std::int32_t writeRemaining = 0;
|
||||
std::int16_t flags = 0;
|
||||
std::int16_t descriptor = -1;
|
||||
unsigned char* buffer = nullptr;
|
||||
std::int32_t bufferSize = 0;
|
||||
std::int32_t bufferPadding = 0;
|
||||
std::int32_t lineBufferSize = 0;
|
||||
};
|
||||
static_assert(offsetof(GuestFilePrefix, flags) == 16);
|
||||
static_assert(offsetof(GuestFilePrefix, descriptor) == 18);
|
||||
static_assert(offsetof(GuestFilePrefix, buffer) == 24);
|
||||
static_assert(offsetof(GuestFilePrefix, lineBufferSize) == 40);
|
||||
|
||||
static constexpr const char* FOPEN_EXT_VERT = ".vert";
|
||||
static constexpr const char* FOPEN_MSG_NULL_ARG = "null argument";
|
||||
|
|
@ -11,12 +33,23 @@ static constexpr const char* FOPEN_MSG_NOT_FOUND = "file not found";
|
|||
static constexpr const char* FOPEN_MSG_OPEN_FAILED = "open failed";
|
||||
|
||||
class FileStream {
|
||||
// Only the macro-accessed FreeBSD prefix is exposed. The remaining guest
|
||||
// FILE area is reserved; host state lives beyond it and is never a guest FILE*.
|
||||
GuestFilePrefix _guest{};
|
||||
std::byte _reserved[256 - sizeof(GuestFilePrefix)]{};
|
||||
std::FILE* _handle;
|
||||
bool _dynamic;
|
||||
|
||||
public:
|
||||
explicit FileStream(std::FILE* handle, bool dynamic = false) : _handle(handle), _dynamic(dynamic) {
|
||||
if (!_handle) throw std::runtime_error("FileStream: null handle");
|
||||
_guest.flags = handle == stdin ? 4 : handle == stdout || handle == stderr ? 8 : 0x10;
|
||||
#ifdef _WIN32
|
||||
const int descriptor = _fileno(handle);
|
||||
#else
|
||||
const int descriptor = ::fileno(handle);
|
||||
#endif
|
||||
_guest.descriptor = descriptor >= 0 && descriptor <= 32767 ? static_cast<std::int16_t>(descriptor) : -1;
|
||||
}
|
||||
|
||||
FileStream(const FileStream&) = delete;
|
||||
|
|
@ -31,11 +64,36 @@ public:
|
|||
return _dynamic;
|
||||
}
|
||||
|
||||
GuestFilePrefix& GuestState() { return _guest; }
|
||||
bool Reopen(const char* filename, const char* mode) {
|
||||
auto* previous = GetHandle();
|
||||
_guest = {};
|
||||
_handle = std::freopen(filename, mode, previous);
|
||||
if (!_handle) return false;
|
||||
_guest.flags = 0x10;
|
||||
#ifdef _WIN32
|
||||
const int descriptor = _fileno(_handle);
|
||||
#else
|
||||
const int descriptor = ::fileno(_handle);
|
||||
#endif
|
||||
_guest.descriptor = descriptor >= 0 && descriptor <= 32767 ? static_cast<std::int16_t>(descriptor) : -1;
|
||||
return true;
|
||||
}
|
||||
void SyncStatus() {
|
||||
_guest.readRemaining = 0;
|
||||
_guest.writeRemaining = 0;
|
||||
_guest.flags = static_cast<std::int16_t>((_guest.flags & ~0x60) |
|
||||
(std::feof(GetHandle()) ? 0x20 : 0) | (std::ferror(GetHandle()) ? 0x40 : 0));
|
||||
}
|
||||
|
||||
void Close() {
|
||||
GetHandle();
|
||||
if (std::fclose(std::exchange(_handle, nullptr)) != 0) throw std::runtime_error("FileStream: close failed");
|
||||
_guest.flags = 0;
|
||||
_guest.descriptor = -1;
|
||||
}
|
||||
};
|
||||
static_assert(std::is_standard_layout_v<FileStream>);
|
||||
|
||||
inline std::FILE* GetNativeStream(FileStream* stream) {
|
||||
if (!stream) throw std::runtime_error("FileStream: null stream");
|
||||
|
|
|
|||
|
|
@ -24,9 +24,7 @@ using Lease = std::vector<std::shared_ptr<const Range>>;
|
|||
|
||||
extern "C" {
|
||||
void* GuestAllocationsBegin_nid_postfix();
|
||||
#ifdef _WIN32
|
||||
void GuestAllocationsRegisterMainImage_nid_postfix(void* mutation);
|
||||
#endif
|
||||
void GuestAllocationsEnd_nid_postfix(void* mutation) noexcept;
|
||||
void GuestAllocationsAdd_nid_postfix(void* mutation, void* pointer, std::size_t bytes, bool readable, bool writable);
|
||||
void GuestAllocationsRequireUnpinned_nid_postfix(void* mutation, const void* pointer, std::size_t bytes);
|
||||
|
|
@ -57,9 +55,7 @@ public:
|
|||
~Mutation() { GuestAllocationsEnd_nid_postfix(handle); }
|
||||
Mutation(const Mutation&) = delete;
|
||||
Mutation& operator=(const Mutation&) = delete;
|
||||
#ifdef _WIN32
|
||||
void RegisterMainImage() { GuestAllocationsRegisterMainImage_nid_postfix(handle); }
|
||||
#endif
|
||||
void Add(void* pointer, std::size_t bytes, bool readable, bool writable) { GuestAllocationsAdd_nid_postfix(handle, pointer, bytes, readable, writable); }
|
||||
void RequireUnpinned(const void* pointer, std::size_t bytes) const { GuestAllocationsRequireUnpinned_nid_postfix(handle, pointer, bytes); }
|
||||
void RequireAvailable(const void* pointer, std::size_t bytes) const { GuestAllocationsRequireAvailable_nid_postfix(handle, pointer, bytes); }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
// FreeBSD 11 ABI used by the PS5 SDK: names start at byte 8.
|
||||
struct GuestDirectoryEntry {
|
||||
std::uint32_t fileNumber;
|
||||
std::uint16_t recordLength;
|
||||
std::uint8_t type;
|
||||
std::uint8_t nameLength;
|
||||
char name[256];
|
||||
};
|
||||
static_assert(offsetof(GuestDirectoryEntry, name) == 8);
|
||||
static_assert(sizeof(GuestDirectoryEntry) == 264);
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBC_INCLUDE_HEAPDIAGNOSTICS_HPP
|
||||
#define CORE_LIBS_PRX_LIBC_INCLUDE_HEAPDIAGNOSTICS_HPP
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
extern "C" void LibcHeapTraceInfo_nid_no_patch(LibcHeapInfo* info);
|
||||
|
||||
#endif
|
||||
|
|
@ -3,5 +3,6 @@
|
|||
|
||||
extern "C" void LibcRegisterShutdown_nid_postfix(void (*callback)());
|
||||
extern "C" void LibcRunShutdown_nid_postfix();
|
||||
extern "C" [[noreturn]] void LibcExit_nid_no_patch(int code);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
#include "prx/libc/include/GuestDirectory.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include <dirent.h>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace {
|
||||
int DirectoryError(int error) {
|
||||
switch (error) {
|
||||
case ENOENT: return 2;
|
||||
case EACCES: return 13;
|
||||
case ENOTDIR: return 20;
|
||||
case EINVAL: return 22;
|
||||
case EMFILE: return 24;
|
||||
case ENFILE: return 23;
|
||||
case ENOMEM: return 12;
|
||||
case ENAMETOOLONG: return 63;
|
||||
default: return 5;
|
||||
}
|
||||
}
|
||||
struct Directory {
|
||||
DIR* native = nullptr;
|
||||
std::filesystem::path path;
|
||||
GuestDirectoryEntry entry{};
|
||||
std::mutex mutex;
|
||||
~Directory() { if (native) ::closedir(native); }
|
||||
};
|
||||
std::uint8_t DirectoryType(const std::filesystem::path& path) {
|
||||
std::error_code error;
|
||||
switch (std::filesystem::symlink_status(path, error).type()) {
|
||||
case std::filesystem::file_type::regular: return 8;
|
||||
case std::filesystem::file_type::directory: return 4;
|
||||
case std::filesystem::file_type::symlink: return 10;
|
||||
case std::filesystem::file_type::block: return 6;
|
||||
case std::filesystem::file_type::character: return 2;
|
||||
case std::filesystem::file_type::fifo: return 1;
|
||||
case std::filesystem::file_type::socket: return 12;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void* APS5_VABI opendir_nid_postfix(const char* path) {
|
||||
if (!path) { errno = 14; return nullptr; }
|
||||
if (!*path) { errno = 2; return nullptr; }
|
||||
try {
|
||||
auto directory = std::make_unique<Directory>();
|
||||
directory->path = ResolvePath_nid_no_patch(path);
|
||||
directory->native = ::opendir(directory->path.string().c_str());
|
||||
if (!directory->native) { errno = DirectoryError(errno); return nullptr; }
|
||||
return directory.release();
|
||||
} catch (const std::bad_alloc&) { errno = 12; return nullptr; }
|
||||
catch (const std::filesystem::filesystem_error&) { errno = 5; return nullptr; }
|
||||
}
|
||||
|
||||
GuestDirectoryEntry* APS5_VABI readdir_nid_postfix(void* handle) {
|
||||
if (!handle) { errno = 9; return nullptr; }
|
||||
auto& directory = *static_cast<Directory*>(handle);
|
||||
std::lock_guard lock(directory.mutex);
|
||||
const int savedError = errno;
|
||||
errno = 0;
|
||||
auto* entry = ::readdir(directory.native);
|
||||
if (!entry) {
|
||||
errno = errno ? DirectoryError(errno) : savedError;
|
||||
return nullptr;
|
||||
}
|
||||
const auto length = std::strlen(entry->d_name);
|
||||
if (length > 255) { errno = 63; return nullptr; }
|
||||
try {
|
||||
directory.entry = {};
|
||||
// MinGW reports no inode identity (zero); do not fabricate one.
|
||||
const auto inode = static_cast<std::uint64_t>(entry->d_ino);
|
||||
directory.entry.fileNumber = inode <= UINT32_MAX ? static_cast<std::uint32_t>(inode) : 0;
|
||||
directory.entry.recordLength = static_cast<std::uint16_t>(8 + ((length + 1 + 3) & ~3));
|
||||
directory.entry.nameLength = static_cast<std::uint8_t>(length);
|
||||
directory.entry.type = DirectoryType(directory.path / entry->d_name);
|
||||
std::memcpy(directory.entry.name, entry->d_name, length + 1);
|
||||
errno = savedError;
|
||||
return &directory.entry;
|
||||
} catch (const std::bad_alloc&) { errno = 12; return nullptr; }
|
||||
catch (const std::filesystem::filesystem_error&) { errno = 5; return nullptr; }
|
||||
}
|
||||
|
||||
int APS5_VABI closedir_nid_postfix(void* handle) {
|
||||
if (!handle) { errno = 9; return -1; }
|
||||
std::unique_ptr<Directory> directory(static_cast<Directory*>(handle));
|
||||
const int result = ::closedir(directory->native);
|
||||
directory->native = nullptr;
|
||||
if (result) errno = DirectoryError(errno);
|
||||
return result;
|
||||
}
|
||||
|
||||
void APS5_VABI rewinddir_nid_postfix(void* handle) {
|
||||
if (!handle) { errno = 9; return; }
|
||||
auto& directory = *static_cast<Directory*>(handle);
|
||||
std::lock_guard lock(directory.mutex);
|
||||
::rewinddir(directory.native);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
|
||||
#ifndef _WIN32
|
||||
extern char** environ;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
struct EnvironmentEntry {
|
||||
std::string owned;
|
||||
char* borrowed = nullptr;
|
||||
char* Data() { return borrowed ? borrowed : owned.data(); }
|
||||
bool Matches(std::string_view name) {
|
||||
const char* data = Data();
|
||||
const char* separator = std::strchr(data, '=');
|
||||
return separator && std::string_view(data, separator - data) == name;
|
||||
}
|
||||
};
|
||||
std::mutex environmentMutex;
|
||||
std::vector<EnvironmentEntry>& Environment() {
|
||||
// Seed once from the host. Guest mutations remain local to the guest runtime.
|
||||
static std::vector<EnvironmentEntry> entries = [] {
|
||||
std::vector<EnvironmentEntry> result;
|
||||
#ifdef _WIN32
|
||||
char** source = _environ;
|
||||
#else
|
||||
char** source = environ;
|
||||
#endif
|
||||
if (source) for (; *source; ++source) {
|
||||
if (**source && **source != '=' && std::strchr(*source, '='))
|
||||
result.push_back({*source, nullptr});
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
return entries;
|
||||
}
|
||||
bool ValidEnvironmentName(const char* name) {
|
||||
return name && *name && !std::strchr(name, '=');
|
||||
}
|
||||
int EnvironmentError(int error) { errno = error; return -1; }
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
char* APS5_VABI getenv_nid_postfix(const char* name) {
|
||||
if (!ValidEnvironmentName(name)) return nullptr;
|
||||
try {
|
||||
std::lock_guard lock(environmentMutex);
|
||||
for (auto& entry : Environment())
|
||||
if (entry.Matches(name)) return entry.Data() + std::strlen(name) + 1;
|
||||
return nullptr;
|
||||
} catch (const std::bad_alloc&) { errno = 12; return nullptr; }
|
||||
}
|
||||
|
||||
int APS5_VABI setenv_nid_postfix(const char* name, const char* value, int overwrite) {
|
||||
if (!ValidEnvironmentName(name) || !value) return EnvironmentError(22);
|
||||
try {
|
||||
std::lock_guard lock(environmentMutex);
|
||||
auto& entries = Environment();
|
||||
auto found = std::find_if(entries.begin(), entries.end(),
|
||||
[name](auto& entry) { return entry.Matches(name); });
|
||||
if (found != entries.end() && !overwrite) return 0;
|
||||
EnvironmentEntry replacement{std::string(name) + '=' + value, nullptr};
|
||||
if (found != entries.end()) *found = std::move(replacement);
|
||||
else entries.push_back(std::move(replacement));
|
||||
return 0;
|
||||
} catch (const std::bad_alloc&) { return EnvironmentError(12); }
|
||||
}
|
||||
|
||||
int APS5_VABI unsetenv_nid_postfix(const char* name) {
|
||||
if (!ValidEnvironmentName(name)) return EnvironmentError(22);
|
||||
try {
|
||||
std::lock_guard lock(environmentMutex);
|
||||
auto& entries = Environment();
|
||||
const std::string key(name);
|
||||
std::erase_if(entries, [&key](auto& entry) { return entry.Matches(key); });
|
||||
return 0;
|
||||
} catch (const std::bad_alloc&) { return EnvironmentError(12); }
|
||||
}
|
||||
|
||||
int APS5_VABI putenv_nid_postfix(char* text) {
|
||||
const char* separator = text ? std::strchr(text, '=') : nullptr;
|
||||
if (!separator || separator == text) return EnvironmentError(22);
|
||||
try {
|
||||
std::lock_guard lock(environmentMutex);
|
||||
auto& entries = Environment();
|
||||
const std::string_view name(text, separator - text);
|
||||
auto found = std::find_if(entries.begin(), entries.end(),
|
||||
[name](auto& entry) { return entry.Matches(name); });
|
||||
EnvironmentEntry replacement{{}, text};
|
||||
if (found != entries.end()) *found = std::move(replacement);
|
||||
else entries.push_back(std::move(replacement));
|
||||
return 0;
|
||||
} catch (const std::bad_alloc&) { return EnvironmentError(12); }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" int* APS5_VABI __error_nid_postfix();
|
||||
|
||||
namespace {
|
||||
// Guest FreeBSD errno numbering, not the host CRT's numbering.
|
||||
constexpr const char* messages[] = {
|
||||
"No error", "Operation not permitted", "No such file or directory", "No such process",
|
||||
"Interrupted system call", "Input/output error", "Device not configured", "Argument list too long",
|
||||
"Exec format error", "Bad file descriptor", "No child processes", "Resource deadlock avoided",
|
||||
"Cannot allocate memory", "Permission denied", "Bad address", "Block device required",
|
||||
"Device busy", "File exists", "Cross-device link", "Operation not supported by device",
|
||||
"Not a directory", "Is a directory", "Invalid argument", "Too many open files in system",
|
||||
"Too many open files", "Inappropriate ioctl for device", "Text file busy", "File too large",
|
||||
"No space left on device", "Illegal seek", "Read-only filesystem", "Too many links", "Broken pipe",
|
||||
"Numerical argument out of domain", "Result too large", "Resource temporarily unavailable",
|
||||
"Operation now in progress", "Operation already in progress", "Socket operation on non-socket",
|
||||
"Destination address required", "Message too long", "Protocol wrong type for socket",
|
||||
"Protocol not available", "Protocol not supported", "Socket type not supported",
|
||||
"Operation not supported", "Protocol family not supported", "Address family not supported",
|
||||
"Address already in use", "Cannot assign requested address", "Network is down", "Network is unreachable",
|
||||
"Network connection reset", "Connection aborted", "Connection reset by peer", "No buffer space available",
|
||||
"Socket is already connected", "Socket is not connected", "Socket is shut down", "Too many references",
|
||||
"Operation timed out", "Connection refused", "Too many symbolic links", "File name too long",
|
||||
"Host is down", "No route to host", "Directory not empty", "Too many processes", "Too many users",
|
||||
"Disk quota exceeded", "Stale file handle", "Remote path has too many levels", "Invalid RPC structure",
|
||||
"RPC version mismatch", "RPC program unavailable", "Program version mismatch", "Invalid RPC procedure",
|
||||
"No locks available", "Function not implemented", "Inappropriate file type", "Authentication error",
|
||||
"Authentication required", "Identifier removed", "No message of requested type", "Value too large",
|
||||
"Operation canceled", "Invalid byte sequence", "Attribute not found", "Programming error",
|
||||
"Bad message", "Multihop attempted", "Link severed", "Protocol error", "Insufficient capabilities",
|
||||
"Operation not permitted in capability mode", "State not recoverable", "Previous owner died"
|
||||
};
|
||||
static_assert(sizeof(messages) / sizeof(messages[0]) == 97);
|
||||
bool Known(int error) { return error >= 0 && error < 97; }
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
char* APS5_VABI strerror_nid_postfix(int error) {
|
||||
thread_local char buffer[128];
|
||||
const int saved = *__error_nid_postfix();
|
||||
if (Known(error)) std::snprintf(buffer, sizeof(buffer), "%s", messages[error]);
|
||||
else std::snprintf(buffer, sizeof(buffer), "Unknown error: %d", error);
|
||||
*__error_nid_postfix() = Known(error) ? saved : 22;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int APS5_VABI strerror_r_nid_postfix(int error, char* buffer, std::size_t length) {
|
||||
const int saved = *__error_nid_postfix();
|
||||
char temporary[128];
|
||||
if (Known(error)) std::snprintf(temporary, sizeof(temporary), "%s", messages[error]);
|
||||
else std::snprintf(temporary, sizeof(temporary), "Unknown error: %d", error);
|
||||
const auto required = std::strlen(temporary) + 1;
|
||||
int result = Known(error) ? 0 : 22;
|
||||
if (!buffer || length < required) result = 34;
|
||||
if (buffer && length) {
|
||||
const auto count = required <= length ? required - 1 : length - 1;
|
||||
std::memcpy(buffer, temporary, count);
|
||||
buffer[count] = '\0';
|
||||
}
|
||||
*__error_nid_postfix() = saved;
|
||||
return result;
|
||||
}
|
||||
|
||||
void APS5_VABI perror_nid_postfix(const char* prefix) {
|
||||
const int saved = *__error_nid_postfix();
|
||||
char message[128];
|
||||
strerror_r_nid_postfix(saved, message, sizeof(message));
|
||||
if (prefix && *prefix) std::fprintf(stderr, "%s: %s\n", prefix, message);
|
||||
else std::fprintf(stderr, "%s\n", message);
|
||||
*__error_nid_postfix() = saved;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include <filesystem>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <cerrno>
|
||||
|
||||
#include "prx/libc/include/FileStream.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
|
|
@ -15,6 +16,25 @@ extern "C" {
|
|||
|
||||
[[noreturn]] void APS5_VABI _ZSt11_Xbad_allocv_nid_postfix();
|
||||
|
||||
FileStream* APS5_VABI freopen_nid_postfix(const char* filename, const char* mode, FileStream* stream) {
|
||||
if (!stream || !mode) { errno = 22; return nullptr; }
|
||||
if (!filename) { errno = 45; return nullptr; } // Mode-only reopening is not supported.
|
||||
const char* supported[] = {"r", "w", "a", "rb", "wb", "ab", "r+", "w+", "a+",
|
||||
"rb+", "wb+", "ab+", "r+b", "w+b", "a+b"};
|
||||
bool valid = false;
|
||||
for (const auto* candidate : supported) if (std::strcmp(mode, candidate) == 0) valid = true;
|
||||
if (!valid) { errno = 22; return nullptr; }
|
||||
try {
|
||||
const auto path = *filename ? ResolvePath_nid_no_patch(filename).string() : std::string{};
|
||||
if (stream->Reopen(path.c_str(), mode)) return stream;
|
||||
const int error = errno;
|
||||
if (stream->IsDynamic()) delete stream;
|
||||
errno = error;
|
||||
return nullptr;
|
||||
} catch (const std::bad_alloc&) { errno = 12; return nullptr; }
|
||||
catch (const std::filesystem::filesystem_error&) { errno = 5; return nullptr; }
|
||||
}
|
||||
|
||||
FileStream* APS5_VABI fopen_nid_postfix(const char* filename, const char* mode) {
|
||||
if (!filename || !mode) throw std::runtime_error(std::string(__func__) + ": " + FOPEN_MSG_NULL_ARG);
|
||||
const std::filesystem::path fpath = ResolvePath_nid_no_patch(filename);
|
||||
|
|
@ -55,6 +75,7 @@ size_t APS5_VABI fread_nid_postfix(void* buffer, size_t size, size_t count, File
|
|||
if (size == 0 || count == 0) return 0;
|
||||
if (!buffer) throw std::runtime_error("fread: null buffer");
|
||||
const auto result = std::fread(buffer, size, count, handle);
|
||||
stream->SyncStatus();
|
||||
if (std::ferror(handle)) throw std::runtime_error("fread: read failed");
|
||||
return result;
|
||||
}
|
||||
|
|
@ -64,22 +85,42 @@ size_t APS5_VABI fwrite_nid_postfix(const void* buffer, size_t size, size_t coun
|
|||
if (size == 0 || count == 0) return 0;
|
||||
if (!buffer) throw std::runtime_error("fwrite: null buffer");
|
||||
const auto result = std::fwrite(buffer, size, count, handle);
|
||||
stream->SyncStatus();
|
||||
if (result != count || std::ferror(handle)) throw std::runtime_error("fwrite: write failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
int APS5_VABI fseek_nid_postfix(FileStream* stream, long offset, int origin) {
|
||||
if (origin != SEEK_SET && origin != SEEK_CUR && origin != SEEK_END) throw std::runtime_error("fseek: invalid origin");
|
||||
if (std::fseek(GetNativeStream(stream), offset, origin) != 0) throw std::runtime_error("fseek: seek failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
long APS5_VABI ftell_nid_postfix(FileStream* stream) {
|
||||
const auto result = std::ftell(GetNativeStream(stream));
|
||||
if (result == -1L) throw std::runtime_error("ftell: position query failed");
|
||||
int APS5_VABI fseeko_nid_postfix(FileStream* stream, std::int64_t offset, int origin) {
|
||||
if (origin != SEEK_SET && origin != SEEK_CUR && origin != SEEK_END) { errno = 22; return -1; }
|
||||
#ifdef _WIN32
|
||||
const int result = _fseeki64(GetNativeStream(stream), offset, origin);
|
||||
#else
|
||||
static_assert(sizeof(off_t) == 8);
|
||||
const int result = ::fseeko(GetNativeStream(stream), offset, origin);
|
||||
#endif
|
||||
const int nativeError = errno;
|
||||
stream->SyncStatus();
|
||||
if (result) errno = nativeError == EOVERFLOW ? 84 : nativeError;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::int64_t APS5_VABI ftello_nid_postfix(FileStream* stream) {
|
||||
#ifdef _WIN32
|
||||
const auto result = _ftelli64(GetNativeStream(stream));
|
||||
#else
|
||||
static_assert(sizeof(off_t) == 8);
|
||||
const auto result = ::ftello(GetNativeStream(stream));
|
||||
#endif
|
||||
if (result == -1 && errno == EOVERFLOW) errno = 84;
|
||||
return result;
|
||||
}
|
||||
|
||||
int APS5_VABI fseek_nid_postfix(FileStream* stream, std::int64_t offset, int origin) {
|
||||
return fseeko_nid_postfix(stream, offset, origin);
|
||||
}
|
||||
|
||||
std::int64_t APS5_VABI ftell_nid_postfix(FileStream* stream) { return ftello_nid_postfix(stream); }
|
||||
|
||||
int APS5_VABI fputs_nid_postfix(const char* str, FileStream* stream) {
|
||||
if (!str) throw std::runtime_error("fputs: null string");
|
||||
const int result = std::fputs(str, GetNativeStream(stream));
|
||||
|
|
@ -116,6 +157,27 @@ int APS5_VABI posix_memalign_nid_postfix(void** pointer, size_t alignment, size_
|
|||
return ApplicationHeapPosixAlign_nid_no_patch(pointer, alignment, size);
|
||||
}
|
||||
|
||||
void* APS5_VABI bsearch_nid_postfix(const void* key, const void* base, size_t count,
|
||||
size_t size, int (APS5_VABI *compare)(const void*, const void*)) {
|
||||
if (count == 0) return nullptr;
|
||||
if (!key || !base || !compare || size == 0)
|
||||
throw std::invalid_argument("bsearch: invalid arguments");
|
||||
if (count > std::numeric_limits<size_t>::max() / size)
|
||||
throw std::overflow_error("bsearch: array size overflow");
|
||||
const auto* bytes = static_cast<const unsigned char*>(base);
|
||||
size_t first = 0;
|
||||
while (count != 0) {
|
||||
const size_t half = count / 2;
|
||||
const size_t middle = first + half;
|
||||
const auto* element = bytes + middle * size;
|
||||
const int result = compare(key, element);
|
||||
if (result == 0) return const_cast<unsigned char*>(element);
|
||||
if (result < 0) count = half;
|
||||
else { first = middle + 1; count -= half + 1; }
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void APS5_VABI qsort_nid_postfix(void* base, size_t count, size_t size, int (APS5_VABI *compare)(const void*, const void*)) {
|
||||
if (!compare) throw std::invalid_argument("qsort: null comparator");
|
||||
if (size == 0) throw std::invalid_argument("qsort: zero element size");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
#include "prx/libc/include/General.hpp"
|
||||
#include <cerrno>
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
int FilesystemError(const std::error_code& error) {
|
||||
if (error == std::errc::no_such_file_or_directory) return 2;
|
||||
if (error == std::errc::permission_denied) return 13;
|
||||
if (error == std::errc::operation_not_permitted) return 1;
|
||||
if (error == std::errc::not_a_directory) return 20;
|
||||
if (error == std::errc::is_a_directory) return 21;
|
||||
if (error == std::errc::directory_not_empty) return 66;
|
||||
if (error == std::errc::device_or_resource_busy) return 16;
|
||||
if (error == std::errc::read_only_file_system) return 30;
|
||||
if (error == std::errc::filename_too_long) return 63;
|
||||
if (error == std::errc::too_many_symbolic_link_levels) return 62;
|
||||
if (error == std::errc::not_enough_memory) return 12;
|
||||
if (error == std::errc::invalid_argument) return 22;
|
||||
if (error == std::errc::cross_device_link) return 18;
|
||||
if (error == std::errc::file_exists) return 17;
|
||||
if (error == std::errc::no_space_on_device) return 28;
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int APS5_VABI rename_nid_postfix(const char* from, const char* to) {
|
||||
if (!from || !to) { errno = 14; return -1; }
|
||||
if (!*from || !*to) { errno = 2; return -1; }
|
||||
try {
|
||||
const auto source = ResolvePath_nid_no_patch(from);
|
||||
const auto destination = ResolvePath_nid_no_patch(to);
|
||||
std::error_code error;
|
||||
std::filesystem::rename(source, destination, error);
|
||||
if (error) { errno = FilesystemError(error); return -1; }
|
||||
return 0;
|
||||
} catch (const std::bad_alloc&) { errno = 12; return -1; }
|
||||
catch (const std::filesystem::filesystem_error& error) {
|
||||
errno = FilesystemError(error.code());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int APS5_VABI remove_nid_postfix(const char* path) {
|
||||
if (!path) { errno = 14; return -1; }
|
||||
if (!*path) { errno = 2; return -1; }
|
||||
try {
|
||||
const auto resolved = ResolvePath_nid_no_patch(path);
|
||||
std::error_code error;
|
||||
#ifdef _WIN32
|
||||
const DWORD attributes = GetFileAttributesW(resolved.c_str());
|
||||
bool removed = false;
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES) {
|
||||
removed = (attributes & FILE_ATTRIBUTE_DIRECTORY) ?
|
||||
RemoveDirectoryW(resolved.c_str()) != 0 : DeleteFileW(resolved.c_str()) != 0;
|
||||
}
|
||||
if (!removed) {
|
||||
const DWORD nativeError = GetLastError();
|
||||
if (nativeError == ERROR_DIR_NOT_EMPTY) { errno = 66; return -1; }
|
||||
error = std::error_code(static_cast<int>(nativeError), std::system_category());
|
||||
}
|
||||
#else
|
||||
const bool removed = std::filesystem::remove(resolved, error);
|
||||
#endif
|
||||
if (error || !removed) {
|
||||
errno = error ? FilesystemError(error) : 2;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} catch (const std::bad_alloc&) { errno = 12; return -1; }
|
||||
catch (const std::filesystem::filesystem_error& error) {
|
||||
errno = FilesystemError(error.code());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstdarg>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/VarArgsAbi.hpp"
|
||||
#include "prx/libc/include/FileStream.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "prx/libc/include/WindowsFormatting.hpp"
|
||||
|
|
@ -11,6 +12,37 @@
|
|||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI vfprintf_nid_postfix(FileStream* stream, const char* format, VaList* args) {
|
||||
auto* native = GetNativeStream(stream);
|
||||
#ifdef _WIN32
|
||||
std::string buffer;
|
||||
const int count = LibcDetail::FormatWindows(nullptr, 0, format, args, &buffer);
|
||||
const int result = std::fwrite(buffer.data(), 1, static_cast<size_t>(count), native) ==
|
||||
static_cast<size_t>(count) ? count : -1;
|
||||
#else
|
||||
const int result = std::vfprintf(native, format, *reinterpret_cast<std::va_list*>(args));
|
||||
#endif
|
||||
stream->SyncStatus();
|
||||
return result;
|
||||
}
|
||||
|
||||
int APS5_VABI fprintf_nid_postfix(FileStream* stream, const char* format, ...) {
|
||||
#ifdef _WIN32
|
||||
__builtin_sysv_va_list args;
|
||||
__builtin_sysv_va_start(args, format);
|
||||
#else
|
||||
std::va_list args;
|
||||
va_start(args, format);
|
||||
#endif
|
||||
const int result = vfprintf_nid_postfix(stream, format, reinterpret_cast<VaList*>(args));
|
||||
#ifdef _WIN32
|
||||
__builtin_sysv_va_end(args);
|
||||
#else
|
||||
va_end(args);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
int APS5_VABI printf_nid_postfix(const char* format, ...) {
|
||||
|
|
@ -120,6 +152,14 @@ int APS5_VABI vprintf_nid_postfix(const char* str, VaList* c) {
|
|||
#endif
|
||||
}
|
||||
|
||||
int APS5_VABI vsprintf_nid_postfix(char* str, const char* format, VaList* args) {
|
||||
#ifdef _WIN32
|
||||
return LibcDetail::FormatWindows(str, SIZE_MAX, format, args);
|
||||
#else
|
||||
return std::vsprintf(str, format, *reinterpret_cast<std::va_list*>(args));
|
||||
#endif
|
||||
}
|
||||
|
||||
int APS5_VABI vsnprintf_nid_postfix(char* str, size_t size, const char* format, VaList* c) {
|
||||
#ifdef _WIN32
|
||||
return LibcDetail::FormatWindows(str, size, format, c);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
#include <optional>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/GuestHeap.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
// Guest prefixes (without leading slashes) mapped to host directories, e.g. save-data mount points:
|
||||
// the PS5 hands the title a short mount point ("/_sm/0") whose files live under _sd/<dir name>.
|
||||
struct PathAliases {
|
||||
|
|
@ -34,6 +36,47 @@ std::string TrimSlashes(const char* path) {
|
|||
return s.substr(start, end - start);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> ResolveAlias(const std::string& guestPath) {
|
||||
const auto relative = TrimSlashes(guestPath.c_str());
|
||||
auto& aliases = Aliases();
|
||||
std::lock_guard lock(aliases.mutex);
|
||||
for (const auto& [prefix, host] : aliases.entries) {
|
||||
if (relative.size() < prefix.size() || relative.compare(0, prefix.size(), prefix) != 0) continue;
|
||||
if (relative.size() == prefix.size()) return std::filesystem::path(host).make_preferred();
|
||||
if (relative[prefix.size()] != '/') continue;
|
||||
std::filesystem::path result = std::filesystem::path(host) / std::filesystem::path(relative.substr(prefix.size() + 1));
|
||||
return result.make_preferred();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
struct WorkingDirectory {
|
||||
std::mutex mutex;
|
||||
const std::filesystem::path root = std::filesystem::canonical(std::filesystem::current_path());
|
||||
std::filesystem::path current = root;
|
||||
};
|
||||
WorkingDirectory& Directories() { static WorkingDirectory state; return state; }
|
||||
std::filesystem::path Resolve(WorkingDirectory& state, const char* path) {
|
||||
std::string text(path);
|
||||
for (auto& character : text) if (character == '\\') character = '/';
|
||||
std::filesystem::path input(text);
|
||||
#ifdef _WIN32
|
||||
// Preserve the existing ability to pass explicit native drive paths.
|
||||
if (input.has_root_name()) return input;
|
||||
#endif
|
||||
auto guest = (std::filesystem::path("/") / state.current.lexically_relative(state.root));
|
||||
guest = (input.is_absolute() ? input : guest / input).lexically_normal();
|
||||
if (auto aliased = ResolveAlias(guest.relative_path().generic_string())) return *aliased;
|
||||
return (state.root / guest.relative_path()).make_preferred();
|
||||
}
|
||||
int DirectoryFailure(const std::error_code& error) {
|
||||
if (error == std::errc::permission_denied) return 13;
|
||||
if (error == std::errc::not_a_directory) return 20;
|
||||
if (error == std::errc::no_such_file_or_directory) return 2;
|
||||
if (error == std::errc::filename_too_long) return 63;
|
||||
if (error == std::errc::too_many_symbolic_link_levels) return 62;
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void AddPathAlias_nid_no_patch(const char* guestPrefix, const char* hostPath) {
|
||||
|
|
@ -63,23 +106,50 @@ extern "C" void RemovePathAlias_nid_no_patch(const char* guestPrefix) {
|
|||
}
|
||||
|
||||
extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path) {
|
||||
if (path == nullptr) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
const auto relative = TrimSlashes(path);
|
||||
{
|
||||
auto& aliases = Aliases();
|
||||
std::lock_guard lock(aliases.mutex);
|
||||
for (const auto& [prefix, host] : aliases.entries) {
|
||||
if (relative.size() < prefix.size() || relative.compare(0, prefix.size(), prefix) != 0) continue;
|
||||
if (relative.size() == prefix.size()) return std::filesystem::path(host).make_preferred();
|
||||
if (relative[prefix.size()] != '/' && relative[prefix.size()] != '\\') continue;
|
||||
std::filesystem::path result = std::filesystem::path(host) / std::filesystem::path(relative.substr(prefix.size() + 1));
|
||||
return result.make_preferred();
|
||||
if (!path) { APS5_INVALID_ARG_EX; }
|
||||
auto& state = Directories();
|
||||
std::lock_guard lock(state.mutex);
|
||||
return Resolve(state, path);
|
||||
}
|
||||
|
||||
extern "C" int APS5_VABI chdir_nid_postfix(const char* path) {
|
||||
if (!path) { errno = 14; return -1; }
|
||||
if (!*path) { errno = 2; return -1; }
|
||||
try {
|
||||
auto& state = Directories();
|
||||
std::lock_guard lock(state.mutex);
|
||||
std::error_code error;
|
||||
const auto resolved = std::filesystem::canonical(Resolve(state, path), error);
|
||||
if (error) { errno = DirectoryFailure(error); return -1; }
|
||||
if (!std::filesystem::is_directory(resolved, error)) {
|
||||
errno = error ? DirectoryFailure(error) : 20; return -1;
|
||||
}
|
||||
}
|
||||
std::filesystem::path result = std::filesystem::current_path() / std::filesystem::path(relative);
|
||||
return result.make_preferred();
|
||||
const auto relative = resolved.lexically_relative(state.root);
|
||||
if (relative.empty() || *relative.begin() == "..") { errno = 45; return -1; }
|
||||
state.current = resolved;
|
||||
return 0;
|
||||
} catch (const std::bad_alloc&) { errno = 12; return -1; }
|
||||
catch (const std::filesystem::filesystem_error& error) { errno = DirectoryFailure(error.code()); return -1; }
|
||||
}
|
||||
|
||||
extern "C" char* APS5_VABI getcwd_nid_postfix(char* buffer, std::size_t size) {
|
||||
if (buffer && size == 0) { errno = 22; return nullptr; }
|
||||
try {
|
||||
auto& state = Directories();
|
||||
std::lock_guard lock(state.mutex);
|
||||
std::error_code error;
|
||||
if (!std::filesystem::is_directory(state.current, error)) {
|
||||
errno = error ? DirectoryFailure(error) : 2; return nullptr;
|
||||
}
|
||||
const auto relative = state.current.lexically_relative(state.root);
|
||||
const auto path = relative == "." ? std::string("/") : "/" + relative.generic_string();
|
||||
const auto required = path.size() + 1;
|
||||
if ((buffer || size) && size < required) { errno = 34; return nullptr; }
|
||||
if (!buffer) buffer = static_cast<char*>(GuestHeap::GuestHeapAllocate_nid_postfix(size ? size : required));
|
||||
std::memcpy(buffer, path.c_str(), required);
|
||||
return buffer;
|
||||
} catch (const std::bad_alloc&) { errno = 12; return nullptr; }
|
||||
catch (const std::filesystem::filesystem_error& error) { errno = DirectoryFailure(error.code()); return nullptr; }
|
||||
}
|
||||
|
||||
extern "C" void NotImplemented_nid_no_patch(const char* funcName) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <link.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace GuestAllocations {
|
||||
|
|
@ -116,6 +119,51 @@ void GuestAllocationsRegisterMainImage_nid_postfix(void*) {
|
|||
state.ranges.swap(replacement);
|
||||
state.mainImageRegistered = true;
|
||||
}
|
||||
#else
|
||||
void GuestAllocationsRegisterMainImage_nid_postfix(void*) {
|
||||
auto& state = registry();
|
||||
if (state.mainImageRegistered) return;
|
||||
struct Page {
|
||||
bool readable = false;
|
||||
bool writable = false;
|
||||
};
|
||||
std::map<std::uint64_t, Page> pages;
|
||||
const auto pageSize = static_cast<std::uint64_t>(::sysconf(_SC_PAGESIZE));
|
||||
std::pair<std::map<std::uint64_t, Page>*, std::uint64_t> collection{&pages, pageSize};
|
||||
dl_iterate_phdr([](dl_phdr_info* image, std::size_t, void* data) {
|
||||
auto& collected = *static_cast<std::pair<std::map<std::uint64_t, Page>*, std::uint64_t>*>(data);
|
||||
for (int index = 0; index < image->dlpi_phnum; ++index) {
|
||||
const auto& header = image->dlpi_phdr[index];
|
||||
if (header.p_type != PT_LOAD || header.p_memsz == 0) continue;
|
||||
const auto start = (image->dlpi_addr + header.p_vaddr) & ~(collected.second - 1);
|
||||
const auto end = (image->dlpi_addr + header.p_vaddr + header.p_memsz + collected.second - 1) & ~(collected.second - 1);
|
||||
for (auto page = start; page < end; page += collected.second) {
|
||||
auto& entry = (*collected.first)[page];
|
||||
entry.readable = entry.readable || (header.p_flags & (PF_R | PF_W)) != 0;
|
||||
entry.writable = entry.writable || (header.p_flags & PF_W) != 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}, &collection);
|
||||
require(!pages.empty(), "main guest image has no loadable segments");
|
||||
auto replacement = state.ranges;
|
||||
for (auto page = pages.begin(); page != pages.end();) {
|
||||
auto last = page;
|
||||
while (std::next(last) != pages.end() && std::next(last)->first == last->first + pageSize && std::next(last)->second.readable == page->second.readable && std::next(last)->second.writable == page->second.writable) ++last;
|
||||
const auto address = page->first;
|
||||
const auto bytes = static_cast<std::size_t>(last->first + pageSize - address);
|
||||
const auto next = replacement.lower_bound(address);
|
||||
require(next == replacement.end() || address + bytes <= next->first, "guest image overlaps a registered allocation");
|
||||
if (next != replacement.begin()) {
|
||||
const auto& previous = *std::prev(next)->second;
|
||||
require(previous.address + previous.bytes <= address, "guest image overlaps a registered allocation");
|
||||
}
|
||||
replacement.emplace(address, std::make_shared<const Range>(Range{address, bytes, page->second.readable, page->second.writable, address, bytes, false}));
|
||||
page = std::next(last);
|
||||
}
|
||||
state.ranges.swap(replacement);
|
||||
state.mainImageRegistered = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void GuestAllocationsAdd_nid_postfix(void* mutation, void* pointer, std::size_t bytes, bool readable, bool writable) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/HeapDiagnostics.hpp"
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace {
|
||||
|
||||
std::uint64_t mspaceAtomicIdMask = 0;
|
||||
std::array<std::uint64_t, 64> mstateTable{};
|
||||
|
||||
static_assert(sizeof(LibcHeapInfo) == 32);
|
||||
static_assert(offsetof(LibcHeapInfo, mspace_atomic_id_mask) == 16);
|
||||
static_assert(offsetof(LibcHeapInfo, mstate_table) == 24);
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void LibcHeapTraceInfo_nid_no_patch(LibcHeapInfo* info) {
|
||||
if (info == nullptr)
|
||||
throw std::invalid_argument("heap trace info: null output");
|
||||
if (info->size != sizeof(LibcHeapInfo))
|
||||
throw std::invalid_argument("heap trace info: unsupported structure size");
|
||||
info->unknown2 = 0;
|
||||
info->mspace_atomic_id_mask = &mspaceAtomicIdMask;
|
||||
info->mstate_table = mstateTable.data();
|
||||
}
|
||||
|
||||
// unknown signature
|
||||
void APS5_VABI sceLibcInternalBacktraceForGame_nid_postfix(const char* heapName) {
|
||||
(void)heapName;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
}
|
||||
|
||||
// unknown signature
|
||||
void APS5_VABI sceLibcInternalHeapErrorReportForGame_nid_postfix(void* heap, void* block, std::uint32_t error) {
|
||||
(void)heap;
|
||||
(void)block;
|
||||
(void)error;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cwchar>
|
||||
#include <cwctype>
|
||||
#include <ios>
|
||||
#include <locale>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
|
|
@ -100,10 +103,43 @@ constexpr auto g_classificationTable = MakeClassificationTable();
|
|||
constexpr auto g_lowerTable = MakeCaseTable(false);
|
||||
constexpr auto g_upperTable = MakeCaseTable(true);
|
||||
|
||||
void ValidateCharacter(int value) {
|
||||
if (value != EOF && (value < 0 || value > UCHAR_MAX)) throw std::invalid_argument("Invalid character value");
|
||||
}
|
||||
|
||||
int ClassifyCharacter(int value, std::ctype_base::mask mask) {
|
||||
ValidateCharacter(value);
|
||||
if (value == EOF) return 0;
|
||||
return std::use_facet<std::ctype<char>>(std::locale::classic()).is(mask, static_cast<char>(value));
|
||||
}
|
||||
|
||||
int ConvertCharacter(int value, bool upper) {
|
||||
ValidateCharacter(value);
|
||||
if (value == EOF) return EOF;
|
||||
const auto& facet = std::use_facet<std::ctype<char>>(std::locale::classic());
|
||||
const auto character = static_cast<char>(value);
|
||||
return static_cast<unsigned char>(upper ? facet.toupper(character) : facet.tolower(character));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI isupper_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::upper); }
|
||||
int APS5_VABI islower_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::lower); }
|
||||
int APS5_VABI isalpha_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::alpha); }
|
||||
int APS5_VABI isdigit_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::digit); }
|
||||
int APS5_VABI isalnum_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::alnum); }
|
||||
int APS5_VABI isspace_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::space); }
|
||||
int APS5_VABI isblank_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::blank); }
|
||||
int APS5_VABI iscntrl_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::cntrl); }
|
||||
int APS5_VABI isprint_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::print); }
|
||||
int APS5_VABI isgraph_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::graph); }
|
||||
int APS5_VABI ispunct_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::punct); }
|
||||
int APS5_VABI isxdigit_nid_postfix(int c) { return ClassifyCharacter(c, std::ctype_base::xdigit); }
|
||||
int APS5_VABI toupper_nid_postfix(int c) { return ConvertCharacter(c, true); }
|
||||
int APS5_VABI tolower_nid_postfix(int c) { return ConvertCharacter(c, false); }
|
||||
|
||||
std::uint64_t _ZNSt5ctypeIcE2idE_nid_postfix = 0;
|
||||
std::uint64_t _ZNSt5ctypeIwE2idE_nid_postfix = 0;
|
||||
std::uint64_t _ZNSt7collateIwE2idE_nid_postfix = 0;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,33 @@
|
|||
#include <mutex>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
float APS5_VABI fmodf_nid_postfix(float x, float y) { return std::fmod(x, y); }
|
||||
float APS5_VABI asinf_nid_postfix(float x) { return std::asin(x); }
|
||||
float APS5_VABI acosf_nid_postfix(float x) { return std::acos(x); }
|
||||
float APS5_VABI atan2f_nid_postfix(float y, float x) { return std::atan2(y, x); }
|
||||
float APS5_VABI tanf_nid_postfix(float x) { return std::tan(x); }
|
||||
float APS5_VABI log10f_nid_postfix(float x) { return std::log10(x); }
|
||||
double APS5_VABI exp2_nid_postfix(double x) { return std::exp2(x); }
|
||||
double APS5_VABI ldexp_nid_postfix(double x, int exponent) { return std::ldexp(x, exponent); }
|
||||
double APS5_VABI scalbn_nid_postfix(double x, int exponent) { return std::scalbn(x, exponent); }
|
||||
float APS5_VABI scalbnf_nid_postfix(float x, int exponent) { return std::scalbn(x, exponent); }
|
||||
double APS5_VABI frexp_nid_postfix(double x, int* exponent) { return std::frexp(x, exponent); }
|
||||
float APS5_VABI frexpf_nid_postfix(float x, int* exponent) { return std::frexp(x, exponent); }
|
||||
// Guest long is 64-bit, including on Windows where native long is 32-bit.
|
||||
std::int64_t APS5_VABI lround_nid_postfix(double x) { return std::llround(x); }
|
||||
std::int64_t APS5_VABI lroundf_nid_postfix(float x) { return std::llround(x); }
|
||||
std::int64_t APS5_VABI llround_nid_postfix(double x) { return std::llround(x); }
|
||||
int APS5_VABI __isfinitef_nid_postfix(float x) { return std::isfinite(x) ? 1 : 0; }
|
||||
int APS5_VABI __isnormal_nid_postfix(double x) { return std::isnormal(x) ? 1 : 0; }
|
||||
int APS5_VABI __isnormalf_nid_postfix(float x) { return std::isnormal(x) ? 1 : 0; }
|
||||
int APS5_VABI __isinff_nid_postfix(float x) { return std::isinf(x) ? 1 : 0; }
|
||||
|
||||
double APS5_VABI cbrt_nid_postfix(double x) { return std::cbrt(x); }
|
||||
double APS5_VABI asin_nid_postfix(double x) { return std::asin(x); }
|
||||
double APS5_VABI acos_nid_postfix(double x) { return std::acos(x); }
|
||||
|
|
@ -51,21 +73,11 @@ int APS5_VABI __signbit_nid_postfix(double x) { return std::signbit(x) ? 1 : 0;
|
|||
|
||||
double APS5_VABI modf_nid_postfix(double x, double* integral) { return std::modf(x, integral); }
|
||||
float APS5_VABI modff_nid_postfix(float x, float* integral) { return std::modf(x, integral); }
|
||||
double APS5_VABI frexp_nid_postfix(double x, int* exponent) { return std::frexp(x, exponent); }
|
||||
float APS5_VABI frexpf_nid_postfix(float x, int* exponent) { return std::frexp(x, exponent); }
|
||||
double APS5_VABI ldexp_nid_postfix(double x, int exponent) { return std::ldexp(x, exponent); }
|
||||
float APS5_VABI fmodf_nid_postfix(float x, float y) { return std::fmod(x, y); }
|
||||
float APS5_VABI atan2f_nid_postfix(float y, float x) { return std::atan2(y, x); }
|
||||
float APS5_VABI asinf_nid_postfix(float x) { return std::asin(x); }
|
||||
float APS5_VABI acosf_nid_postfix(float x) { return std::acos(x); }
|
||||
float APS5_VABI tanf_nid_postfix(float x) { return std::tan(x); }
|
||||
double APS5_VABI tanh_nid_postfix(double x) { return std::tanh(x); }
|
||||
float APS5_VABI tanhf_nid_postfix(float x) { return std::tanh(x); }
|
||||
float APS5_VABI log10f_nid_postfix(float x) { return std::log10(x); }
|
||||
float APS5_VABI _FSinh_nid_postfix(float x, float y) { return y * std::sinh(x); }
|
||||
float APS5_VABI _FCosh_nid_postfix(float x, float y) { return y * std::cosh(x); }
|
||||
int APS5_VABI __isnanf_nid_postfix(float x) { return std::isnan(x) ? 1 : 0; }
|
||||
int APS5_VABI __isinff_nid_postfix(float x) { return std::isinf(x) ? 1 : 0; }
|
||||
int APS5_VABI __signbitf_nid_postfix(float x) { return std::signbit(x) ? 1 : 0; }
|
||||
|
||||
static std::mutex g_randLock;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
|
||||
extern "C" int* APS5_VABI __error_nid_postfix();
|
||||
|
||||
namespace {
|
||||
struct alignas(16) Block {
|
||||
std::size_t capacity;
|
||||
Block* next;
|
||||
void* pointer;
|
||||
std::size_t used;
|
||||
};
|
||||
struct alignas(16) Arena {
|
||||
Arena* next;
|
||||
Block* first;
|
||||
std::uintptr_t end;
|
||||
};
|
||||
std::mutex arenaMutex;
|
||||
Arena* arenas = nullptr;
|
||||
|
||||
void Error(int value) { *__error_nid_postfix() = value; }
|
||||
Arena* Find(void* handle) {
|
||||
for (auto* arena = arenas; arena; arena = arena->next)
|
||||
if (arena == handle) return arena;
|
||||
Error(22);
|
||||
return nullptr;
|
||||
}
|
||||
Block* FindBlock(Arena* arena, const void* pointer) {
|
||||
if (arena && pointer)
|
||||
for (auto* block = arena->first; block; block = block->next)
|
||||
if (block->pointer == pointer) return block;
|
||||
Error(22);
|
||||
return nullptr;
|
||||
}
|
||||
void* Allocate(Arena* arena, std::size_t size, std::size_t alignment) {
|
||||
if (!arena) return nullptr;
|
||||
size = std::max<std::size_t>(size, 1);
|
||||
for (auto* block = arena->first; block; block = block->next) {
|
||||
if (block->pointer) continue;
|
||||
const auto start = reinterpret_cast<std::uintptr_t>(block + 1);
|
||||
if (start > std::numeric_limits<std::uintptr_t>::max() - (alignment - 1)) continue;
|
||||
const auto aligned = (start + alignment - 1) & ~(alignment - 1);
|
||||
const auto padding = aligned - start;
|
||||
if (padding > block->capacity || size > block->capacity - padding) continue;
|
||||
auto consumed = padding + size;
|
||||
// Splitting preserves header alignment and avoids small unusable fragments.
|
||||
if (consumed <= std::numeric_limits<std::size_t>::max() - 15) {
|
||||
const auto rounded = (consumed + 15) & ~std::size_t{15};
|
||||
if (rounded <= block->capacity && block->capacity - rounded >= sizeof(Block) + 16) {
|
||||
auto* tail = reinterpret_cast<Block*>(start + rounded);
|
||||
*tail = {block->capacity - rounded - sizeof(Block), block->next, nullptr, 0};
|
||||
block->capacity = rounded;
|
||||
block->next = tail;
|
||||
}
|
||||
}
|
||||
block->pointer = reinterpret_cast<void*>(aligned);
|
||||
block->used = size;
|
||||
return block->pointer;
|
||||
}
|
||||
Error(12);
|
||||
return nullptr;
|
||||
}
|
||||
void Release(Arena* arena, Block* released) {
|
||||
released->pointer = nullptr;
|
||||
released->used = 0;
|
||||
for (auto* block = arena->first; block && block->next;) {
|
||||
if (!block->pointer && !block->next->pointer) {
|
||||
block->capacity += sizeof(Block) + block->next->capacity;
|
||||
block->next = block->next->next;
|
||||
} else block = block->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void* APS5_VABI sceLibcMspaceCreate_nid_postfix(const char* name, void* base,
|
||||
std::size_t size, unsigned flags) {
|
||||
(void)name;
|
||||
const auto start = reinterpret_cast<std::uintptr_t>(base);
|
||||
if (!base || (start & 15) || size < sizeof(Arena) + sizeof(Block) + 16 ||
|
||||
size > std::numeric_limits<std::uintptr_t>::max() - start || flags != 0) {
|
||||
Error(22);
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard lock(arenaMutex);
|
||||
for (auto* arena = arenas; arena; arena = arena->next) {
|
||||
if (start < arena->end && reinterpret_cast<std::uintptr_t>(arena) < start + size) {
|
||||
Error(22);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
auto* arena = static_cast<Arena*>(base);
|
||||
auto* block = reinterpret_cast<Block*>(arena + 1);
|
||||
*block = {size - sizeof(Arena) - sizeof(Block), nullptr, nullptr, 0};
|
||||
*arena = {arenas, block, start + size};
|
||||
arenas = arena;
|
||||
return arena;
|
||||
}
|
||||
|
||||
int APS5_VABI sceLibcMspaceDestroy_nid_postfix(void* handle) {
|
||||
std::lock_guard lock(arenaMutex);
|
||||
for (auto** entry = &arenas; *entry; entry = &(*entry)->next) {
|
||||
if (*entry == handle) {
|
||||
*entry = (*entry)->next;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Error(22);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void* APS5_VABI sceLibcMspaceMalloc_nid_postfix(void* handle, std::size_t size) {
|
||||
std::lock_guard lock(arenaMutex);
|
||||
return Allocate(Find(handle), size, 16);
|
||||
}
|
||||
|
||||
void APS5_VABI sceLibcMspaceFree_nid_postfix(void* handle, void* pointer) {
|
||||
if (!pointer) return;
|
||||
std::lock_guard lock(arenaMutex);
|
||||
auto* arena = Find(handle);
|
||||
if (auto* block = FindBlock(arena, pointer)) Release(arena, block);
|
||||
}
|
||||
|
||||
void* APS5_VABI sceLibcMspaceCalloc_nid_postfix(void* handle, std::size_t count, std::size_t size) {
|
||||
if (size && count > std::numeric_limits<std::size_t>::max() / size) {
|
||||
Error(12);
|
||||
return nullptr;
|
||||
}
|
||||
std::lock_guard lock(arenaMutex);
|
||||
void* result = Allocate(Find(handle), count * size, 16);
|
||||
if (result) std::memset(result, 0, count * size);
|
||||
return result;
|
||||
}
|
||||
|
||||
void* APS5_VABI sceLibcMspaceRealloc_nid_postfix(void* handle, void* pointer, std::size_t size) {
|
||||
std::lock_guard lock(arenaMutex);
|
||||
auto* arena = Find(handle);
|
||||
if (!pointer) return Allocate(arena, size, 16);
|
||||
auto* block = FindBlock(arena, pointer);
|
||||
if (!block) return nullptr;
|
||||
if (!size) { Release(arena, block); return nullptr; }
|
||||
const auto padding = reinterpret_cast<std::uintptr_t>(pointer) - reinterpret_cast<std::uintptr_t>(block + 1);
|
||||
if (size <= block->capacity - padding) { block->used = size; return pointer; }
|
||||
void* result = Allocate(arena, size, 16);
|
||||
if (result) {
|
||||
std::memcpy(result, pointer, block->used);
|
||||
Release(arena, block);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int APS5_VABI sceLibcMspacePosixMemalign_nid_postfix(void* handle, void** result,
|
||||
std::size_t alignment, std::size_t size) {
|
||||
if (!result || alignment < sizeof(void*) || (alignment & (alignment - 1))) return 22;
|
||||
std::lock_guard lock(arenaMutex);
|
||||
auto* arena = Find(handle);
|
||||
if (!arena) return 22;
|
||||
void* pointer = Allocate(arena, size, std::max<std::size_t>(alignment, 16));
|
||||
if (!pointer) return 12;
|
||||
*result = pointer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::size_t APS5_VABI sceLibcMspaceMallocUsableSize_nid_postfix(const void* pointer) {
|
||||
if (!pointer) return 0;
|
||||
std::lock_guard lock(arenaMutex);
|
||||
for (auto* arena = arenas; arena; arena = arena->next)
|
||||
for (auto* block = arena->first; block; block = block->next)
|
||||
if (block->pointer == pointer) return block->used;
|
||||
Error(22);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -58,14 +58,27 @@ extern "C" void LibcRunShutdown_nid_postfix() {
|
|||
|
||||
extern "C" {
|
||||
|
||||
[[noreturn]] void APS5_VABI _Exit_nid_postfix(int code) {
|
||||
static const bool trace = std::getenv("APS5_TRACE_EXIT") != nullptr;
|
||||
if (trace) {
|
||||
std::fprintf(stderr, "[libc] _Exit(%d) called from %p\n", code, __builtin_return_address(0));
|
||||
std::fflush(stderr);
|
||||
}
|
||||
std::_Exit(code);
|
||||
}
|
||||
|
||||
[[noreturn]] void LibcExit_nid_no_patch(int code) {
|
||||
LibcRunShutdown_nid_postfix();
|
||||
std::exit(code);
|
||||
}
|
||||
|
||||
void APS5_VABI exit_nid_postfix(int code) {
|
||||
static const bool trace = std::getenv("APS5_TRACE_EXIT") != nullptr;
|
||||
if (trace) {
|
||||
std::fprintf(stderr, "[libc] exit(%d) called from %p\n", code, __builtin_return_address(0));
|
||||
std::fflush(stderr);
|
||||
}
|
||||
LibcRunShutdown_nid_postfix();
|
||||
std::exit(code);
|
||||
LibcExit_nid_no_patch(code);
|
||||
}
|
||||
|
||||
[[noreturn]] void abort_nid_postfix(
|
||||
|
|
@ -98,5 +111,3 @@ int APS5_VABI atexit_nid_postfix(atexit_func_t func) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#include "prx/libc/src/specifics/linux/PosixProcess.cpp"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
#include "prx/libc/include/FileStream.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace { FileStream input{stdin}; }
|
||||
extern "C" {
|
||||
FileStream* __stdinp_nid_postfix = &input;
|
||||
FileStream* __stdoutp_nid_postfix = &_Stdout_nid_postfix;
|
||||
FileStream* __stderrp_nid_postfix = &_Stderr_nid_postfix;
|
||||
int __isthreaded_nid_postfix = 1;
|
||||
|
||||
int APS5_VABI fgetc_nid_postfix(FileStream* stream) {
|
||||
const int result = std::fgetc(GetNativeStream(stream));
|
||||
stream->SyncStatus();
|
||||
return result;
|
||||
}
|
||||
int APS5_VABI getc_nid_postfix(FileStream* stream) { return fgetc_nid_postfix(stream); }
|
||||
int APS5_VABI __srget_nid_postfix(FileStream* stream) { return fgetc_nid_postfix(stream); }
|
||||
int APS5_VABI getchar_nid_postfix() { return fgetc_nid_postfix(__stdinp_nid_postfix); }
|
||||
int APS5_VABI fputc_nid_postfix(int value, FileStream* stream) {
|
||||
const int result = std::fputc(value, GetNativeStream(stream));
|
||||
stream->SyncStatus();
|
||||
return result;
|
||||
}
|
||||
int APS5_VABI putc_nid_postfix(int value, FileStream* stream) { return fputc_nid_postfix(value, stream); }
|
||||
int APS5_VABI __swbuf_nid_postfix(int value, FileStream* stream) { return fputc_nid_postfix(value, stream); }
|
||||
int APS5_VABI putchar_nid_postfix(int value) { return fputc_nid_postfix(value, __stdoutp_nid_postfix); }
|
||||
int APS5_VABI ungetc_nid_postfix(int value, FileStream* stream) {
|
||||
const int result = std::ungetc(value, GetNativeStream(stream));
|
||||
stream->SyncStatus();
|
||||
return result;
|
||||
}
|
||||
char* APS5_VABI fgets_nid_postfix(char* buffer, int size, FileStream* stream) {
|
||||
auto* result = std::fgets(buffer, size, GetNativeStream(stream));
|
||||
stream->SyncStatus();
|
||||
return result;
|
||||
}
|
||||
int APS5_VABI feof_nid_postfix(FileStream* stream) {
|
||||
stream->SyncStatus();
|
||||
return (stream->GuestState().flags & 0x20) != 0;
|
||||
}
|
||||
int APS5_VABI ferror_nid_postfix(FileStream* stream) {
|
||||
stream->SyncStatus();
|
||||
return (stream->GuestState().flags & 0x40) != 0;
|
||||
}
|
||||
void APS5_VABI clearerr_nid_postfix(FileStream* stream) {
|
||||
std::clearerr(GetNativeStream(stream));
|
||||
stream->SyncStatus();
|
||||
}
|
||||
int APS5_VABI fileno_nid_postfix(FileStream* stream) {
|
||||
#ifdef _WIN32
|
||||
const int descriptor = _fileno(GetNativeStream(stream));
|
||||
#else
|
||||
const int descriptor = ::fileno(GetNativeStream(stream));
|
||||
#endif
|
||||
stream->GuestState().descriptor = static_cast<std::int16_t>(descriptor);
|
||||
return descriptor;
|
||||
}
|
||||
int APS5_VABI setvbuf_nid_postfix(FileStream* stream, char* buffer, int mode, std::size_t size) {
|
||||
if (mode < 0 || mode > 2) return -1;
|
||||
const int native = mode == 0 ? _IOFBF : mode == 1 ? _IOLBF : _IONBF;
|
||||
return std::setvbuf(GetNativeStream(stream), buffer, native, size);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,8 @@
|
|||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "prx/libc/include/FileStream.hpp"
|
||||
#include "prx/libc/include/GuestHeap.hpp"
|
||||
|
|
@ -18,100 +15,16 @@
|
|||
|
||||
namespace {
|
||||
|
||||
using GuestCompare = int (APS5_VABI*)(const void*, const void*);
|
||||
using GuestNewHandler = void (APS5_VABI*)();
|
||||
|
||||
GuestNewHandler g_newHandler = nullptr;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Formats with a guest (SysV) va_list into a host string.
|
||||
std::string FormatGuest(const char* format, const void* args) {
|
||||
std::string text;
|
||||
LibcDetail::FormatWindows(nullptr, 0, format, args, &text);
|
||||
return text;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void* APS5_VABI bsearch_nid_postfix(const void* key, const void* base, size_t count, size_t size, GuestCompare compare) {
|
||||
const auto* bytes = static_cast<const unsigned char*>(base);
|
||||
size_t low = 0;
|
||||
size_t high = count;
|
||||
while (low < high) {
|
||||
const size_t middle = low + (high - low) / 2;
|
||||
const void* element = bytes + middle * size;
|
||||
const int order = compare(key, element);
|
||||
if (order == 0) return const_cast<void*>(element);
|
||||
if (order < 0) high = middle;
|
||||
else low = middle + 1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void APS5_VABI clearerr_nid_postfix(FileStream* stream) {
|
||||
std::clearerr(GetNativeStream(stream));
|
||||
}
|
||||
|
||||
int APS5_VABI feof_nid_postfix(FileStream* stream) {
|
||||
return std::feof(GetNativeStream(stream));
|
||||
}
|
||||
|
||||
int APS5_VABI ferror_nid_postfix(FileStream* stream) {
|
||||
return std::ferror(GetNativeStream(stream));
|
||||
}
|
||||
|
||||
int APS5_VABI fputc_nid_postfix(int character, FileStream* stream) {
|
||||
return std::fputc(character, GetNativeStream(stream));
|
||||
}
|
||||
|
||||
int APS5_VABI getc_nid_postfix(FileStream* stream) {
|
||||
return std::getc(GetNativeStream(stream));
|
||||
}
|
||||
|
||||
int APS5_VABI ungetc_nid_postfix(int character, FileStream* stream) {
|
||||
return std::ungetc(character, GetNativeStream(stream));
|
||||
}
|
||||
|
||||
char* APS5_VABI fgets_nid_postfix(char* buffer, int count, FileStream* stream) {
|
||||
return std::fgets(buffer, count, GetNativeStream(stream));
|
||||
}
|
||||
|
||||
int APS5_VABI putchar_nid_postfix(int character) {
|
||||
return std::putchar(character);
|
||||
}
|
||||
|
||||
void APS5_VABI perror_nid_postfix(const char* prefix) {
|
||||
if (prefix && *prefix) std::fprintf(stderr, "%s: %s\n", prefix, std::strerror(errno));
|
||||
else std::fprintf(stderr, "%s\n", std::strerror(errno));
|
||||
}
|
||||
|
||||
int APS5_VABI remove_nid_postfix(const char* path) {
|
||||
std::error_code error;
|
||||
return std::filesystem::remove(ResolvePath_nid_no_patch(path), error) ? 0 : -1;
|
||||
}
|
||||
|
||||
// FreeBSD and the host CRT agree on the classic errno values the messages are looked up by.
|
||||
char* APS5_VABI strerror_nid_postfix(int error) {
|
||||
return std::strerror(error);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
int APS5_VABI fprintf_nid_postfix(FileStream* stream, const char* format, ...) {
|
||||
__builtin_sysv_va_list args;
|
||||
__builtin_sysv_va_start(args, format);
|
||||
const auto text = FormatGuest(format, args);
|
||||
__builtin_sysv_va_end(args);
|
||||
return static_cast<int>(std::fwrite(text.data(), 1, text.size(), GetNativeStream(stream)));
|
||||
}
|
||||
|
||||
int APS5_VABI vsprintf_nid_postfix(char* buffer, const char* format, VaList* args) {
|
||||
return LibcDetail::FormatWindows(buffer, SIZE_MAX, format, args);
|
||||
}
|
||||
|
||||
int APS5_VABI vsprintf_s_nid_postfix(char* buffer, size_t size, const char* format, VaList* args) {
|
||||
return LibcDetail::FormatWindows(buffer, size, format, args);
|
||||
}
|
||||
|
|
@ -132,13 +45,6 @@ int APS5_VABI sprintf_s_nid_postfix(char* buffer, size_t size, const char* forma
|
|||
std::abort();
|
||||
}
|
||||
|
||||
[[noreturn]] void APS5_VABI _Exit_nid_postfix(int status) {
|
||||
static const bool trace = std::getenv("APS5_TRACE_EXIT") != nullptr;
|
||||
if (trace) std::fprintf(stderr, "[libc] _Exit(%d) called from %p\n", status, __builtin_return_address(0));
|
||||
std::fflush(nullptr);
|
||||
std::_Exit(status);
|
||||
}
|
||||
|
||||
// Only the "C" locale exists.
|
||||
const char* APS5_VABI setlocale_nid_postfix(int category, const char* locale) {
|
||||
(void)category;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <string_view>
|
||||
|
||||
extern "C" {
|
||||
|
||||
char* APS5_VABI basename_nid_postfix(const char* path) {
|
||||
thread_local char buffer[1024];
|
||||
std::string_view name = path && *path ? path : ".";
|
||||
while (name.size() > 1 && name.back() == '/') name.remove_suffix(1);
|
||||
if (name != "/") {
|
||||
const auto separator = name.find_last_of('/');
|
||||
if (separator != std::string_view::npos) name.remove_prefix(separator + 1);
|
||||
}
|
||||
if (name.size() >= sizeof(buffer)) { errno = 63; return nullptr; }
|
||||
std::memcpy(buffer, name.data(), name.size());
|
||||
buffer[name.size()] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::size_t APS5_VABI strnlen_nid_postfix(const char* text, std::size_t limit) {
|
||||
std::size_t length = 0;
|
||||
while (length < limit && text[length] != '\0') ++length;
|
||||
return length;
|
||||
}
|
||||
|
||||
char* APS5_VABI strncat_nid_postfix(char* destination, const char* source, std::size_t limit) {
|
||||
return std::strncat(destination, source, limit);
|
||||
}
|
||||
|
||||
char* APS5_VABI strpbrk_nid_postfix(const char* text, const char* accept) {
|
||||
return const_cast<char*>(std::strpbrk(text, accept));
|
||||
}
|
||||
|
||||
std::size_t APS5_VABI strcspn_nid_postfix(const char* text, const char* reject) {
|
||||
return std::strcspn(text, reject);
|
||||
}
|
||||
|
||||
std::size_t APS5_VABI strlcat_nid_postfix(char* destination, const char* source, std::size_t capacity) {
|
||||
const auto destinationLength = strnlen_nid_postfix(destination, capacity);
|
||||
const auto sourceLength = std::strlen(source);
|
||||
if (destinationLength < capacity) {
|
||||
const auto remaining = capacity - destinationLength - 1;
|
||||
const auto count = sourceLength < remaining ? sourceLength : remaining;
|
||||
std::memcpy(destination + destinationLength, source, count);
|
||||
destination[destinationLength + count] = '\0';
|
||||
}
|
||||
return destinationLength + sourceLength;
|
||||
}
|
||||
|
||||
char* APS5_VABI strtok_r_nid_postfix(char* text, const char* delimiters, char** state) {
|
||||
if (text == nullptr) text = *state;
|
||||
if (text == nullptr) return nullptr;
|
||||
text += std::strspn(text, delimiters);
|
||||
if (*text == '\0') {
|
||||
*state = text;
|
||||
return nullptr;
|
||||
}
|
||||
char* end = text + std::strcspn(text, delimiters);
|
||||
if (*end != '\0') *end++ = '\0';
|
||||
*state = end;
|
||||
return text;
|
||||
}
|
||||
|
||||
char* APS5_VABI strtok_nid_postfix(char* text, const char* delimiters) {
|
||||
thread_local char* state = nullptr;
|
||||
return strtok_r_nid_postfix(text, delimiters, &state);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -97,6 +97,13 @@ double APS5_VABI strtod_nid_postfix(const char* str, char** endptr) {
|
|||
return std::strtod(str, endptr);
|
||||
}
|
||||
|
||||
double APS5_VABI atof_nid_postfix(const char* str) { return std::atof(str); }
|
||||
float APS5_VABI strtof_nid_postfix(const char* str, char** endptr) { return std::strtof(str, endptr); }
|
||||
long double APS5_VABI strtold_nid_postfix(const char* str, char** endptr) {
|
||||
static_assert(sizeof(long double) == 16, "Guest long double requires x87 extended precision storage");
|
||||
return std::strtold(str, endptr);
|
||||
}
|
||||
|
||||
int APS5_VABI atoi_nid_postfix(const char* str) {
|
||||
return std::atoi(str);
|
||||
}
|
||||
|
|
@ -158,18 +165,6 @@ size_t APS5_VABI strspn_nid_postfix(const char* s, const char* accept) {
|
|||
return std::strspn(s, accept);
|
||||
}
|
||||
|
||||
size_t APS5_VABI strcspn_nid_postfix(const char* s, const char* reject) {
|
||||
return std::strcspn(s, reject);
|
||||
}
|
||||
|
||||
const char* APS5_VABI strpbrk_nid_postfix(const char* s, const char* accept) {
|
||||
return std::strpbrk(s, accept);
|
||||
}
|
||||
|
||||
char* APS5_VABI strncat_nid_postfix(char* dest, const char* src, size_t n) {
|
||||
return std::strncat(dest, src, n);
|
||||
}
|
||||
|
||||
int APS5_VABI strcoll_nid_postfix(const char* s1, const char* s2) {
|
||||
return std::strcmp(s1, s2);
|
||||
}
|
||||
|
|
@ -193,14 +188,6 @@ int APS5_VABI strncpy_s_nid_postfix(char* dest, size_t destsz, const char* src,
|
|||
return 0;
|
||||
}
|
||||
|
||||
float APS5_VABI strtof_nid_postfix(const char* str, char** endptr) {
|
||||
return std::strtof(str, endptr);
|
||||
}
|
||||
|
||||
double APS5_VABI atof_nid_postfix(const char* str) {
|
||||
return std::atof(str);
|
||||
}
|
||||
|
||||
unsigned long long APS5_VABI _Stoull_nid_postfix(const char* str, char** endptr, int base) {
|
||||
return std::strtoull(str, endptr, base);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,25 @@ GCC_GLOBAL_ALIAS(__cxa_end_catch_nid_no_patch_cut, __cxa_end_catch_nid_postfix);
|
|||
GCC_GLOBAL_ALIAS(__cxa_rethrow_nid_no_patch_cut, __cxa_rethrow_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__gxx_personality_v0_nid_no_patch_cut, __gxx_personality_v0_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_Unwind_Resume_nid_no_patch_cut, _Unwind_Resume_nid_postfix);
|
||||
GCC_LOCAL_ALIAS(__cxa_free_exception, __cxa_free_exception_nid_postfix);
|
||||
GCC_LOCAL_ALIAS(_ZSt9terminatev, _ZSt9terminatev_nid_postfix);
|
||||
GCC_LOCAL_ALIAS(__cxa_call_terminate, __cxa_call_terminate_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_free_exception, __cxa_free_exception_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_ZSt9terminatev, _ZSt9terminatev_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_call_terminate, __cxa_call_terminate_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_allocate_exception, __cxa_allocate_exception_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_throw, __cxa_throw_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_init_primary_exception, __cxa_init_primary_exception_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_begin_catch, __cxa_begin_catch_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_end_catch, __cxa_end_catch_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_rethrow, __cxa_rethrow_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_get_exception_ptr, __cxa_get_exception_ptr_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(__cxa_current_exception_type, __cxa_current_exception_type_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_ZSt13set_terminatePFvvE, _ZSt13set_terminatePFvvE_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_ZSt13get_terminatev, _ZSt13get_terminatev_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_ZSt18uncaught_exceptionv, _ZSt18uncaught_exceptionv_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_ZSt19uncaught_exceptionsv, _ZSt19uncaught_exceptionsv_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_Unwind_Resume, _Unwind_Resume_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_Unwind_RaiseException, _Unwind_RaiseException_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_Unwind_Resume_or_Rethrow, _Unwind_Resume_or_Rethrow_nid_postfix);
|
||||
GCC_GLOBAL_ALIAS(_Unwind_DeleteException, _Unwind_DeleteException_nid_postfix);
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C" {
|
||||
|
|
|
|||
|
|
@ -246,6 +246,18 @@ void APS5_VABI __cxa_free_exception_nid_postfix(void* object) {
|
|||
InvokeTerminate(header->terminate);
|
||||
}
|
||||
|
||||
LibcException::Header* __cxa_init_primary_exception_nid_postfix(void* object, std::type_info* type, void (*destructor)(void*)) {
|
||||
using namespace LibcException;
|
||||
auto* header = FromObject(object);
|
||||
header->type = type;
|
||||
header->destructor = destructor;
|
||||
header->terminate = terminateHandler.load(std::memory_order_acquire);
|
||||
header->adjusted = object;
|
||||
header->unwind.exception_class = PrimaryClass;
|
||||
header->unwind.exception_cleanup = Cleanup;
|
||||
return header;
|
||||
}
|
||||
|
||||
void* APS5_VABI __cxa_begin_catch_nid_postfix(void* exception) {
|
||||
using namespace LibcException;
|
||||
auto* unwind = static_cast<_Unwind_Exception*>(exception);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
#if defined(__linux__) || defined(_WIN32)
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) || defined(__linux__)
|
||||
extern "C" _Unwind_Reason_Code __gxx_personality_v0(int, _Unwind_Action, std::uint64_t, _Unwind_Exception*, _Unwind_Context*);
|
||||
#endif
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ bool OwnPersonality(Word personality) {
|
|||
}
|
||||
#endif
|
||||
if (personality == reinterpret_cast<Word>(__gxx_personality_v0_nid_postfix)) return true;
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) || defined(__linux__)
|
||||
if (personality == reinterpret_cast<Word>(__gxx_personality_v0)) return true;
|
||||
#endif
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI setenv_nid_postfix(const char* name, const char* value, int overwrite) {
|
||||
if (overwrite == 0 && std::getenv(name) != nullptr) {
|
||||
return 0;
|
||||
}
|
||||
const size_t nameLength = std::strlen(name);
|
||||
const size_t valueLength = std::strlen(value);
|
||||
char* entry = static_cast<char*>(std::malloc(nameLength + valueLength + 2));
|
||||
if (entry == nullptr) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
std::memcpy(entry, name, nameLength);
|
||||
entry[nameLength] = '=';
|
||||
std::memcpy(entry + nameLength + 1, value, valueLength);
|
||||
entry[nameLength + 1 + valueLength] = '\0';
|
||||
return ::putenv(entry);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ add_library(libkernel SHARED EXCLUDE_FROM_ALL
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/Semaphore/src/Semaphore.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DirectMemory/Export.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DirectMemory/DirectMemory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DirectMemory/PosixMemory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DirectMemory/MemoryPool.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DirectMemory/MemoryPoolExport.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/src/Mutex.cpp
|
||||
|
|
@ -23,17 +24,25 @@ add_library(libkernel SHARED EXCLUDE_FROM_ALL
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Mutex.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Rwlock.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Thread.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Once.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Tsd.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pthread/Posix/Sem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Socket/src/Socket.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Socket/src/Udp.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Socket/src/AddressConversion.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/EventFlag/src/EventFlag.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Module/src/Module.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Module/src/DynamicLoader.cpp
|
||||
${LIBS_INCLUDE_DIR}/nid/src/NidCompute.cpp
|
||||
${LIBS_INCLUDE_DIR}/nid/src/Sha1.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Aio/src/Aio.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/System/src/Coredump.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/System/src/Exception.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/System/src/Sanitizer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/System/src/Rtld.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/System/src/Process.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/System/src/SystemConfiguration.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/System/src/Signals.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Time/Time.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Time/TimedWait.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AppMetadata/src/AppMetadata.cpp
|
||||
|
|
@ -41,8 +50,11 @@ add_library(libkernel SHARED EXCLUDE_FROM_ALL
|
|||
)
|
||||
|
||||
target_include_directories(libkernel PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_include_directories(libkernel PRIVATE ${LIBS_INCLUDE_DIR}/nid/include)
|
||||
target_link_libraries(libkernel PRIVATE ${CMAKE_DL_LIBS})
|
||||
target_link_libraries(libkernel PRIVATE libc)
|
||||
if(WIN32)
|
||||
target_link_libraries(libkernel PRIVATE ws2_32)
|
||||
target_compile_definitions(libkernel PRIVATE _WIN32_WINNT=0x0602)
|
||||
endif()
|
||||
configure_windows_unwind(libkernel)
|
||||
|
|
|
|||
|
|
@ -159,9 +159,9 @@ int LinuxProtFromSce(int prot) {
|
|||
throw std::invalid_argument("Unsupported memory protection bits: " + std::to_string(prot));
|
||||
}
|
||||
int result = PROT_NONE;
|
||||
if (prot & 1) result |= PROT_READ;
|
||||
if (prot & 2) result |= PROT_WRITE;
|
||||
if (prot & 4) result |= PROT_EXEC;
|
||||
if (prot & 0x13) result |= PROT_READ;
|
||||
if (prot & 0x22) result |= PROT_READ | PROT_WRITE;
|
||||
if (prot & 4) result |= PROT_READ | PROT_EXEC;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -320,6 +320,8 @@ int DoMprotect(const void* addr, size_t len, int prot) {
|
|||
if (memory.AllocationBase != GetModuleHandleW(nullptr)) throw std::invalid_argument("Memory protection of a foreign image is not supported");
|
||||
mutation.RegisterMainImage();
|
||||
}
|
||||
#else
|
||||
mutation.RegisterMainImage();
|
||||
#endif
|
||||
mutation.Protect(pointer, bytes, (prot & 3) != 0, (prot & 2) != 0, [&] {
|
||||
if (mprotect(const_cast<void*>(pointer), bytes, nativeProtection) != 0) throw std::system_error(errno, std::generic_category(), "mprotect failed");
|
||||
|
|
|
|||
|
|
@ -315,3 +315,14 @@ int APS5_VABI sceKernelBatchMap2(KernelBatchMapEntry* entries, int num_entries,
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceKernelMlock_nid_postfix(void* address, std::uint64_t length) {
|
||||
(void)address;
|
||||
(void)length;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
#include "DirectMemory.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <limits>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" int* APS5_VABI __error_nid_postfix();
|
||||
|
||||
namespace {
|
||||
// FreeBSD/PS5 ABI values, independent of the host's errno and mmap constants.
|
||||
constexpr int GuestInvalid = 22;
|
||||
constexpr int GuestNoMemory = 12;
|
||||
constexpr int GuestNotSupported = 45;
|
||||
constexpr int GuestPrivate = 0x2;
|
||||
constexpr int GuestAnonymous = 0x1000;
|
||||
|
||||
bool RoundLength(std::size_t length, std::size_t& rounded) {
|
||||
constexpr auto mask = PS5_PAGE_SIZE - 1;
|
||||
if (length == 0 || length > std::numeric_limits<std::size_t>::max() - mask)
|
||||
return false;
|
||||
rounded = (length + mask) & ~mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetError(int error) {
|
||||
*__error_nid_postfix() = error;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void* APS5_VABI mmap_nid_postfix(void* address, std::size_t length, int protection,
|
||||
int flags, int descriptor, std::int64_t offset) noexcept {
|
||||
const auto failed = [](int error) -> void* {
|
||||
SetError(error);
|
||||
return reinterpret_cast<void*>(static_cast<std::uintptr_t>(-1));
|
||||
};
|
||||
std::size_t rounded;
|
||||
if (!RoundLength(length, rounded) || (protection & ~7) != 0)
|
||||
return failed(GuestInvalid);
|
||||
if (flags != (GuestPrivate | GuestAnonymous))
|
||||
return failed(GuestNotSupported);
|
||||
if (descriptor != -1 || offset != 0)
|
||||
return failed(GuestInvalid);
|
||||
// A non-fixed address is a hint; the kernel may choose another address.
|
||||
(void)address;
|
||||
void* mapped = nullptr;
|
||||
try {
|
||||
const auto result = DoMapAnon(&mapped, rounded, protection, 0);
|
||||
if (result != 0) return failed(GuestNoMemory);
|
||||
return mapped;
|
||||
} catch (const std::bad_alloc&) {
|
||||
return failed(GuestNoMemory);
|
||||
} catch (const std::exception&) {
|
||||
return failed(GuestNoMemory);
|
||||
}
|
||||
}
|
||||
|
||||
int APS5_VABI munmap_nid_postfix(void* address, std::size_t length) noexcept {
|
||||
const auto failed = [](int error) {
|
||||
SetError(error);
|
||||
return -1;
|
||||
};
|
||||
std::size_t rounded;
|
||||
const auto start = reinterpret_cast<std::uintptr_t>(address);
|
||||
if (!RoundLength(length, rounded) || start == 0 ||
|
||||
(start & (PS5_PAGE_SIZE - 1)) != 0 ||
|
||||
rounded > std::numeric_limits<std::uintptr_t>::max() - start)
|
||||
return failed(GuestInvalid);
|
||||
try {
|
||||
if (DoMunmap(address, rounded) != 0) return failed(GuestInvalid);
|
||||
return 0;
|
||||
} catch (const std::bad_alloc&) {
|
||||
return failed(GuestNoMemory);
|
||||
} catch (const std::exception&) {
|
||||
// The allocation tracker rejects foreign, pinned, and noncontiguous ranges.
|
||||
return failed(GuestInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
namespace File {
|
||||
|
||||
void FillFileStat(const std::filesystem::path& nativePath, FileStat* sb);
|
||||
void FillFileStat(int nativeDescriptor, FileStat* sb);
|
||||
bool FillFileStatFromDescriptor(int fd, FileStat* sb);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,26 +24,7 @@ static int DoFstat(int fd, NativeStat* st) {
|
|||
}
|
||||
#endif
|
||||
|
||||
namespace File {
|
||||
|
||||
static void FillFromNative(const NativeStat& st, FileStat* sb);
|
||||
|
||||
void FillFileStat(const std::filesystem::path& nativePath, FileStat* sb) {
|
||||
NativeStat st{};
|
||||
if (DoStat(nativePath, &st) != 0) {
|
||||
throw std::runtime_error(std::string("FillFileStat: stat failed for ") + nativePath.string());
|
||||
}
|
||||
FillFromNative(st, sb);
|
||||
}
|
||||
|
||||
bool FillFileStatFromDescriptor(int fd, FileStat* sb) {
|
||||
NativeStat st{};
|
||||
if (DoFstat(fd, &st) != 0) return false;
|
||||
FillFromNative(st, sb);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void FillFromNative(const NativeStat& st, FileStat* sb) {
|
||||
static void CopyNativeStat(const NativeStat& st, FileStat* sb) {
|
||||
*sb = FileStat{};
|
||||
sb->st_mode = static_cast<std::uint16_t>(st.st_mode);
|
||||
sb->st_size = static_cast<std::int64_t>(st.st_size);
|
||||
|
|
@ -91,4 +72,29 @@ static void FillFromNative(const NativeStat& st, FileStat* sb) {
|
|||
#endif
|
||||
}
|
||||
|
||||
namespace File {
|
||||
|
||||
void FillFileStat(const std::filesystem::path& nativePath, FileStat* sb) {
|
||||
NativeStat st{};
|
||||
if (DoStat(nativePath, &st) != 0) {
|
||||
throw std::runtime_error(std::string("FillFileStat: stat failed for ") + nativePath.string());
|
||||
}
|
||||
CopyNativeStat(st, sb);
|
||||
}
|
||||
|
||||
void FillFileStat(int nativeDescriptor, FileStat* sb) {
|
||||
NativeStat st{};
|
||||
if (DoFstat(nativeDescriptor, &st) != 0) {
|
||||
throw std::runtime_error(std::string("FillFileStat: fstat failed for fd ") + std::to_string(nativeDescriptor));
|
||||
}
|
||||
CopyNativeStat(st, sb);
|
||||
}
|
||||
|
||||
bool FillFileStatFromDescriptor(int fd, FileStat* sb) {
|
||||
NativeStat st{};
|
||||
if (DoFstat(fd, &st) != 0) return false;
|
||||
CopyNativeStat(st, sb);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,105 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/File/include/File.hpp"
|
||||
#include "prx/libkernel/File/include/FileFlags.hpp"
|
||||
#include "prx/libkernel/File/include/NativeStat.hpp"
|
||||
#include "prx/libkernel/Socket/include/SocketRuntime.hpp"
|
||||
#include <cerrno>
|
||||
#include <cstdarg>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <sys/stat.h>
|
||||
static int NativeRmdir(const std::filesystem::path& path) {
|
||||
return ::_wrmdir(path.wstring().c_str());
|
||||
}
|
||||
static int NativeMkdir(const std::filesystem::path& path, std::uint16_t mode) {
|
||||
(void)mode;
|
||||
return ::_wmkdir(path.wstring().c_str());
|
||||
}
|
||||
static int NativeChmod(const std::filesystem::path& path, int mode) {
|
||||
return ::_wchmod(path.wstring().c_str(), mode);
|
||||
}
|
||||
static int NativeFtruncate(int descriptor, std::int64_t length) {
|
||||
return static_cast<int>(::_chsize_s(descriptor, length));
|
||||
}
|
||||
static int NativeFlock(int descriptor, int operation) {
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(::_get_osfhandle(descriptor));
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
return -1;
|
||||
}
|
||||
OVERLAPPED overlapped{};
|
||||
if (operation & 8) {
|
||||
return ::UnlockFileEx(handle, 0, MAXDWORD, MAXDWORD, &overlapped) ? 0 : -1;
|
||||
}
|
||||
DWORD flags = 0;
|
||||
if (operation & 2) flags |= LOCKFILE_EXCLUSIVE_LOCK;
|
||||
if (operation & 4) flags |= LOCKFILE_FAIL_IMMEDIATELY;
|
||||
return ::LockFileEx(handle, flags, 0, MAXDWORD, MAXDWORD, &overlapped) ? 0 : -1;
|
||||
}
|
||||
static std::int64_t NativePread(int descriptor, void* buf, std::size_t nbytes, std::int64_t offset) {
|
||||
if (nbytes > static_cast<std::size_t>(std::numeric_limits<unsigned int>::max())) {
|
||||
throw std::runtime_error("NativePread: nbytes exceeds platform limit");
|
||||
}
|
||||
std::int64_t saved = ::_lseeki64(descriptor, 0, SEEK_CUR);
|
||||
if (saved < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (::_lseeki64(descriptor, offset, SEEK_SET) < 0) {
|
||||
return -1;
|
||||
}
|
||||
int n = ::_read(descriptor, buf, static_cast<unsigned int>(nbytes));
|
||||
::_lseeki64(descriptor, saved, SEEK_SET);
|
||||
return n;
|
||||
}
|
||||
static std::int64_t NativePwrite(int descriptor, const void* buf, std::size_t nbytes, std::int64_t offset) {
|
||||
if (nbytes > static_cast<std::size_t>(std::numeric_limits<unsigned int>::max())) {
|
||||
throw std::runtime_error("NativePwrite: nbytes exceeds platform limit");
|
||||
}
|
||||
std::int64_t saved = ::_lseeki64(descriptor, 0, SEEK_CUR);
|
||||
if (saved < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (::_lseeki64(descriptor, offset, SEEK_SET) < 0) {
|
||||
return -1;
|
||||
}
|
||||
int n = ::_write(descriptor, buf, static_cast<unsigned int>(nbytes));
|
||||
::_lseeki64(descriptor, saved, SEEK_SET);
|
||||
return n;
|
||||
}
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
static int NativeRmdir(const std::filesystem::path& path) {
|
||||
return ::rmdir(path.c_str());
|
||||
}
|
||||
static int NativeMkdir(const std::filesystem::path& path, std::uint16_t mode) {
|
||||
return ::mkdir(path.c_str(), static_cast<mode_t>(mode));
|
||||
}
|
||||
static int NativeChmod(const std::filesystem::path& path, int mode) {
|
||||
return ::chmod(path.c_str(), static_cast<mode_t>(mode));
|
||||
}
|
||||
static int NativeFtruncate(int descriptor, std::int64_t length) {
|
||||
return ::ftruncate(descriptor, static_cast<off_t>(length));
|
||||
}
|
||||
static int NativeFlock(int descriptor, int operation) {
|
||||
return ::flock(descriptor, operation);
|
||||
}
|
||||
static std::int64_t NativePread(int descriptor, void* buf, std::size_t nbytes, std::int64_t offset) {
|
||||
return static_cast<std::int64_t>(::pread(descriptor, buf, nbytes, static_cast<off_t>(offset)));
|
||||
}
|
||||
static std::int64_t NativePwrite(int descriptor, const void* buf, std::size_t nbytes, std::int64_t offset) {
|
||||
return static_cast<std::int64_t>(::pwrite(descriptor, buf, nbytes, static_cast<off_t>(offset)));
|
||||
}
|
||||
#endif
|
||||
|
||||
static constexpr int GUEST_ENOENT = 2;
|
||||
static constexpr int GUEST_EIO = 5;
|
||||
|
|
@ -20,101 +114,155 @@ static int SceErrorFromErrno(int error) {
|
|||
extern "C" {
|
||||
|
||||
int APS5_VABI chmod_nid_postfix(const char* path, int mode) {
|
||||
(void)path;
|
||||
(void)mode;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (path == nullptr) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
auto native = ResolvePath_nid_no_patch(path);
|
||||
if (NativeChmod(native, mode) != 0) {
|
||||
throw std::runtime_error(std::string(__func__) + ": chmod failed for " + native.string() + ", errno=" + std::to_string(errno));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI close_nid_postfix(int d) {
|
||||
(void)d;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (d >= GuestSockets::FirstDescriptor) return GuestSockets::Close(d);
|
||||
#ifdef _WIN32
|
||||
return _close(d);
|
||||
#else
|
||||
return ::close(d);
|
||||
#endif
|
||||
}
|
||||
|
||||
int APS5_VABI _close_nid_postfix(int descriptor) {
|
||||
return close_nid_postfix(descriptor);
|
||||
}
|
||||
|
||||
int APS5_VABI flock_nid_postfix(int d, int operation) {
|
||||
(void)d;
|
||||
(void)operation;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (NativeFlock(d, operation) != 0) {
|
||||
#ifdef _WIN32
|
||||
throw std::runtime_error(std::string(__func__) + ": flock failed, fd=" + std::to_string(d) + ", error=" + std::to_string(::GetLastError()));
|
||||
#else
|
||||
throw std::runtime_error(std::string(__func__) + ": flock failed, fd=" + std::to_string(d) + ", errno=" + std::to_string(errno));
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t APS5_VABI fstat_nid_disambig1_nid_postfix(int d, FileStat* sb) {
|
||||
(void)d;
|
||||
(void)sb;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (sb == nullptr) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
File::FillFileStat(d, sb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI ftruncate_nid_postfix(int d, int64_t length) {
|
||||
(void)d;
|
||||
(void)length;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (length < 0) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
int error = NativeFtruncate(d, length);
|
||||
if (error != 0) {
|
||||
throw std::runtime_error(std::string(__func__) + ": ftruncate failed, fd=" + std::to_string(d) + ", error=" + std::to_string(error));
|
||||
}
|
||||
#else
|
||||
if (NativeFtruncate(d, length) != 0) {
|
||||
throw std::runtime_error(std::string(__func__) + ": ftruncate failed, fd=" + std::to_string(d) + ", errno=" + std::to_string(errno));
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t APS5_VABI lseek_nid_postfix(int d, int64_t offset, int whence) {
|
||||
(void)d;
|
||||
(void)offset;
|
||||
(void)whence;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return static_cast<int64_t>(sceKernelLseek(d, offset, whence));
|
||||
}
|
||||
|
||||
int APS5_VABI mkdir_nid_postfix(const char* path, uint16_t mode) {
|
||||
(void)path;
|
||||
(void)mode;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (path == nullptr) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
auto native = ResolvePath_nid_no_patch(path);
|
||||
if (NativeMkdir(native, mode) != 0) {
|
||||
throw std::runtime_error(std::string(__func__) + ": mkdir failed for " + native.string() + ", errno=" + std::to_string(errno));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI open_nid_postfix(const char* path, int flags, int mode) {
|
||||
(void)path;
|
||||
(void)flags;
|
||||
(void)mode;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return sceKernelOpen(path, flags, static_cast<std::uint16_t>(mode));
|
||||
}
|
||||
|
||||
int APS5_VABI _open_nid_postfix(const char* path, int flags, ...) {
|
||||
std::uint16_t mode = 0;
|
||||
if (flags & SCE_KERNEL_O_CREAT) {
|
||||
#ifdef _WIN32
|
||||
__builtin_sysv_va_list arguments;
|
||||
__builtin_sysv_va_start(arguments, flags);
|
||||
mode = static_cast<std::uint16_t>(__builtin_va_arg(arguments, int));
|
||||
__builtin_sysv_va_end(arguments);
|
||||
#else
|
||||
std::va_list arguments;
|
||||
va_start(arguments, flags);
|
||||
mode = static_cast<std::uint16_t>(va_arg(arguments, int));
|
||||
va_end(arguments);
|
||||
#endif
|
||||
}
|
||||
return sceKernelOpen(path, flags, mode);
|
||||
}
|
||||
|
||||
int64_t APS5_VABI pread_nid_postfix(int d, void* buf, size_t nbytes, int64_t offset) {
|
||||
(void)d;
|
||||
(void)buf;
|
||||
(void)nbytes;
|
||||
(void)offset;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (buf == nullptr) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
if (offset < 0) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
auto n = NativePread(d, buf, nbytes, offset);
|
||||
if (n < 0) {
|
||||
throw std::runtime_error(std::string(__func__) + ": pread failed, fd=" + std::to_string(d) + ", errno=" + std::to_string(errno));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int64_t APS5_VABI pwrite_nid_disambig1_nid_postfix(int d, const void* buf, size_t nbytes, int64_t offset) {
|
||||
(void)d;
|
||||
(void)buf;
|
||||
(void)nbytes;
|
||||
(void)offset;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (buf == nullptr) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
if (offset < 0) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
auto n = NativePwrite(d, buf, nbytes, offset);
|
||||
if (n < 0) {
|
||||
throw std::runtime_error(std::string(__func__) + ": pwrite failed, fd=" + std::to_string(d) + ", errno=" + std::to_string(errno));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int64_t APS5_VABI read_nid_postfix(int d, void* buf, uint64_t nbytes) {
|
||||
(void)d;
|
||||
(void)buf;
|
||||
(void)nbytes;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return sceKernelRead(d, buf, static_cast<size_t>(nbytes));
|
||||
}
|
||||
|
||||
std::int64_t APS5_VABI _read_nid_postfix(int descriptor, void* buffer, std::size_t count) {
|
||||
return sceKernelRead(descriptor, buffer, count);
|
||||
}
|
||||
|
||||
int64_t APS5_VABI write_nid_postfix(int d, const char* str, int64_t size) {
|
||||
(void)d;
|
||||
(void)str;
|
||||
(void)size;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (size < 0) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
return sceKernelWrite(d, str, static_cast<size_t>(size));
|
||||
}
|
||||
|
||||
std::int64_t APS5_VABI _write_nid_postfix(int descriptor, const void* buffer, std::size_t count) {
|
||||
return sceKernelWrite(descriptor, buffer, count);
|
||||
}
|
||||
|
||||
int APS5_VABI stat_nid_postfix(const char* path, FileStat* sb) {
|
||||
(void)path;
|
||||
(void)sb;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return sceKernelStat(path, sb);
|
||||
}
|
||||
|
||||
int APS5_VABI unlink_nid_postfix(const char* path) {
|
||||
return sceKernelUnlink(path);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelCheckReachability(const char* path) {
|
||||
|
|
@ -201,4 +349,33 @@ int APS5_VABI sceKernelRmdir(const char* path) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI rmdir_nid_postfix(const char* path) {
|
||||
return sceKernelRmdir(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceKernelChmod_nid_postfix(const char* path, std::uint16_t mode) {
|
||||
(void)path;
|
||||
(void)mode;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelTruncate_nid_postfix(const char* path, std::int64_t length) {
|
||||
(void)path;
|
||||
(void)length;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelUtimes_nid_postfix(const char* path, const KernelTimeval* times) {
|
||||
(void)path;
|
||||
(void)times;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
#include "prx/libc/include/General.hpp"
|
||||
#include <nid/NidCompute.hpp>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
thread_local std::array<char, 512> loaderError{};
|
||||
thread_local bool pendingError = false;
|
||||
void Error(const char* message) {
|
||||
std::snprintf(loaderError.data(), loaderError.size(), "%s", message);
|
||||
pendingError = true;
|
||||
}
|
||||
struct Module {
|
||||
void* native = nullptr;
|
||||
bool owned = true;
|
||||
bool global = false;
|
||||
~Module() {
|
||||
if (owned && native) {
|
||||
#ifdef _WIN32
|
||||
FreeLibrary(static_cast<HMODULE>(native));
|
||||
#else
|
||||
::dlclose(native);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
};
|
||||
std::mutex modulesMutex;
|
||||
std::map<std::uintptr_t, std::shared_ptr<Module>> modules;
|
||||
std::uintptr_t nextHandle = 0x20000000;
|
||||
void* Symbol(Module& module, const char* name) {
|
||||
#ifdef _WIN32
|
||||
return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(module.native), name));
|
||||
#else
|
||||
return ::dlsym(module.native, name);
|
||||
#endif
|
||||
}
|
||||
void* FindSymbol(Module& module, const char* name) {
|
||||
if (auto* symbol = Symbol(module, name)) return symbol;
|
||||
const auto nid = Nid::ComputeNid(name, "");
|
||||
return Symbol(module, nid.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
char* APS5_VABI dlerror_nid_postfix() {
|
||||
if (!pendingError) return nullptr;
|
||||
pendingError = false;
|
||||
return loaderError.data();
|
||||
}
|
||||
void* APS5_VABI dlopen_nid_postfix(const char* path, int flags) {
|
||||
if ((flags & ~0x103) || (flags & 3) == 0 || (flags & 3) == 3) {
|
||||
Error("dlopen: unsupported flags"); return nullptr;
|
||||
}
|
||||
try {
|
||||
auto module = std::make_shared<Module>();
|
||||
module->global = (flags & 0x100) != 0 || !path;
|
||||
#ifdef _WIN32
|
||||
if (!path) {
|
||||
module->native = GetModuleHandleW(nullptr);
|
||||
module->owned = false;
|
||||
} else {
|
||||
if (!*path) { Error("dlopen: empty module path"); return nullptr; }
|
||||
const auto resolved = ResolvePath_nid_no_patch(path);
|
||||
module->native = LoadLibraryExW(resolved.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
}
|
||||
if (!module->native) {
|
||||
char message[128];
|
||||
std::snprintf(message, sizeof(message), "dlopen: Windows loader error %lu (module must be host-compatible)", GetLastError());
|
||||
Error(message); return nullptr;
|
||||
}
|
||||
#else
|
||||
const auto resolved = path ? ResolvePath_nid_no_patch(path).string() : std::string{};
|
||||
const int nativeFlags = ((flags & 3) == 1 ? RTLD_LAZY : RTLD_NOW) |
|
||||
((flags & 0x100) ? RTLD_GLOBAL : RTLD_LOCAL);
|
||||
module->native = ::dlopen(path ? resolved.c_str() : nullptr, nativeFlags);
|
||||
if (!module->native) { Error(::dlerror()); return nullptr; }
|
||||
#endif
|
||||
std::lock_guard lock(modulesMutex);
|
||||
const auto handle = nextHandle++;
|
||||
modules.emplace(handle, std::move(module));
|
||||
return reinterpret_cast<void*>(handle);
|
||||
} catch (const std::exception& error) { Error(error.what()); return nullptr; }
|
||||
}
|
||||
void* APS5_VABI dlsym_nid_postfix(void* handle, const char* name) {
|
||||
if (!name || !*name) { Error("dlsym: empty symbol name"); return nullptr; }
|
||||
try {
|
||||
std::vector<std::shared_ptr<Module>> search;
|
||||
{
|
||||
std::lock_guard lock(modulesMutex);
|
||||
if (handle == reinterpret_cast<void*>(static_cast<std::intptr_t>(-2))) {
|
||||
for (const auto& [key, module] : modules) if (module->global) search.push_back(module);
|
||||
} else {
|
||||
auto found = modules.find(reinterpret_cast<std::uintptr_t>(handle));
|
||||
if (found == modules.end()) { Error("dlsym: invalid or unsupported module handle"); return nullptr; }
|
||||
search.push_back(found->second);
|
||||
}
|
||||
}
|
||||
for (const auto& module : search) if (auto* result = FindSymbol(*module, name)) return result;
|
||||
Error("dlsym: symbol not found in supported module scope");
|
||||
return nullptr;
|
||||
} catch (const std::exception& error) { Error(error.what()); return nullptr; }
|
||||
}
|
||||
int APS5_VABI dlclose_nid_postfix(void* handle) {
|
||||
std::shared_ptr<Module> module;
|
||||
{
|
||||
std::lock_guard lock(modulesMutex);
|
||||
auto found = modules.find(reinterpret_cast<std::uintptr_t>(handle));
|
||||
if (found == modules.end()) { Error("dlclose: invalid module handle"); return -1; }
|
||||
module = std::move(found->second);
|
||||
modules.erase(found);
|
||||
}
|
||||
// Unload outside the registry lock: module destructors may call loader APIs.
|
||||
module.reset();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -52,3 +52,21 @@ int APS5_VABI sceKernelStopUnloadModule(KernelModule handle, size_t args, const
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI __elf_phdr_match_addr_nid_postfix(ModuleInfo* module, std::uint64_t address) {
|
||||
(void)module;
|
||||
(void)address;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// unknown signature
|
||||
std::int32_t APS5_VABI sceKernelInternalMemoryGetModuleSegmentInfo_nid_postfix(void* result) {
|
||||
(void)result;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,58 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "../include/Pthread.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Pthread.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Cond.hpp"
|
||||
#include "Common.hpp"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI scePthreadCondattrInit(PthreadCondattr* attr);
|
||||
int APS5_VABI scePthreadCondattrDestroy(PthreadCondattr* attr);
|
||||
int APS5_VABI scePthreadCondInit(PthreadCond* cond, const PthreadCondattr* attr, const char* name);
|
||||
int APS5_VABI scePthreadCondSignal(PthreadCond* cond);
|
||||
int APS5_VABI scePthreadCondBroadcast(PthreadCond* cond);
|
||||
int APS5_VABI scePthreadCondWait(PthreadCond* cond, PthreadMutex* mutex);
|
||||
int APS5_VABI scePthreadCondTimedwait(PthreadCond* cond, PthreadMutex* mutex, unsigned int usec);
|
||||
namespace {
|
||||
|
||||
int toPosix(int result) {
|
||||
if (result == 0)
|
||||
return 0;
|
||||
const auto error = static_cast<std::uint32_t>(result);
|
||||
if ((error & 0xffff0000u) != 0x80020000u)
|
||||
throw std::runtime_error("Unexpected SCE condition variable error");
|
||||
return static_cast<int>(error & 0xffffu);
|
||||
}
|
||||
|
||||
static void InitializeStatic(PthreadCond* cond, const char* funcName) {
|
||||
if (!cond) throw std::runtime_error(std::string(funcName) + ": null cond");
|
||||
std::atomic_ref<PthreadCond> slot(*cond);
|
||||
PthreadCond current = slot.load(std::memory_order_acquire);
|
||||
if (current != nullptr) return;
|
||||
auto* created = new PthreadCondPrivate();
|
||||
if (!slot.compare_exchange_strong(current, created, std::memory_order_acq_rel, std::memory_order_acquire))
|
||||
delete created;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI pthread_cond_broadcast_nid_postfix(PthreadCond* cond) {
|
||||
InitializeStatic(cond, __func__);
|
||||
return PosixThread::ToErrno(scePthreadCondBroadcast(cond));
|
||||
return toPosix(scePthreadCondBroadcast(cond));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_cond_init_nid_postfix(PthreadCond* cond, const PthreadCondattr* attr) {
|
||||
return PosixThread::ToErrno(scePthreadCondInit(cond, attr, nullptr));
|
||||
return toPosix(scePthreadCondInit(cond, attr, nullptr));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_cond_signal_nid_postfix(PthreadCond* cond) {
|
||||
InitializeStatic(cond, __func__);
|
||||
return PosixThread::ToErrno(scePthreadCondSignal(cond));
|
||||
return toPosix(scePthreadCondSignal(cond));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_cond_timedwait_nid_postfix(PthreadCond* cond, PthreadMutex* mutex, const KernelTimespec* abstime) {
|
||||
InitializeStatic(cond, __func__);
|
||||
KernelUseconds usec = 0;
|
||||
if (!PosixThread::RelativeMicroseconds((*cond)->_clockid, abstime, &usec)) return PosixThread::GUEST_EINVAL;
|
||||
return PosixThread::ToErrno(scePthreadCondTimedwait(cond, mutex, usec));
|
||||
if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) return PosixThread::GUEST_EINVAL;
|
||||
return toPosix(CondOperations::AbsoluteTimedwait(cond, mutex, abstime));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_cond_wait_nid_postfix(PthreadCond* cond, PthreadMutex* mutex) {
|
||||
InitializeStatic(cond, __func__);
|
||||
return PosixThread::ToErrno(scePthreadCondWait(cond, mutex));
|
||||
return toPosix(scePthreadCondWait(cond, mutex));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_condattr_destroy_nid_postfix(PthreadCondattr* attr) {
|
||||
if (!attr || !*attr) return PosixThread::GUEST_EINVAL;
|
||||
return PosixThread::ToErrno(scePthreadCondattrDestroy(attr));
|
||||
return toPosix(scePthreadCondattrDestroy(attr));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_condattr_init_nid_postfix(PthreadCondattr* attr) {
|
||||
if (!attr) return PosixThread::GUEST_EINVAL;
|
||||
return PosixThread::ToErrno(scePthreadCondattrInit(attr));
|
||||
return toPosix(scePthreadCondattrInit(attr));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_condattr_setclock_nid_postfix(PthreadCondattr* attr, KernelClockid clock_id) {
|
||||
if (!attr || !*attr) return PosixThread::GUEST_EINVAL;
|
||||
(*attr)->_clockid = clock_id;
|
||||
return 0;
|
||||
return toPosix(scePthreadCondattrSetclock(attr, clock_id));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,22 +3,13 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "../include/Pthread.hpp"
|
||||
#include "../include/Mutex.hpp"
|
||||
#include "Common.hpp"
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI scePthreadMutexattrInit(PthreadMutexattr* attr);
|
||||
int APS5_VABI scePthreadMutexattrDestroy(PthreadMutexattr* attr);
|
||||
int APS5_VABI scePthreadMutexattrSettype(PthreadMutexattr* attr, int type);
|
||||
int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char* name);
|
||||
int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex);
|
||||
int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex);
|
||||
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex);
|
||||
int APS5_VABI scePthreadMutexTimedlock(PthreadMutex* mutex, KernelUseconds usec);
|
||||
int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex);
|
||||
}
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
|
|
@ -27,10 +18,6 @@ constexpr int POSIX_MUTEX_ADAPTIVE_NP = 4;
|
|||
constexpr int POSIX_PRIO_PROTECT = 2;
|
||||
constexpr std::uintptr_t POSIX_ADAPTIVE_MUTEX_INITIALIZER = 1;
|
||||
|
||||
int _toErrno(int sceResult) {
|
||||
return sceResult == 0 ? 0 : static_cast<int>(static_cast<std::uint32_t>(sceResult) & 0xFFFFu);
|
||||
}
|
||||
|
||||
bool _isStaticInitializer(PthreadMutex mutex) {
|
||||
const auto value = reinterpret_cast<std::uintptr_t>(mutex);
|
||||
return value == 0 || value == POSIX_ADAPTIVE_MUTEX_INITIALIZER;
|
||||
|
|
@ -57,18 +44,18 @@ int APS5_VABI pthread_mutex_destroy_nid_postfix(PthreadMutex* mutex) {
|
|||
*mutex = nullptr;
|
||||
return 0;
|
||||
}
|
||||
return _toErrno(scePthreadMutexDestroy(mutex));
|
||||
return PosixThread::ToErrno(scePthreadMutexDestroy(mutex));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutex_init_nid_postfix(PthreadMutex* mutex, const PthreadMutexattr* attr) {
|
||||
const int result = scePthreadMutexInit(mutex, attr, nullptr);
|
||||
if (result == 0 && (!attr || !*attr)) (*mutex)->_type = MutexType::ErrorCheck;
|
||||
return _toErrno(result);
|
||||
return PosixThread::ToErrno(result);
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutex_lock_nid_postfix(PthreadMutex* mutex) {
|
||||
_initializeStatic(mutex, __func__);
|
||||
return _toErrno(scePthreadMutexLock(mutex));
|
||||
return PosixThread::ToErrno(scePthreadMutexLock(mutex));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutex_timedlock_nid_postfix(PthreadMutex* mutex, const KernelTimespec* abstime) {
|
||||
|
|
@ -79,27 +66,27 @@ int APS5_VABI pthread_mutex_timedlock_nid_postfix(PthreadMutex* mutex, const Ker
|
|||
const auto now = std::chrono::system_clock::now().time_since_epoch();
|
||||
const auto remaining = std::chrono::duration_cast<std::chrono::microseconds>(deadline - now).count();
|
||||
const auto usec = remaining <= 0 ? 0 : remaining >= std::numeric_limits<KernelUseconds>::max() ? std::numeric_limits<KernelUseconds>::max() : static_cast<KernelUseconds>(remaining);
|
||||
return _toErrno(scePthreadMutexTimedlock(mutex, usec));
|
||||
return PosixThread::ToErrno(scePthreadMutexTimedlock(mutex, usec));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutex_trylock_nid_postfix(PthreadMutex* mutex) {
|
||||
_initializeStatic(mutex, __func__);
|
||||
return _toErrno(scePthreadMutexTrylock(mutex));
|
||||
return PosixThread::ToErrno(scePthreadMutexTrylock(mutex));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutex_unlock_nid_postfix(PthreadMutex* mutex) {
|
||||
_initializeStatic(mutex, __func__);
|
||||
return _toErrno(scePthreadMutexUnlock(mutex));
|
||||
return PosixThread::ToErrno(scePthreadMutexUnlock(mutex));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutexattr_destroy_nid_postfix(PthreadMutexattr* attr) {
|
||||
return _toErrno(scePthreadMutexattrDestroy(attr));
|
||||
return PosixThread::ToErrno(scePthreadMutexattrDestroy(attr));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutexattr_init_nid_postfix(PthreadMutexattr* attr) {
|
||||
const int result = scePthreadMutexattrInit(attr);
|
||||
if (result == 0) (*attr)->type = MutexType::ErrorCheck;
|
||||
return _toErrno(result);
|
||||
return PosixThread::ToErrno(result);
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutexattr_setprotocol_nid_postfix(PthreadMutexattr* attr, int protocol) {
|
||||
|
|
@ -114,7 +101,7 @@ int APS5_VABI pthread_mutexattr_settype_nid_postfix(PthreadMutexattr* attr, int
|
|||
(*attr)->type = MutexType::Normal;
|
||||
return 0;
|
||||
}
|
||||
return _toErrno(scePthreadMutexattrSettype(attr, type));
|
||||
return PosixThread::ToErrno(scePthreadMutexattrSettype(attr, type));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
namespace {
|
||||
// FreeBSD guest ABI: PTHREAD_ONCE_INIT is {0, nullptr}; completed state is 1.
|
||||
struct GuestOnce {
|
||||
std::int32_t state;
|
||||
void* mutex;
|
||||
};
|
||||
static_assert(offsetof(GuestOnce, mutex) == 8 && sizeof(GuestOnce) == 16);
|
||||
std::mutex onceMutex;
|
||||
std::condition_variable onceChanged;
|
||||
}
|
||||
|
||||
extern "C" int APS5_VABI pthread_once_nid_postfix(
|
||||
GuestOnce* control, void (APS5_VABI *initialize)()) {
|
||||
if (!control || !initialize) return 22;
|
||||
std::unique_lock lock(onceMutex);
|
||||
onceChanged.wait(lock, [&] { return control->state != 2; });
|
||||
if (control->state == 1) return 0;
|
||||
if (control->state != 0) return 22;
|
||||
control->state = 2;
|
||||
lock.unlock();
|
||||
// Initializers may initialize other once controls. Never run guest code
|
||||
// under the shared lock. The lock publishes all initializer writes.
|
||||
try {
|
||||
initialize();
|
||||
} catch (...) {
|
||||
lock.lock();
|
||||
control->state = 0;
|
||||
lock.unlock();
|
||||
onceChanged.notify_all();
|
||||
throw;
|
||||
}
|
||||
lock.lock();
|
||||
control->state = 1;
|
||||
lock.unlock();
|
||||
onceChanged.notify_all();
|
||||
return 0;
|
||||
}
|
||||
|
||||
using OnceRoutine = void (APS5_VABI *)();
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI scePthreadOnce_nid_postfix(void* onceControl, OnceRoutine initRoutine) {
|
||||
(void)onceControl;
|
||||
(void)initRoutine;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,22 +3,23 @@
|
|||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "../include/Pthread.hpp"
|
||||
#include "../include/Rwlock.hpp"
|
||||
#include <atomic>
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI scePthreadRwlockInit(PthreadRwlock* rwlock, const PthreadRwlockattr* attr, const char* name);
|
||||
int APS5_VABI scePthreadRwlockDestroy(PthreadRwlock* rwlock);
|
||||
int APS5_VABI scePthreadRwlockWrlock(PthreadRwlock* rwlock);
|
||||
}
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
int _toErrno(int sceResult) {
|
||||
return sceResult == 0 ? 0 : static_cast<int>(static_cast<std::uint32_t>(sceResult) & 0xFFFFu);
|
||||
int toPosix(int result) {
|
||||
if (result == 0)
|
||||
return 0;
|
||||
const auto error = static_cast<std::uint32_t>(result);
|
||||
if ((error & 0xffff0000u) != 0x80020000u)
|
||||
throw std::runtime_error("Unexpected SCE rwlock error");
|
||||
return static_cast<int>(error & 0xffffu);
|
||||
}
|
||||
|
||||
void _initializeStatic(PthreadRwlock* rwlock, const char* funcName) {
|
||||
void initializeStatic(PthreadRwlock* rwlock, const char* funcName) {
|
||||
if (!rwlock) throw std::runtime_error(std::string(funcName) + ": null rwlock");
|
||||
std::atomic_ref<PthreadRwlock> slot(*rwlock);
|
||||
PthreadRwlock current = slot.load(std::memory_order_acquire);
|
||||
|
|
@ -35,16 +36,26 @@ extern "C" {
|
|||
int APS5_VABI pthread_rwlock_destroy_nid_postfix(PthreadRwlock* rwlock) {
|
||||
if (!rwlock) throw std::runtime_error("pthread_rwlock_destroy: null rwlock");
|
||||
if (*rwlock == nullptr) return 0;
|
||||
return _toErrno(scePthreadRwlockDestroy(rwlock));
|
||||
return toPosix(scePthreadRwlockDestroy(rwlock));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_rwlock_init_nid_postfix(PthreadRwlock* rwlock, const PthreadRwlockattr* attr) {
|
||||
return _toErrno(scePthreadRwlockInit(rwlock, attr, nullptr));
|
||||
return toPosix(scePthreadRwlockInit(rwlock, attr, nullptr));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_rwlock_rdlock_nid_postfix(PthreadRwlock* rwlock) {
|
||||
initializeStatic(rwlock, __func__);
|
||||
return toPosix(scePthreadRwlockRdlock(rwlock));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_rwlock_unlock_nid_postfix(PthreadRwlock* rwlock) {
|
||||
initializeStatic(rwlock, __func__);
|
||||
return toPosix(scePthreadRwlockUnlock(rwlock));
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_rwlock_wrlock_nid_postfix(PthreadRwlock* rwlock) {
|
||||
_initializeStatic(rwlock, __func__);
|
||||
return _toErrno(scePthreadRwlockWrlock(rwlock));
|
||||
initializeStatic(rwlock, __func__);
|
||||
return toPosix(scePthreadRwlockWrlock(rwlock));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "../include/ThreadLifecycle.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "../include/Pthread.hpp"
|
||||
#include "Common.hpp"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBKERNEL_PTHREAD_COND_HPP
|
||||
#define CORE_LIBS_PRX_LIBKERNEL_PTHREAD_COND_HPP
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
class CondOperations {
|
||||
public:
|
||||
static int AbsoluteTimedwait(PthreadCond* cond, PthreadMutex* mutex, const KernelTimespec* abstime);
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI scePthreadCondattrInit(PthreadCondattr* attr);
|
||||
int APS5_VABI scePthreadCondattrDestroy(PthreadCondattr* attr);
|
||||
int APS5_VABI scePthreadCondattrSetclock(PthreadCondattr* attr, KernelClockid clockId);
|
||||
int APS5_VABI scePthreadCondInit(PthreadCond* cond, const PthreadCondattr* attr, const char* name);
|
||||
int APS5_VABI scePthreadCondDestroy(PthreadCond* cond);
|
||||
int APS5_VABI scePthreadCondSignal(PthreadCond* cond);
|
||||
int APS5_VABI scePthreadCondBroadcast(PthreadCond* cond);
|
||||
int APS5_VABI scePthreadCondSignalto(PthreadCond* cond, Pthread thread);
|
||||
int APS5_VABI scePthreadCondWait(PthreadCond* cond, PthreadMutex* mutex);
|
||||
int APS5_VABI scePthreadCondTimedwait(PthreadCond* cond, PthreadMutex* mutex, KernelUseconds usec);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBKERNEL_PTHREAD_MUTEX_HPP
|
||||
#define CORE_LIBS_PRX_LIBKERNEL_PTHREAD_MUTEX_HPP
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
class MutexOperations {
|
||||
public:
|
||||
static int Timedlock(PthreadMutex* mutex, const KernelTimespec* abstime);
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI scePthreadMutexattrInit(PthreadMutexattr* attr);
|
||||
int APS5_VABI scePthreadMutexattrDestroy(PthreadMutexattr* attr);
|
||||
int APS5_VABI scePthreadMutexattrSettype(PthreadMutexattr* attr, int type);
|
||||
int APS5_VABI scePthreadMutexattrSetprotocol(PthreadMutexattr* attr, int protocol);
|
||||
int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char* name);
|
||||
int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex);
|
||||
int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex);
|
||||
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex);
|
||||
int APS5_VABI scePthreadMutexTimedlock(PthreadMutex* mutex, KernelUseconds usec);
|
||||
int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -30,7 +30,7 @@ struct PthreadMutexPrivate {
|
|||
std::atomic<std::thread::id> _owner;
|
||||
int _count;
|
||||
|
||||
PthreadMutexPrivate() : _type(MutexType::Normal), _count(0) {}
|
||||
PthreadMutexPrivate() : _type(MutexType::Normal), _owner(std::thread::id{}), _count(0) {}
|
||||
};
|
||||
|
||||
struct PthreadRwlockattrPrivate {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBKERNEL_PTHREAD_RWLOCK_HPP
|
||||
#define CORE_LIBS_PRX_LIBKERNEL_PTHREAD_RWLOCK_HPP
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI scePthreadRwlockattrInit(PthreadRwlockattr* attr);
|
||||
int APS5_VABI scePthreadRwlockattrDestroy(PthreadRwlockattr* attr);
|
||||
int APS5_VABI scePthreadRwlockattrSettype(PthreadRwlockattr* attr, int type);
|
||||
int APS5_VABI scePthreadRwlockInit(PthreadRwlock* rwlock, const PthreadRwlockattr* attr, const char* name);
|
||||
int APS5_VABI scePthreadRwlockDestroy(PthreadRwlock* rwlock);
|
||||
int APS5_VABI scePthreadRwlockRdlock(PthreadRwlock* rwlock);
|
||||
int APS5_VABI scePthreadRwlockTryrdlock(PthreadRwlock* rwlock);
|
||||
int APS5_VABI scePthreadRwlockWrlock(PthreadRwlock* rwlock);
|
||||
int APS5_VABI scePthreadRwlockTrywrlock(PthreadRwlock* rwlock);
|
||||
int APS5_VABI scePthreadRwlockUnlock(PthreadRwlock* rwlock);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef CORE_LIBS_PRX_LIBKERNEL_PTHREAD_THREADLIFECYCLE_HPP
|
||||
#define CORE_LIBS_PRX_LIBKERNEL_PTHREAD_THREADLIFECYCLE_HPP
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
|
||||
class ThreadLifecycle {
|
||||
public:
|
||||
static void SetThreadDtors(thread_dtors_func_t callback);
|
||||
static void SetThreadAtexitCount(get_thread_atexit_count_func_t callback);
|
||||
static void SetThreadAtexitReport(thread_atexit_report_func_t callback);
|
||||
};
|
||||
|
||||
extern "C" void APS5_VABI scePthreadExit(void* retval);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
#include "../include/Pthread.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Pthread.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
static constexpr int SCE_OK = 0;
|
||||
static constexpr int SCE_KERNEL_ERROR_EINVAL = 0x80020016;
|
||||
static constexpr int SCE_KERNEL_ERROR_ENOMEM = 0x8002000C;
|
||||
|
||||
static constexpr std::size_t DEFAULT_STACK_SIZE = 1u << 20;
|
||||
|
|
@ -19,7 +20,7 @@ static constexpr int SCHED_FIFO_PS5 = 1;
|
|||
extern "C" {
|
||||
|
||||
int APS5_VABI scePthreadAttrInit(PthreadAttr* attr) {
|
||||
if (!attr) throw std::runtime_error("scePthreadAttrInit: null attr");
|
||||
if (!attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
auto* p = new (std::nothrow) PthreadAttrPrivate{};
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
p->_stacksize = DEFAULT_STACK_SIZE;
|
||||
|
|
@ -32,35 +33,35 @@ int APS5_VABI scePthreadAttrInit(PthreadAttr* attr) {
|
|||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrDestroy(PthreadAttr* attr) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadAttrDestroy: null attr");
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetdetachstate(PthreadAttr* attr, int detachstate) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadAttrSetdetachstate: null attr");
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (detachstate != DETACH_JOINABLE && detachstate != DETACH_DETACHED)
|
||||
throw std::runtime_error("scePthreadAttrSetdetachstate: invalid state");
|
||||
throw std::runtime_error(std::string(__func__) + ": invalid state");
|
||||
(*attr)->_detachstate = detachstate;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedParam* param) {
|
||||
if (!attr || !*attr || !param)
|
||||
throw std::runtime_error("scePthreadAttrSetschedparam: null arg");
|
||||
throw std::runtime_error(std::string(__func__) + ": null arg");
|
||||
(*attr)->_schedpriority = param->sched_priority;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetstacksize(PthreadAttr* attr, std::size_t stacksize) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadAttrSetstacksize: null attr");
|
||||
if (stacksize < 16384) throw std::runtime_error("scePthreadAttrSetstacksize: too small");
|
||||
if (!attr || !*attr) throw std::runtime_error(std::string(__func__) + ": null attr");
|
||||
if (stacksize < 16384) throw std::runtime_error(std::string(__func__) + ": too small");
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO system{};
|
||||
GetSystemInfo(&system);
|
||||
if (stacksize % system.dwPageSize != 0 || stacksize > std::numeric_limits<unsigned>::max())
|
||||
throw std::runtime_error("scePthreadAttrSetstacksize: invalid Windows stack size");
|
||||
throw std::runtime_error(std::string(__func__) + ": invalid Windows stack size");
|
||||
#endif
|
||||
(*attr)->_stacksize = stacksize;
|
||||
return SCE_OK;
|
||||
|
|
@ -68,7 +69,7 @@ int APS5_VABI scePthreadAttrSetstacksize(PthreadAttr* attr, std::size_t stacksiz
|
|||
|
||||
int APS5_VABI scePthreadAttrGetstack(const PthreadAttr* attr, void** stackaddr, std::size_t* stacksize) {
|
||||
if (!attr || !*attr || !stackaddr || !stacksize)
|
||||
throw std::runtime_error("scePthreadAttrGetstack: null arg");
|
||||
throw std::runtime_error(std::string(__func__) + ": null arg");
|
||||
*stackaddr = (*attr)->stackAddress;
|
||||
*stacksize = (*attr)->_stacksize;
|
||||
return SCE_OK;
|
||||
|
|
@ -76,7 +77,7 @@ int APS5_VABI scePthreadAttrGetstack(const PthreadAttr* attr, void** stackaddr,
|
|||
|
||||
int APS5_VABI scePthreadAttrGet(Pthread thread, PthreadAttr* attr) {
|
||||
if (!thread || !attr || !*attr)
|
||||
throw std::runtime_error("scePthreadAttrGet: null arg");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
(*attr)->_stacksize = thread->stackSize;
|
||||
(*attr)->stackAddress = thread->stackAddress;
|
||||
(*attr)->_detachstate = thread->_detached ? DETACH_DETACHED : DETACH_JOINABLE;
|
||||
|
|
@ -165,18 +166,17 @@ int APS5_VABI scePthreadAttrSetsolosched(PthreadAttr* attr, int solosched) {
|
|||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetstack(PthreadAttr* attr, void* addr, size_t size) {
|
||||
(void)attr;
|
||||
(void)addr;
|
||||
(void)size;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (!attr || !*attr || !addr) throw std::runtime_error(std::string(__func__) + ": null arg");
|
||||
if (size < 16384) throw std::runtime_error(std::string(__func__) + ": too small");
|
||||
(*attr)->stackAddress = addr;
|
||||
(*attr)->_stacksize = size;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadAttrSetstackaddr(PthreadAttr* attr, void* addr) {
|
||||
(void)attr;
|
||||
(void)addr;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (!attr || !*attr || !addr) throw std::runtime_error(std::string(__func__) + ": null arg");
|
||||
(*attr)->stackAddress = addr;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,103 +1,154 @@
|
|||
#include "../include/Pthread.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Pthread.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Mutex.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Cond.hpp"
|
||||
#include "prx/libkernel/Pthread/Posix/Common.hpp"
|
||||
#include "prx/libkernel/Time/include/Time.hpp"
|
||||
#include "prx/libkernel/Time/include/TimedWait.hpp"
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
static constexpr int SCE_OK = 0;
|
||||
static constexpr int SCE_KERNEL_ERROR_ENOMEM = 0x8002000C;
|
||||
static constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = 0x8002003C;
|
||||
namespace {
|
||||
|
||||
static int WaitOn(PthreadCondPrivate* cond, PthreadMutexPrivate* mutex, std::optional<std::uint64_t> deadlineNanos, const void* caller) {
|
||||
const auto self = std::this_thread::get_id();
|
||||
constexpr int sceTimedOut = static_cast<int>(0x8002003cu);
|
||||
std::mutex condInitializationMutex;
|
||||
|
||||
PthreadCond destroyedCond() {
|
||||
return reinterpret_cast<PthreadCond>(std::uintptr_t{2});
|
||||
}
|
||||
|
||||
PthreadCond resolveCond(PthreadCond* cond) {
|
||||
if (!cond)
|
||||
throw std::invalid_argument("Condition variable pointer is null");
|
||||
std::lock_guard lock(condInitializationMutex);
|
||||
if (*cond == destroyedCond())
|
||||
throw std::runtime_error("Condition variable has been destroyed");
|
||||
if (!*cond)
|
||||
*cond = new PthreadCondPrivate();
|
||||
return *cond;
|
||||
}
|
||||
|
||||
PthreadMutex lockedMutex(PthreadMutex* mutex) {
|
||||
if (!mutex || !*mutex)
|
||||
throw std::invalid_argument("Mutex pointer is null");
|
||||
auto* current = *mutex;
|
||||
if (current->_owner.load(std::memory_order_acquire) != std::this_thread::get_id())
|
||||
throw std::runtime_error("Condition wait mutex is not owned by the current thread");
|
||||
return current;
|
||||
}
|
||||
|
||||
int waitUntil(PthreadCond* cond, PthreadMutex* mutex, std::optional<std::uint64_t> deadlineNanos, const void* caller) {
|
||||
auto* c = resolveCond(cond);
|
||||
auto* m = lockedMutex(mutex);
|
||||
bool timedOut = false;
|
||||
const auto waitStart = std::chrono::steady_clock::now();
|
||||
struct Trace {
|
||||
const void* caller; const bool& timedOut; std::chrono::steady_clock::time_point start;
|
||||
~Trace() { KernelTraceWait_nid_postfix("cond", caller, static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - start).count()), timedOut); }
|
||||
} trace{caller, timedOut, waitStart};
|
||||
mutex->_owner.store(std::thread::id{}, std::memory_order_relaxed);
|
||||
if (mutex->_type == MutexType::Recursive) {
|
||||
std::unique_lock<std::recursive_timed_mutex> lock(mutex->_rmtx, std::adopt_lock);
|
||||
if (deadlineNanos) timedOut = !cond->_cv.WaitUntil(lock, *deadlineNanos);
|
||||
else cond->_cv.Wait(lock);
|
||||
lock.release();
|
||||
} else {
|
||||
std::unique_lock<std::timed_mutex> lock(mutex->_mtx, std::adopt_lock);
|
||||
if (deadlineNanos) timedOut = !cond->_cv.WaitUntil(lock, *deadlineNanos);
|
||||
else cond->_cv.Wait(lock);
|
||||
if (m->_type == MutexType::Recursive) {
|
||||
std::unique_lock<std::recursive_timed_mutex> lock(m->_rmtx, std::adopt_lock);
|
||||
const auto previousCount = m->_count;
|
||||
m->_count = 0;
|
||||
m->_owner.store(std::thread::id{}, std::memory_order_release);
|
||||
if (deadlineNanos) timedOut = !c->_cv.WaitUntil(lock, *deadlineNanos);
|
||||
else c->_cv.Wait(lock);
|
||||
m->_owner.store(std::this_thread::get_id(), std::memory_order_release);
|
||||
m->_count = previousCount;
|
||||
lock.release();
|
||||
return timedOut ? sceTimedOut : 0;
|
||||
}
|
||||
mutex->_owner.store(self, std::memory_order_relaxed);
|
||||
return timedOut ? SCE_KERNEL_ERROR_ETIMEDOUT : SCE_OK;
|
||||
std::unique_lock<std::timed_mutex> lock(m->_mtx, std::adopt_lock);
|
||||
m->_owner.store(std::thread::id{}, std::memory_order_release);
|
||||
if (deadlineNanos) timedOut = !c->_cv.WaitUntil(lock, *deadlineNanos);
|
||||
else c->_cv.Wait(lock);
|
||||
m->_owner.store(std::this_thread::get_id(), std::memory_order_release);
|
||||
lock.release();
|
||||
return timedOut ? sceTimedOut : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int CondOperations::AbsoluteTimedwait(PthreadCond* cond, PthreadMutex* mutex, const KernelTimespec* abstime) {
|
||||
KernelUseconds usec = 0;
|
||||
if (!PosixThread::RelativeMicroseconds(resolveCond(cond)->_clockid, abstime, &usec))
|
||||
throw std::invalid_argument("Invalid absolute condition variable timeout");
|
||||
return waitUntil(cond, mutex, TimedWait::DeadlineNanos(usec), __builtin_return_address(0));
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI scePthreadCondattrInit(PthreadCondattr* attr) {
|
||||
if (!attr) throw std::runtime_error("scePthreadCondattrInit: null attr");
|
||||
auto* p = new (std::nothrow) PthreadCondattrPrivate{0};
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
*attr = p;
|
||||
return SCE_OK;
|
||||
if (!attr)
|
||||
throw std::invalid_argument("Condition attribute pointer is null");
|
||||
*attr = new PthreadCondattrPrivate{0};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondattrDestroy(PthreadCondattr* attr) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadCondattrDestroy: null attr");
|
||||
if (!attr || !*attr)
|
||||
throw std::invalid_argument("Condition attributes are not initialized");
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return SCE_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondattrSetclock(PthreadCondattr* attr, KernelClockid clockId) {
|
||||
if (!attr || !*attr)
|
||||
throw std::invalid_argument("Condition attributes are not initialized");
|
||||
(*attr)->_clockid = static_cast<int>(clockId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondInit(PthreadCond* cond, const PthreadCondattr* attr, const char*) {
|
||||
if (!cond) throw std::runtime_error("scePthreadCondInit: null cond");
|
||||
auto* p = new (std::nothrow) PthreadCondPrivate{};
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
if (attr && *attr) p->_clockid = (*attr)->_clockid;
|
||||
*cond = p;
|
||||
return SCE_OK;
|
||||
if (!cond)
|
||||
throw std::invalid_argument("Condition variable pointer is null");
|
||||
if (attr && !*attr)
|
||||
throw std::invalid_argument("Condition attributes are not initialized");
|
||||
auto replacement = std::make_unique<PthreadCondPrivate>();
|
||||
if (attr)
|
||||
replacement->_clockid = (*attr)->_clockid;
|
||||
std::lock_guard lock(condInitializationMutex);
|
||||
*cond = replacement.release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondDestroy(PthreadCond* cond) {
|
||||
if (!cond || !*cond) throw std::runtime_error("scePthreadCondDestroy: null cond");
|
||||
if (!cond)
|
||||
throw std::invalid_argument("Condition variable pointer is null");
|
||||
std::lock_guard lock(condInitializationMutex);
|
||||
if (*cond == destroyedCond())
|
||||
throw std::runtime_error("Condition variable has already been destroyed");
|
||||
delete *cond;
|
||||
*cond = nullptr;
|
||||
return SCE_OK;
|
||||
*cond = destroyedCond();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondSignal(PthreadCond* cond) {
|
||||
if (!cond || !*cond) throw std::runtime_error("scePthreadCondSignal: null cond");
|
||||
(*cond)->_cv.NotifyOne();
|
||||
return SCE_OK;
|
||||
resolveCond(cond)->_cv.NotifyOne();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondBroadcast(PthreadCond* cond) {
|
||||
if (!cond || !*cond) throw std::runtime_error("scePthreadCondBroadcast: null cond");
|
||||
(*cond)->_cv.NotifyAll();
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondTimedwait(PthreadCond* cond, PthreadMutex* mutex, unsigned int usec) {
|
||||
if (!cond || !*cond || !mutex || !*mutex)
|
||||
throw std::runtime_error("scePthreadCondTimedwait: null arg");
|
||||
return WaitOn(*cond, *mutex, TimedWait::DeadlineNanos(usec), __builtin_return_address(0));
|
||||
resolveCond(cond)->_cv.NotifyAll();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondSignalto(PthreadCond* cond, Pthread thread) {
|
||||
(void)thread;
|
||||
if (!cond || !*cond) throw std::runtime_error("scePthreadCondSignalto: null cond");
|
||||
(*cond)->_cv.NotifyAll();
|
||||
return SCE_OK;
|
||||
resolveCond(cond)->_cv.NotifyAll();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondWait(PthreadCond* cond, PthreadMutex* mutex) {
|
||||
if (!cond || !*cond || !mutex || !*mutex)
|
||||
throw std::runtime_error("scePthreadCondWait: null arg");
|
||||
return WaitOn(*cond, *mutex, std::nullopt, __builtin_return_address(0));
|
||||
return waitUntil(cond, mutex, std::nullopt, __builtin_return_address(0));
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadCondTimedwait(PthreadCond* cond, PthreadMutex* mutex, KernelUseconds usec) {
|
||||
return waitUntil(cond, mutex, TimedWait::DeadlineNanos(usec), __builtin_return_address(0));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,142 +1,174 @@
|
|||
#include "../include/Pthread.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "../include/Mutex.hpp"
|
||||
#include "prx/libkernel/Time/include/TimedWait.hpp"
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
static constexpr int SCE_OK = 0;
|
||||
static constexpr int SCE_KERNEL_ERROR_ENOMEM = 0x8002000C;
|
||||
static constexpr int SCE_KERNEL_ERROR_EDEADLK = 0x8002000B;
|
||||
static constexpr int SCE_KERNEL_ERROR_EPERM = 0x80020001;
|
||||
static constexpr int SCE_KERNEL_ERROR_EBUSY = 0x80020010;
|
||||
static constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = 0x8002003C;
|
||||
namespace {
|
||||
|
||||
constexpr int sceNotPermitted = static_cast<int>(0x80020001u);
|
||||
constexpr int sceDeadlock = static_cast<int>(0x8002000bu);
|
||||
constexpr int sceBusy = static_cast<int>(0x80020010u);
|
||||
constexpr int sceTimedOut = static_cast<int>(0x8002003cu);
|
||||
std::mutex initializationMutex;
|
||||
|
||||
PthreadMutex destroyedMutex() {
|
||||
return reinterpret_cast<PthreadMutex>(std::uintptr_t{2});
|
||||
}
|
||||
|
||||
PthreadMutex resolveMutex(PthreadMutex* mutex, bool initialize) {
|
||||
if (!mutex)
|
||||
throw std::invalid_argument("Mutex pointer is null");
|
||||
std::lock_guard lock(initializationMutex);
|
||||
if (*mutex == destroyedMutex())
|
||||
throw std::runtime_error("Mutex has been destroyed");
|
||||
if (reinterpret_cast<std::uintptr_t>(*mutex) == 1)
|
||||
throw std::runtime_error("Adaptive mutex initializer is unsupported");
|
||||
if (!*mutex) {
|
||||
if (!initialize)
|
||||
throw std::runtime_error("Mutex is not initialized");
|
||||
*mutex = new PthreadMutexPrivate();
|
||||
}
|
||||
return *mutex;
|
||||
}
|
||||
|
||||
template<typename TAcquire>
|
||||
int acquireMutex(PthreadMutex mutex, TAcquire acquire, int unavailable, bool tryOnly) {
|
||||
const auto thread = std::this_thread::get_id();
|
||||
const bool owned = mutex->_owner.load(std::memory_order_acquire) == thread;
|
||||
if (owned && mutex->_type != MutexType::Recursive) {
|
||||
if (tryOnly)
|
||||
return sceBusy;
|
||||
if (mutex->_type == MutexType::ErrorCheck)
|
||||
return sceDeadlock;
|
||||
throw std::runtime_error("Mutex is already owned by the current thread");
|
||||
}
|
||||
if (owned && mutex->_count == std::numeric_limits<int>::max())
|
||||
throw std::overflow_error("Recursive mutex lock count overflow");
|
||||
if (mutex->_type == MutexType::Recursive) {
|
||||
if (!acquire(mutex->_rmtx))
|
||||
return unavailable;
|
||||
++mutex->_count;
|
||||
} else {
|
||||
if (!acquire(mutex->_mtx))
|
||||
return unavailable;
|
||||
}
|
||||
mutex->_owner.store(thread, std::memory_order_release);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int MutexOperations::Timedlock(PthreadMutex* mutex, const KernelTimespec* abstime) {
|
||||
if (!abstime || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
|
||||
throw std::invalid_argument("Invalid absolute mutex timeout");
|
||||
const auto maximum = std::chrono::nanoseconds::max().count();
|
||||
if (abstime->tv_sec > (maximum - abstime->tv_nsec) / 1000000000)
|
||||
throw std::overflow_error("Absolute mutex timeout exceeds the host clock range");
|
||||
const auto duration = std::chrono::nanoseconds(abstime->tv_sec * 1000000000 + abstime->tv_nsec);
|
||||
if (std::chrono::duration<long double>(duration) >= std::chrono::duration<long double>(std::chrono::system_clock::duration::max()))
|
||||
throw std::overflow_error("Absolute mutex timeout exceeds the host clock range");
|
||||
const auto deadline = std::chrono::system_clock::time_point(std::chrono::duration_cast<std::chrono::system_clock::duration>(duration));
|
||||
return acquireMutex(resolveMutex(mutex, true), [&](auto& native) { return native.try_lock_until(deadline); }, sceTimedOut, false);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI scePthreadMutexattrInit(PthreadMutexattr* attr) {
|
||||
if (!attr) throw std::runtime_error("scePthreadMutexattrInit: null attr");
|
||||
auto* p = new (std::nothrow) PthreadMutexattrPrivate{MutexType::Normal};
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
*attr = p;
|
||||
return SCE_OK;
|
||||
if (!attr)
|
||||
throw std::invalid_argument("Mutex attribute pointer is null");
|
||||
*attr = new PthreadMutexattrPrivate{MutexType::Normal};
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexattrDestroy(PthreadMutexattr* attr) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadMutexattrDestroy: null attr");
|
||||
if (!attr || !*attr)
|
||||
throw std::invalid_argument("Mutex attributes are not initialized");
|
||||
delete *attr;
|
||||
*attr = nullptr;
|
||||
return SCE_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexattrSettype(PthreadMutexattr* attr, int type) {
|
||||
if (!attr || !*attr) throw std::runtime_error("scePthreadMutexattrSettype: null attr");
|
||||
if (!attr || !*attr)
|
||||
throw std::invalid_argument("Mutex attributes are not initialized");
|
||||
switch (type) {
|
||||
case 1: (*attr)->type = MutexType::ErrorCheck; break;
|
||||
case 2: (*attr)->type = MutexType::Recursive; break;
|
||||
case 3: (*attr)->type = MutexType::Normal; break;
|
||||
case 4: (*attr)->type = MutexType::Normal; break;
|
||||
default: throw std::runtime_error("scePthreadMutexattrSettype: invalid type " + std::to_string(type));
|
||||
default: throw std::invalid_argument("Invalid mutex type");
|
||||
}
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char*) {
|
||||
if (!mutex) throw std::runtime_error("scePthreadMutexInit: null mutex");
|
||||
MutexType t = MutexType::Normal;
|
||||
if (attr && *attr) t = (*attr)->type;
|
||||
auto* p = new (std::nothrow) PthreadMutexPrivate();
|
||||
if (!p) return SCE_KERNEL_ERROR_ENOMEM;
|
||||
p->_type = t;
|
||||
*mutex = p;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex) {
|
||||
if (!mutex || !*mutex) throw std::runtime_error("scePthreadMutexDestroy: null mutex");
|
||||
delete *mutex;
|
||||
*mutex = nullptr;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex) {
|
||||
if (!mutex || !*mutex) throw std::runtime_error("scePthreadMutexLock: null mutex");
|
||||
auto* m = *mutex;
|
||||
const auto tid = std::this_thread::get_id();
|
||||
if (m->_type == MutexType::Recursive) {
|
||||
m->_rmtx.lock();
|
||||
m->_owner.store(tid, std::memory_order_relaxed);
|
||||
++m->_count;
|
||||
return SCE_OK;
|
||||
}
|
||||
if (m->_type == MutexType::ErrorCheck) {
|
||||
if (m->_owner.load(std::memory_order_acquire) == tid) return SCE_KERNEL_ERROR_EDEADLK;
|
||||
}
|
||||
m->_mtx.lock();
|
||||
m->_owner.store(tid, std::memory_order_relaxed);
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex) {
|
||||
if (!mutex || !*mutex) throw std::runtime_error("scePthreadMutexUnlock: null mutex");
|
||||
auto* m = *mutex;
|
||||
if (m->_type == MutexType::ErrorCheck || m->_type == MutexType::Normal) {
|
||||
if (m->_owner.load(std::memory_order_acquire) != std::this_thread::get_id())
|
||||
return SCE_KERNEL_ERROR_EPERM;
|
||||
}
|
||||
if (m->_type == MutexType::Recursive) {
|
||||
if (--m->_count == 0) m->_owner.store(std::thread::id{}, std::memory_order_relaxed);
|
||||
m->_rmtx.unlock();
|
||||
return SCE_OK;
|
||||
}
|
||||
m->_owner.store(std::thread::id{}, std::memory_order_relaxed);
|
||||
m->_mtx.unlock();
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexTimedlock(PthreadMutex* mutex, KernelUseconds usec) {
|
||||
if (!mutex || !*mutex) throw std::runtime_error("scePthreadMutexTimedlock: null mutex");
|
||||
auto* m = *mutex;
|
||||
const auto tid = std::this_thread::get_id();
|
||||
const std::uint64_t deadline = TimedWait::DeadlineNanos(usec);
|
||||
if (m->_type == MutexType::Recursive) {
|
||||
const bool locked = TimedWait::AcquireUntil(deadline, [&] { return m->_rmtx.try_lock(); }, [&](std::uint64_t micros) { return m->_rmtx.try_lock_for(std::chrono::microseconds(micros)); });
|
||||
if (!locked) return SCE_KERNEL_ERROR_ETIMEDOUT;
|
||||
m->_owner.store(tid, std::memory_order_relaxed);
|
||||
++m->_count;
|
||||
return SCE_OK;
|
||||
}
|
||||
if (m->_type == MutexType::ErrorCheck) {
|
||||
if (m->_owner.load(std::memory_order_acquire) == tid) return SCE_KERNEL_ERROR_EDEADLK;
|
||||
}
|
||||
const bool locked = TimedWait::AcquireUntil(deadline, [&] { return m->_mtx.try_lock(); }, [&](std::uint64_t micros) { return m->_mtx.try_lock_for(std::chrono::microseconds(micros)); });
|
||||
if (!locked) return SCE_KERNEL_ERROR_ETIMEDOUT;
|
||||
m->_owner.store(tid, std::memory_order_relaxed);
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex) {
|
||||
if (!mutex || !*mutex) throw std::runtime_error("scePthreadMutexTrylock: null mutex");
|
||||
auto* m = *mutex;
|
||||
const auto tid = std::this_thread::get_id();
|
||||
if (m->_type == MutexType::Recursive) {
|
||||
if (!m->_rmtx.try_lock()) return SCE_KERNEL_ERROR_EBUSY;
|
||||
m->_owner.store(tid, std::memory_order_relaxed);
|
||||
++m->_count;
|
||||
return SCE_OK;
|
||||
}
|
||||
if (!m->_mtx.try_lock()) return SCE_KERNEL_ERROR_EBUSY;
|
||||
m->_owner.store(tid, std::memory_order_relaxed);
|
||||
return SCE_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexattrSetprotocol(PthreadMutexattr* attr, int protocol) {
|
||||
(void)attr;
|
||||
(void)protocol;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
if (!attr || !*attr)
|
||||
throw std::invalid_argument("Mutex attributes are not initialized");
|
||||
if (protocol != 0)
|
||||
throw std::invalid_argument("Mutex priority inheritance and protection are unsupported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char*) {
|
||||
if (!mutex)
|
||||
throw std::invalid_argument("Mutex pointer is null");
|
||||
if (attr && !*attr)
|
||||
throw std::invalid_argument("Mutex attributes are not initialized");
|
||||
auto replacement = std::make_unique<PthreadMutexPrivate>();
|
||||
if (attr)
|
||||
replacement->_type = (*attr)->type;
|
||||
std::lock_guard lock(initializationMutex);
|
||||
*mutex = replacement.release();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex) {
|
||||
if (!mutex)
|
||||
throw std::invalid_argument("Mutex pointer is null");
|
||||
std::lock_guard lock(initializationMutex);
|
||||
if (*mutex == destroyedMutex())
|
||||
throw std::runtime_error("Mutex has already been destroyed");
|
||||
if (reinterpret_cast<std::uintptr_t>(*mutex) == 1)
|
||||
throw std::runtime_error("Adaptive mutex initializer is unsupported");
|
||||
if (*mutex && (*mutex)->_owner.load(std::memory_order_acquire) != std::thread::id{})
|
||||
throw std::runtime_error("Cannot destroy a locked mutex");
|
||||
delete *mutex;
|
||||
*mutex = destroyedMutex();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex) {
|
||||
return acquireMutex(resolveMutex(mutex, true), [](auto& native) { native.lock(); return true; }, 0, false);
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex) {
|
||||
auto* current = resolveMutex(mutex, false);
|
||||
if (current->_owner.load(std::memory_order_acquire) != std::this_thread::get_id())
|
||||
return sceNotPermitted;
|
||||
if (current->_type == MutexType::Recursive) {
|
||||
if (--current->_count == 0)
|
||||
current->_owner.store(std::thread::id{}, std::memory_order_release);
|
||||
current->_rmtx.unlock();
|
||||
} else {
|
||||
current->_owner.store(std::thread::id{}, std::memory_order_release);
|
||||
current->_mtx.unlock();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexTimedlock(PthreadMutex* mutex, KernelUseconds usec) {
|
||||
const auto deadline = TimedWait::DeadlineNanos(usec);
|
||||
return acquireMutex(resolveMutex(mutex, true), [=](auto& native) {
|
||||
return TimedWait::AcquireUntil(deadline, [&] { return native.try_lock(); }, [&](std::uint64_t micros) { return native.try_lock_for(std::chrono::microseconds(micros)); });
|
||||
}, sceTimedOut, false);
|
||||
}
|
||||
|
||||
int APS5_VABI scePthreadMutexTrylock(PthreadMutex* mutex) {
|
||||
return acquireMutex(resolveMutex(mutex, true), [](auto& native) { return native.try_lock(); }, sceBusy, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "../include/Pthread.hpp"
|
||||
#include "../include/Rwlock.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libkernel/Time/include/TimedWait.hpp"
|
||||
#include <chrono>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "../include/Pthread.hpp"
|
||||
#include "../include/ThreadLifecycle.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/CpuTopology.hpp"
|
||||
#include <algorithm>
|
||||
|
|
@ -35,7 +36,44 @@ struct ThreadArgs {
|
|||
PthreadPrivate* self;
|
||||
};
|
||||
|
||||
static void FinishThread(PthreadPrivate* self, void* retval) {
|
||||
static std::atomic<thread_dtors_func_t> threadDtors{nullptr};
|
||||
static std::atomic<get_thread_atexit_count_func_t> threadAtexitCount{nullptr};
|
||||
static std::atomic<thread_atexit_report_func_t> threadAtexitReport{nullptr};
|
||||
static thread_local PthreadPrivate* currentThread = nullptr;
|
||||
static thread_local bool threadFinishing = false;
|
||||
|
||||
void ThreadLifecycle::SetThreadDtors(thread_dtors_func_t callback) {
|
||||
if (!callback)
|
||||
throw std::runtime_error("Thread destructor callback is null");
|
||||
thread_dtors_func_t expected = nullptr;
|
||||
if (!threadDtors.compare_exchange_strong(expected, callback))
|
||||
throw std::runtime_error("Thread destructor callback is already registered");
|
||||
}
|
||||
|
||||
void ThreadLifecycle::SetThreadAtexitCount(get_thread_atexit_count_func_t callback) {
|
||||
if (!callback)
|
||||
throw std::runtime_error("Thread atexit count callback is null");
|
||||
get_thread_atexit_count_func_t expected = nullptr;
|
||||
if (!threadAtexitCount.compare_exchange_strong(expected, callback))
|
||||
throw std::runtime_error("Thread atexit count callback is already registered");
|
||||
}
|
||||
|
||||
void ThreadLifecycle::SetThreadAtexitReport(thread_atexit_report_func_t callback) {
|
||||
if (!callback)
|
||||
throw std::runtime_error("Thread atexit report callback is null");
|
||||
thread_atexit_report_func_t expected = nullptr;
|
||||
if (!threadAtexitReport.compare_exchange_strong(expected, callback))
|
||||
throw std::runtime_error("Thread atexit report callback is already registered");
|
||||
}
|
||||
|
||||
static void finishThread(PthreadPrivate* self, void* retval) {
|
||||
if (!self || self != currentThread)
|
||||
throw std::runtime_error("Finishing an unregistered guest thread");
|
||||
if (threadFinishing)
|
||||
throw std::runtime_error("Guest thread is already finishing");
|
||||
threadFinishing = true;
|
||||
if (const auto callback = threadDtors.load())
|
||||
callback();
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(self->_join_mtx);
|
||||
self->_retval = retval;
|
||||
|
|
@ -49,13 +87,13 @@ static void RunThread(std::unique_ptr<ThreadArgs> args) {
|
|||
const auto entry = args->entry;
|
||||
void* arg = args->arg;
|
||||
PthreadPrivate* self = args->self;
|
||||
currentThread = self;
|
||||
args.reset();
|
||||
FinishThread(self, entry(arg));
|
||||
finishThread(self, entry(arg));
|
||||
currentThread = nullptr;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static thread_local PthreadPrivate* currentThread = nullptr;
|
||||
|
||||
static void ReleaseThread(PthreadPrivate* thread) {
|
||||
if (thread->references.fetch_sub(1, std::memory_order_acq_rel) != 1)
|
||||
return;
|
||||
|
|
@ -229,18 +267,18 @@ int APS5_VABI scePthreadDetach(Pthread thread) {
|
|||
}
|
||||
|
||||
void APS5_VABI scePthreadExit(void* retval) {
|
||||
#ifdef _WIN32
|
||||
if (!currentThread)
|
||||
throw std::runtime_error("scePthreadExit: current thread is not registered");
|
||||
auto* self = currentThread;
|
||||
FinishThread(self, retval);
|
||||
finishThread(self, retval);
|
||||
currentThread = nullptr;
|
||||
#ifdef _WIN32
|
||||
ReleaseThread(self);
|
||||
_endthreadex(0);
|
||||
#else
|
||||
pthread_exit(retval);
|
||||
#endif
|
||||
__builtin_unreachable();
|
||||
throw std::runtime_error("Native thread exit returned");
|
||||
}
|
||||
|
||||
Pthread APS5_VABI scePthreadSelf() {
|
||||
|
|
@ -256,10 +294,8 @@ Pthread APS5_VABI scePthreadSelf() {
|
|||
adopted->references.store(1, std::memory_order_relaxed);
|
||||
currentThread = adopted.release();
|
||||
}
|
||||
return currentThread;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
return currentThread;
|
||||
}
|
||||
|
||||
void APS5_VABI scePthreadYield() {
|
||||
|
|
@ -361,3 +397,12 @@ int APS5_VABI scePthreadOnce(int32_t* once, void (APS5_VABI* init)(void)) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void APS5_VABI __pthread_cxa_finalize_nid_postfix(void* argument) {
|
||||
(void)argument;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
namespace GuestSockets {
|
||||
constexpr int FirstDescriptor = 0x10000000;
|
||||
int Close(int descriptor);
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" int* APS5_VABI __error_nid_postfix();
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI __inet_pton_nid_postfix(int family, const char* text, void* destination) {
|
||||
if (family != 2 && family != 28) { *__error_nid_postfix() = 47; return -1; }
|
||||
if (!text || !destination) { *__error_nid_postfix() = 14; return -1; }
|
||||
unsigned char address[16]{};
|
||||
const int nativeFamily = family == 2 ? AF_INET : AF_INET6;
|
||||
#ifdef _WIN32
|
||||
const int result = InetPtonA(nativeFamily, text, address);
|
||||
#else
|
||||
const int result = ::inet_pton(nativeFamily, text, address);
|
||||
#endif
|
||||
if (result == 1) std::memcpy(destination, address, family == 2 ? 4 : 16);
|
||||
else if (result == -1) *__error_nid_postfix() = 22;
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* APS5_VABI __inet_ntop_nid_postfix(int family, const void* source,
|
||||
char* destination, std::uint32_t capacity) {
|
||||
if (family != 2 && family != 28) { *__error_nid_postfix() = 47; return nullptr; }
|
||||
if (!source || !destination) { *__error_nid_postfix() = 14; return nullptr; }
|
||||
char text[INET6_ADDRSTRLEN]{};
|
||||
const int nativeFamily = family == 2 ? AF_INET : AF_INET6;
|
||||
#ifdef _WIN32
|
||||
const auto* result = InetNtopA(nativeFamily, const_cast<void*>(source), text, sizeof(text));
|
||||
#else
|
||||
const auto* result = ::inet_ntop(nativeFamily, source, text, sizeof(text));
|
||||
#endif
|
||||
if (!result) { *__error_nid_postfix() = 22; return nullptr; }
|
||||
const auto size = std::strlen(text) + 1;
|
||||
if (size > capacity) { *__error_nid_postfix() = 28; return nullptr; }
|
||||
std::memcpy(destination, text, size);
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,13 +13,6 @@ int APS5_VABI accept_nid_postfix(int s, void* addr, uint32_t* addrlen) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI bind_nid_postfix(int s, const void* addr, uint32_t addrlen) {
|
||||
(void)s;
|
||||
(void)addr;
|
||||
(void)addrlen;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI connect_nid_postfix(int s, const void* addr, uint32_t addrlen) {
|
||||
(void)s;
|
||||
|
|
@ -36,41 +29,9 @@ int APS5_VABI listen_nid_postfix(int s, int backlog) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI socket_nid_postfix(int family, int type, int protocol) {
|
||||
(void)family;
|
||||
(void)type;
|
||||
(void)protocol;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI getsockname_nid_postfix(int s, void* addr, uint32_t* addrlen) {
|
||||
(void)s;
|
||||
(void)addr;
|
||||
(void)addrlen;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI getsockopt_nid_postfix(int s, int level, int optname, void* optval, uint32_t* optlen) {
|
||||
(void)s;
|
||||
(void)level;
|
||||
(void)optname;
|
||||
(void)optval;
|
||||
(void)optlen;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI setsockopt_nid_postfix(int s, int level, int optname, const void* optval, uint32_t optlen) {
|
||||
(void)s;
|
||||
(void)level;
|
||||
(void)optname;
|
||||
(void)optval;
|
||||
(void)optlen;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t APS5_VABI send_nid_postfix(int s, const void* buf, uint64_t len, int flags) {
|
||||
(void)s;
|
||||
|
|
@ -81,16 +42,6 @@ int64_t APS5_VABI send_nid_postfix(int s, const void* buf, uint64_t len, int fla
|
|||
return 0;
|
||||
}
|
||||
|
||||
int64_t APS5_VABI sendto_nid_postfix(int s, const void* buf, uint64_t len, int flags, const void* addr, uint32_t addrlen) {
|
||||
(void)s;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
(void)flags;
|
||||
(void)addr;
|
||||
(void)addrlen;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t APS5_VABI recv_nid_postfix(int s, void* buf, uint64_t len, int flags) {
|
||||
(void)s;
|
||||
|
|
@ -101,16 +52,6 @@ int64_t APS5_VABI recv_nid_postfix(int s, void* buf, uint64_t len, int flags) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int64_t APS5_VABI recvfrom_nid_postfix(int s, void* buf, uint64_t len, int flags, void* addr, uint32_t* addrlen) {
|
||||
(void)s;
|
||||
(void)buf;
|
||||
(void)len;
|
||||
(void)flags;
|
||||
(void)addr;
|
||||
(void)addrlen;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char* APS5_VABI inet_ntop_nid_postfix(int af, const void* src, char* dst, uint32_t size) {
|
||||
(void)af;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,287 @@
|
|||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <cerrno>
|
||||
#endif
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "prx/libkernel/Socket/include/SocketRuntime.hpp"
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <cstdarg>
|
||||
|
||||
extern "C" int* APS5_VABI __error_nid_postfix();
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
using NativeSocket = SOCKET;
|
||||
constexpr auto Invalid = INVALID_SOCKET;
|
||||
#else
|
||||
using NativeSocket = int;
|
||||
constexpr auto Invalid = -1;
|
||||
#endif
|
||||
int Fail(int error) { *__error_nid_postfix() = error; return -1; }
|
||||
int NativeError() {
|
||||
#ifdef _WIN32
|
||||
switch (WSAGetLastError()) {
|
||||
case WSAEWOULDBLOCK: return 35;
|
||||
case WSAEADDRINUSE: return 48;
|
||||
case WSAEADDRNOTAVAIL: return 49;
|
||||
case WSAEACCES: return 13;
|
||||
case WSAEMSGSIZE: return 40;
|
||||
case WSAENETUNREACH: return 51;
|
||||
case WSAECONNRESET: return 54;
|
||||
case WSAENOBUFS: return 55;
|
||||
case WSAETIMEDOUT: return 60;
|
||||
case WSAECONNREFUSED: return 61;
|
||||
case WSAEINTR: return 4;
|
||||
case WSAEINVAL: return 22;
|
||||
default: return 5;
|
||||
}
|
||||
#else
|
||||
switch (errno) {
|
||||
case EAGAIN: return 35;
|
||||
case EADDRINUSE: return 48;
|
||||
case EADDRNOTAVAIL: return 49;
|
||||
case EMSGSIZE: return 40;
|
||||
case ENETUNREACH: return 51;
|
||||
case ECONNREFUSED: return 61;
|
||||
default: return 5;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
struct Socket {
|
||||
NativeSocket value;
|
||||
std::mutex modeMutex;
|
||||
bool nonblocking = false;
|
||||
explicit Socket(NativeSocket value) : value(value) {}
|
||||
~Socket() {
|
||||
if (value == Invalid) return;
|
||||
#ifdef _WIN32
|
||||
closesocket(value);
|
||||
#else
|
||||
::close(value);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
std::mutex socketsMutex;
|
||||
std::map<int, std::shared_ptr<Socket>> sockets;
|
||||
int nextDescriptor = GuestSockets::FirstDescriptor;
|
||||
std::shared_ptr<Socket> Lookup(int descriptor) {
|
||||
std::lock_guard lock(socketsMutex);
|
||||
const auto found = sockets.find(descriptor);
|
||||
if (found != sockets.end()) return found->second;
|
||||
Fail(9);
|
||||
return {};
|
||||
}
|
||||
int Option(int guest) {
|
||||
switch (guest) {
|
||||
case 0x4: return SO_REUSEADDR;
|
||||
case 0x20: return SO_BROADCAST;
|
||||
case 0x1001: return SO_SNDBUF;
|
||||
case 0x1002: return SO_RCVBUF;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
bool Address(const void* input, std::uint32_t length, sockaddr_storage& native, socklen_t& size) {
|
||||
if (!input || length < 2) { Fail(14); return false; }
|
||||
const auto* bytes = static_cast<const unsigned char*>(input);
|
||||
if (bytes[1] == 2 && length >= 16 && bytes[0] == 16) {
|
||||
auto& v4 = reinterpret_cast<sockaddr_in&>(native);
|
||||
v4.sin_family = AF_INET;
|
||||
std::memcpy(&v4.sin_port, bytes + 2, 2);
|
||||
std::memcpy(&v4.sin_addr, bytes + 4, 4);
|
||||
size = sizeof(v4);
|
||||
return true;
|
||||
}
|
||||
if (bytes[1] == 28 && length >= 28 && bytes[0] == 28) {
|
||||
auto& v6 = reinterpret_cast<sockaddr_in6&>(native);
|
||||
v6.sin6_family = AF_INET6;
|
||||
std::memcpy(&v6.sin6_port, bytes + 2, 2);
|
||||
std::memcpy(&v6.sin6_flowinfo, bytes + 4, 4);
|
||||
std::memcpy(&v6.sin6_addr, bytes + 8, 16);
|
||||
std::memcpy(&v6.sin6_scope_id, bytes + 24, 4);
|
||||
size = sizeof(v6);
|
||||
return true;
|
||||
}
|
||||
Fail(47);
|
||||
return false;
|
||||
}
|
||||
void GuestAddress(const sockaddr_storage& native, void* output, std::uint32_t* length) {
|
||||
unsigned char bytes[28]{};
|
||||
bytes[0] = native.ss_family == AF_INET ? 16 : 28;
|
||||
bytes[1] = native.ss_family == AF_INET ? 2 : 28;
|
||||
if (native.ss_family == AF_INET) {
|
||||
const auto& v4 = reinterpret_cast<const sockaddr_in&>(native);
|
||||
std::memcpy(bytes + 2, &v4.sin_port, 2);
|
||||
std::memcpy(bytes + 4, &v4.sin_addr, 4);
|
||||
} else {
|
||||
const auto& v6 = reinterpret_cast<const sockaddr_in6&>(native);
|
||||
std::memcpy(bytes + 2, &v6.sin6_port, 2);
|
||||
std::memcpy(bytes + 4, &v6.sin6_flowinfo, 4);
|
||||
std::memcpy(bytes + 8, &v6.sin6_addr, 16);
|
||||
std::memcpy(bytes + 24, &v6.sin6_scope_id, 4);
|
||||
}
|
||||
std::memcpy(output, bytes, std::min<std::uint32_t>(*length, bytes[0]));
|
||||
*length = bytes[0];
|
||||
}
|
||||
}
|
||||
|
||||
int GuestSockets::Close(int descriptor) {
|
||||
std::lock_guard lock(socketsMutex);
|
||||
return sockets.erase(descriptor) ? 0 : Fail(9);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI fcntl_nid_postfix(int descriptor, int command, ...) {
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
std::lock_guard lock(socket->modeMutex);
|
||||
if (command == 3) return 2 | (socket->nonblocking ? 4 : 0); // F_GETFL, O_RDWR
|
||||
if (command != 4) return Fail(22);
|
||||
#ifdef _WIN32
|
||||
__builtin_sysv_va_list arguments;
|
||||
__builtin_sysv_va_start(arguments, command);
|
||||
const int flags = __builtin_va_arg(arguments, int);
|
||||
__builtin_sysv_va_end(arguments);
|
||||
#else
|
||||
std::va_list arguments;
|
||||
va_start(arguments, command);
|
||||
const int flags = va_arg(arguments, int);
|
||||
va_end(arguments);
|
||||
#endif
|
||||
if ((flags & ~7) != 0) return Fail(45);
|
||||
#ifdef _WIN32
|
||||
unsigned long enabled = (flags & 4) != 0;
|
||||
if (ioctlsocket(socket->value, FIONBIO, &enabled)) return Fail(NativeError());
|
||||
#else
|
||||
int enabled = (flags & 4) != 0;
|
||||
if (::ioctl(socket->value, FIONBIO, &enabled)) return Fail(NativeError());
|
||||
#endif
|
||||
socket->nonblocking = enabled != 0;
|
||||
return 0;
|
||||
}
|
||||
int APS5_VABI setsockopt_nid_postfix(int descriptor, int level, int option,
|
||||
const void* value, std::uint32_t length) {
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
const int nativeOption = Option(option);
|
||||
if (level != 0xffff || nativeOption == -1) return Fail(42);
|
||||
if (!value || length != sizeof(int)) return Fail(22);
|
||||
return ::setsockopt(socket->value, SOL_SOCKET, nativeOption,
|
||||
static_cast<const char*>(value), sizeof(int)) ? Fail(NativeError()) : 0;
|
||||
}
|
||||
int APS5_VABI getsockopt_nid_postfix(int descriptor, int level, int option,
|
||||
void* value, std::uint32_t* length) {
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
const int nativeOption = Option(option);
|
||||
if (level != 0xffff || nativeOption == -1) return Fail(42);
|
||||
if (!value || !length || *length < sizeof(int)) return Fail(22);
|
||||
int result = 0;
|
||||
socklen_t size = sizeof(result);
|
||||
if (::getsockopt(socket->value, SOL_SOCKET, nativeOption, reinterpret_cast<char*>(&result), &size))
|
||||
return Fail(NativeError());
|
||||
std::memcpy(value, &result, sizeof(result));
|
||||
*length = sizeof(result);
|
||||
return 0;
|
||||
}
|
||||
int APS5_VABI socket_nid_postfix(int family, int type, int protocol) {
|
||||
if (family != 2 && family != 28) return Fail(47);
|
||||
if (type != 2 || (protocol != 0 && protocol != 17)) return Fail(43);
|
||||
#ifdef _WIN32
|
||||
static const int startup = [] { WSADATA data{}; return WSAStartup(MAKEWORD(2, 2), &data); }();
|
||||
if (startup) return Fail(5);
|
||||
#endif
|
||||
const auto native = ::socket(family == 2 ? AF_INET : AF_INET6, SOCK_DGRAM, protocol);
|
||||
if (native == Invalid) return Fail(NativeError());
|
||||
Socket guard(native);
|
||||
try {
|
||||
auto socket = std::make_shared<Socket>(native);
|
||||
guard.value = Invalid;
|
||||
std::lock_guard lock(socketsMutex);
|
||||
if (nextDescriptor == INT_MAX) return Fail(24);
|
||||
const int descriptor = nextDescriptor++;
|
||||
sockets.emplace(descriptor, std::move(socket));
|
||||
return descriptor;
|
||||
} catch (const std::bad_alloc&) {
|
||||
return Fail(12);
|
||||
}
|
||||
}
|
||||
int APS5_VABI bind_nid_postfix(int descriptor, const void* address, std::uint32_t length) {
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
sockaddr_storage native{};
|
||||
socklen_t size;
|
||||
if (!Address(address, length, native, size)) return -1;
|
||||
return ::bind(socket->value, reinterpret_cast<sockaddr*>(&native), size) ? Fail(NativeError()) : 0;
|
||||
}
|
||||
int APS5_VABI getsockname_nid_postfix(int descriptor, void* address, std::uint32_t* length) {
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
if (!address || !length) return Fail(14);
|
||||
sockaddr_storage native{};
|
||||
socklen_t size = sizeof(native);
|
||||
if (::getsockname(socket->value, reinterpret_cast<sockaddr*>(&native), &size)) return Fail(NativeError());
|
||||
GuestAddress(native, address, length);
|
||||
return 0;
|
||||
}
|
||||
int APS5_VABI ioctl_nid_postfix(int descriptor, std::uint64_t request, void* argument) {
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
if (!argument) return Fail(14);
|
||||
if (request != 0x8004667e && request != 0x4004667f) return Fail(25);
|
||||
std::lock_guard lock(socket->modeMutex);
|
||||
unsigned long value = 0;
|
||||
if (request == 0x8004667e) value = *static_cast<int*>(argument) != 0;
|
||||
#ifdef _WIN32
|
||||
const auto result = ioctlsocket(socket->value, request == 0x8004667e ? FIONBIO : FIONREAD, &value);
|
||||
#else
|
||||
int nativeValue = static_cast<int>(value);
|
||||
const auto result = ::ioctl(socket->value, request == 0x8004667e ? FIONBIO : FIONREAD, &nativeValue);
|
||||
value = static_cast<unsigned long>(nativeValue);
|
||||
#endif
|
||||
if (result) return Fail(NativeError());
|
||||
if (request == 0x8004667e) socket->nonblocking = value != 0;
|
||||
if (request == 0x4004667f) *static_cast<int*>(argument) = static_cast<int>(value);
|
||||
return 0;
|
||||
}
|
||||
std::int64_t APS5_VABI sendto_nid_postfix(int descriptor, const void* buffer, std::uint64_t length,
|
||||
int flags, const void* address, std::uint32_t addressLength) {
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
if (flags != 0) return Fail(45);
|
||||
if (length > INT_MAX) return Fail(40);
|
||||
if (!buffer && length) return Fail(14);
|
||||
sockaddr_storage native{};
|
||||
socklen_t size;
|
||||
if (!Address(address, addressLength, native, size)) return -1;
|
||||
const auto result = ::sendto(socket->value, static_cast<const char*>(buffer), static_cast<int>(length), 0,
|
||||
reinterpret_cast<sockaddr*>(&native), size);
|
||||
return result < 0 ? Fail(NativeError()) : result;
|
||||
}
|
||||
std::int64_t APS5_VABI recvfrom_nid_postfix(int descriptor, void* buffer, std::uint64_t length,
|
||||
int flags, void* address, std::uint32_t* addressLength) {
|
||||
const auto socket = Lookup(descriptor);
|
||||
if (!socket) return -1;
|
||||
if ((flags & ~2) != 0) return Fail(45);
|
||||
if (length > INT_MAX) return Fail(40);
|
||||
if ((!buffer && length) || (address && !addressLength)) return Fail(14);
|
||||
sockaddr_storage native{};
|
||||
socklen_t size = sizeof(native);
|
||||
const auto result = ::recvfrom(socket->value, static_cast<char*>(buffer), static_cast<int>(length),
|
||||
flags & 2 ? MSG_PEEK : 0, reinterpret_cast<sockaddr*>(&native), &size);
|
||||
if (result < 0) return Fail(NativeError());
|
||||
if (address) GuestAddress(native, address, addressLength);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,91 +1,320 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include "prx/libc/include/Shutdown.hpp"
|
||||
#include "prx/libkernel/DirectMemory/DirectMemory.hpp"
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
#ifndef _WIN32
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
static constexpr int SCE_KERNEL_ERROR_EINVAL = static_cast<int>(0x80020016);
|
||||
namespace {
|
||||
|
||||
constexpr int sceInvalidArgument = static_cast<int>(0x80020016u);
|
||||
|
||||
std::atomic<std::uint32_t> gpoBits{0};
|
||||
constexpr std::array<std::uint8_t, 16> openPsId{'A', 'n', 'y', 'P', 'S', '5', 'O', 'p', 'e', 'n', 'P', 's', 'I', 'd', 0, 1};
|
||||
|
||||
class ProcessArguments {
|
||||
public:
|
||||
ProcessArguments() {
|
||||
#ifdef _WIN32
|
||||
std::array<char, 32768> path{};
|
||||
const auto size = GetModuleFileNameA(nullptr, path.data(), static_cast<DWORD>(path.size()));
|
||||
if (size == 0)
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Reading executable path");
|
||||
if (size >= path.size())
|
||||
throw std::runtime_error("Executable path exceeds the guest argument buffer");
|
||||
arguments.emplace_back(path.data(), size);
|
||||
#else
|
||||
std::ifstream stream("/proc/self/cmdline", std::ios::binary);
|
||||
if (!stream)
|
||||
throw std::runtime_error("Cannot read process arguments");
|
||||
std::string argument;
|
||||
while (std::getline(stream, argument, '\0')) {
|
||||
if (stream.eof())
|
||||
throw std::runtime_error("Unterminated process argument");
|
||||
arguments.push_back(argument);
|
||||
}
|
||||
if (stream.bad() || !stream.eof())
|
||||
throw std::runtime_error("Reading process arguments failed");
|
||||
#endif
|
||||
if (arguments.empty() || arguments.size() > static_cast<std::size_t>(std::numeric_limits<int>::max()))
|
||||
throw std::runtime_error("Invalid process argument count");
|
||||
for (const auto& argument : arguments)
|
||||
pointers.push_back(argument.c_str());
|
||||
pointers.push_back(nullptr);
|
||||
}
|
||||
|
||||
int GetCount() const { return static_cast<int>(arguments.size()); }
|
||||
const char** GetValues() { return pointers.data(); }
|
||||
|
||||
private:
|
||||
std::vector<std::string> arguments;
|
||||
std::vector<const char*> pointers;
|
||||
};
|
||||
|
||||
ProcessArguments& getProcessArguments() {
|
||||
static ProcessArguments arguments;
|
||||
return arguments;
|
||||
}
|
||||
|
||||
void validateSchedulingPolicy(int policy) {
|
||||
if (policy != 1 && policy != 3)
|
||||
throw std::invalid_argument("Unsupported guest scheduling policy");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
void syncVolumes() {
|
||||
std::array<wchar_t, 32768> name{};
|
||||
const auto first = FindFirstVolumeW(name.data(), static_cast<DWORD>(name.size()));
|
||||
if (first == INVALID_HANDLE_VALUE)
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Enumerating volumes for sync");
|
||||
const std::unique_ptr<void, decltype(&FindVolumeClose)> search(first, &FindVolumeClose);
|
||||
for (;;) {
|
||||
DWORD flags = 0;
|
||||
if (!GetVolumeInformationW(name.data(), nullptr, 0, nullptr, nullptr, &flags, nullptr, 0))
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Reading volume properties for sync");
|
||||
if ((flags & FILE_READ_ONLY_VOLUME) == 0) {
|
||||
std::wstring path(name.data());
|
||||
if (path.empty() || path.back() != L'\\')
|
||||
throw std::runtime_error("Invalid volume path for sync");
|
||||
path.pop_back();
|
||||
const auto native = CreateFileW(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if (native == INVALID_HANDLE_VALUE)
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Opening volume for sync");
|
||||
const std::unique_ptr<void, decltype(&CloseHandle)> volume(native, &CloseHandle);
|
||||
if (!FlushFileBuffers(volume.get()))
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Flushing volume");
|
||||
}
|
||||
if (FindNextVolumeW(search.get(), name.data(), static_cast<DWORD>(name.size())))
|
||||
continue;
|
||||
const auto error = GetLastError();
|
||||
if (error != ERROR_NO_MORE_FILES)
|
||||
throw std::system_error(error, std::system_category(), "Enumerating volumes for sync");
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
struct GuestResourceUsage {
|
||||
KernelTimeval ru_utime;
|
||||
KernelTimeval ru_stime;
|
||||
std::int64_t ru_maxrss;
|
||||
std::int64_t ru_ixrss;
|
||||
std::int64_t ru_idrss;
|
||||
std::int64_t ru_isrss;
|
||||
std::int64_t ru_minflt;
|
||||
std::int64_t ru_majflt;
|
||||
std::int64_t ru_nswap;
|
||||
std::int64_t ru_inblock;
|
||||
std::int64_t ru_oublock;
|
||||
std::int64_t ru_msgsnd;
|
||||
std::int64_t ru_msgrcv;
|
||||
std::int64_t ru_nsignals;
|
||||
std::int64_t ru_nvcsw;
|
||||
std::int64_t ru_nivcsw;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
// unknown data
|
||||
const char* __progname_nid_postfix = "eboot.bin";
|
||||
|
||||
int APS5_VABI getargc_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return getProcessArguments().GetCount();
|
||||
}
|
||||
|
||||
const char** APS5_VABI getargv_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
return getProcessArguments().GetValues();
|
||||
}
|
||||
|
||||
int APS5_VABI getpagesize_nid_postfix(void) {
|
||||
return 0x4000;
|
||||
return PS5_PAGE_SIZE;
|
||||
}
|
||||
|
||||
int APS5_VABI getpid_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
#ifdef _WIN32
|
||||
const auto pid = GetCurrentProcessId();
|
||||
#else
|
||||
const auto pid = ::getpid();
|
||||
#endif
|
||||
if (pid == 0 || static_cast<std::uint64_t>(pid) > static_cast<std::uint64_t>(std::numeric_limits<int>::max()))
|
||||
throw std::runtime_error("Process identifier is outside the guest range");
|
||||
return static_cast<int>(pid);
|
||||
}
|
||||
|
||||
void APS5_VABI exit_nid_postfix(int code) {
|
||||
(void)code;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
LibcExit_nid_no_patch(code);
|
||||
}
|
||||
|
||||
[[noreturn]] void APS5_VABI _exit_nid_postfix(int status) {
|
||||
std::_Exit(status);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelGetCurrentCpu(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
#ifdef _WIN32
|
||||
PROCESSOR_NUMBER processor{};
|
||||
GetCurrentProcessorNumberEx(&processor);
|
||||
unsigned index = processor.Number;
|
||||
for (WORD group = 0; group < processor.Group; ++group) {
|
||||
const auto count = GetActiveProcessorCount(group);
|
||||
if (count == 0)
|
||||
throw std::system_error(GetLastError(), std::system_category(), "Reading processor group size");
|
||||
index += count;
|
||||
}
|
||||
return static_cast<int>(index);
|
||||
#else
|
||||
const int cpu = ::sched_getcpu();
|
||||
if (cpu < 0)
|
||||
throw std::system_error(errno, std::generic_category(), "Reading current processor");
|
||||
return cpu;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t APS5_VABI sceKernelGetGPI(void) {
|
||||
std::uint64_t APS5_VABI sceKernelGetGPI(void) {
|
||||
return gpoBits.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void APS5_VABI sceKernelSetGPO(std::uint32_t bits) {
|
||||
gpoBits.store(bits, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelGetOpenPsId(void* openPsIdOutput) {
|
||||
if (!openPsIdOutput)
|
||||
throw std::invalid_argument("sceKernelGetOpenPsId: null output");
|
||||
std::memcpy(openPsIdOutput, openPsId.data(), openPsId.size());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void APS5_VABI sceKernelSetGPO(uint32_t bits) {
|
||||
(void)bits;
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelGetOpenPsId(void* open_ps_id) {
|
||||
(void)open_ps_id;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* APS5_VABI sceKernelGetProcParam(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
return const_cast<void*>(ApplicationProcessParameters_nid_no_patch());
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelUuidCreate(uint8_t* uuid) {
|
||||
if (!uuid) return SCE_KERNEL_ERROR_EINVAL;
|
||||
static std::mutex lock;
|
||||
static std::mt19937_64 generator{std::random_device{}()};
|
||||
std::lock_guard guard(lock);
|
||||
for (int index = 0; index < 16; index += 8) {
|
||||
const uint64_t bits = generator();
|
||||
std::memcpy(uuid + index, &bits, 8);
|
||||
}
|
||||
uuid[6] = static_cast<uint8_t>((uuid[6] & 0x0F) | 0x40);
|
||||
uuid[8] = static_cast<uint8_t>((uuid[8] & 0x3F) | 0x80);
|
||||
int APS5_VABI sceKernelUuidCreate(std::uint32_t* uuid) {
|
||||
if (!uuid) return sceInvalidArgument;
|
||||
static thread_local std::random_device device;
|
||||
std::uniform_int_distribution<std::uint32_t> distribution;
|
||||
std::array<std::uint32_t, 4> value;
|
||||
for (auto& word : value)
|
||||
word = distribution(device);
|
||||
value[1] = (value[1] & 0x0fffffffu) | 0x40000000u;
|
||||
value[2] = (value[2] & 0xffffff3fu) | 0x80u;
|
||||
std::memcpy(uuid, value.data(), sizeof(value));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void APS5_VABI sceKernelSync(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
#ifdef _WIN32
|
||||
syncVolumes();
|
||||
#else
|
||||
::sync();
|
||||
#endif
|
||||
}
|
||||
|
||||
int APS5_VABI sched_get_priority_max_nid_postfix(int policy) {
|
||||
(void)policy;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
validateSchedulingPolicy(policy);
|
||||
return 256;
|
||||
}
|
||||
|
||||
int APS5_VABI sched_get_priority_min_nid_postfix(int policy) {
|
||||
(void)policy;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
validateSchedulingPolicy(policy);
|
||||
return 767;
|
||||
}
|
||||
|
||||
int APS5_VABI getrusage_nid_postfix(int who, GuestResourceUsage* usage) {
|
||||
if (usage == nullptr)
|
||||
throw std::invalid_argument("getrusage: usage is null");
|
||||
if (who != 0 && who != 1)
|
||||
throw std::invalid_argument("getrusage: unsupported who");
|
||||
#ifdef _WIN32
|
||||
FILETIME creation{};
|
||||
FILETIME exitTime{};
|
||||
FILETIME kernel{};
|
||||
FILETIME user{};
|
||||
if (who == 0) {
|
||||
if (!GetProcessTimes(GetCurrentProcess(), &creation, &exitTime, &kernel, &user))
|
||||
throw std::system_error(GetLastError(), std::system_category(), "getrusage: GetProcessTimes failed");
|
||||
} else {
|
||||
if (!GetThreadTimes(GetCurrentThread(), &creation, &exitTime, &kernel, &user))
|
||||
throw std::system_error(GetLastError(), std::system_category(), "getrusage: GetThreadTimes failed");
|
||||
}
|
||||
const auto toMicros = [](const FILETIME& time) {
|
||||
return ((static_cast<std::uint64_t>(time.dwHighDateTime) << 32) | time.dwLowDateTime) / 10ULL;
|
||||
};
|
||||
const auto userMicros = toMicros(user);
|
||||
const auto kernelMicros = toMicros(kernel);
|
||||
usage->ru_utime.tv_sec = static_cast<std::int64_t>(userMicros / 1000000ULL);
|
||||
usage->ru_utime.tv_usec = static_cast<std::int64_t>(userMicros % 1000000ULL);
|
||||
usage->ru_stime.tv_sec = static_cast<std::int64_t>(kernelMicros / 1000000ULL);
|
||||
usage->ru_stime.tv_usec = static_cast<std::int64_t>(kernelMicros % 1000000ULL);
|
||||
usage->ru_maxrss = 0;
|
||||
usage->ru_ixrss = 0;
|
||||
usage->ru_idrss = 0;
|
||||
usage->ru_isrss = 0;
|
||||
usage->ru_minflt = 0;
|
||||
usage->ru_majflt = 0;
|
||||
usage->ru_nswap = 0;
|
||||
usage->ru_inblock = 0;
|
||||
usage->ru_oublock = 0;
|
||||
usage->ru_msgsnd = 0;
|
||||
usage->ru_msgrcv = 0;
|
||||
usage->ru_nsignals = 0;
|
||||
usage->ru_nvcsw = 0;
|
||||
usage->ru_nivcsw = 0;
|
||||
#else
|
||||
if (who == 1)
|
||||
throw std::invalid_argument("getrusage: RUSAGE_THREAD is not supported on this platform");
|
||||
struct rusage native{};
|
||||
if (::getrusage(RUSAGE_SELF, &native) != 0)
|
||||
throw std::system_error(errno, std::generic_category(), "getrusage: getrusage failed");
|
||||
usage->ru_utime.tv_sec = static_cast<std::int64_t>(native.ru_utime.tv_sec);
|
||||
usage->ru_utime.tv_usec = static_cast<std::int64_t>(native.ru_utime.tv_usec);
|
||||
usage->ru_stime.tv_sec = static_cast<std::int64_t>(native.ru_stime.tv_sec);
|
||||
usage->ru_stime.tv_usec = static_cast<std::int64_t>(native.ru_stime.tv_usec);
|
||||
usage->ru_maxrss = static_cast<std::int64_t>(native.ru_maxrss);
|
||||
usage->ru_ixrss = static_cast<std::int64_t>(native.ru_ixrss);
|
||||
usage->ru_idrss = static_cast<std::int64_t>(native.ru_idrss);
|
||||
usage->ru_isrss = static_cast<std::int64_t>(native.ru_isrss);
|
||||
usage->ru_minflt = static_cast<std::int64_t>(native.ru_minflt);
|
||||
usage->ru_majflt = static_cast<std::int64_t>(native.ru_majflt);
|
||||
usage->ru_nswap = static_cast<std::int64_t>(native.ru_nswap);
|
||||
usage->ru_inblock = static_cast<std::int64_t>(native.ru_inblock);
|
||||
usage->ru_oublock = static_cast<std::int64_t>(native.ru_oublock);
|
||||
usage->ru_msgsnd = static_cast<std::int64_t>(native.ru_msgsnd);
|
||||
usage->ru_msgrcv = static_cast<std::int64_t>(native.ru_msgrcv);
|
||||
usage->ru_nsignals = static_cast<std::int64_t>(native.ru_nsignals);
|
||||
usage->ru_nvcsw = static_cast<std::int64_t>(native.ru_nvcsw);
|
||||
usage->ru_nivcsw = static_cast<std::int64_t>(native.ru_nivcsw);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelIsTrinityMode(void) {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,37 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include "prx/libkernel/Pthread/include/ThreadLifecycle.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
std::mutex rtldHeapMutex;
|
||||
std::array<void*, 10> rtldHeapApi{};
|
||||
|
||||
void registerApplicationHeapApi(void* const* api) {
|
||||
if (api == nullptr)
|
||||
throw std::invalid_argument("RTLD application heap: null allocator API");
|
||||
std::array<void*, 10> replacement;
|
||||
std::memcpy(replacement.data(), api, sizeof(replacement));
|
||||
if (replacement[0] == nullptr || replacement[1] == nullptr)
|
||||
throw std::invalid_argument("RTLD application heap: malloc and free are required");
|
||||
std::lock_guard lock(rtldHeapMutex);
|
||||
if (rtldHeapApi[0] != nullptr && rtldHeapApi != replacement)
|
||||
throw std::runtime_error("RTLD application heap: cannot replace an active allocator");
|
||||
rtldHeapApi = replacement;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void APS5_VABI sceKernelRtldSetApplicationHeapAPI(void* api[]) {
|
||||
ApplicationHeapRegister_nid_no_patch(api);
|
||||
registerApplicationHeapApi(api);
|
||||
}
|
||||
|
||||
int APS5_VABI sceKernelRtldThreadAtexitDecrement(uint64_t* c) {
|
||||
|
|
@ -23,18 +47,47 @@ int APS5_VABI sceKernelRtldThreadAtexitIncrement(uint64_t* c) {
|
|||
}
|
||||
|
||||
void APS5_VABI sceKernelSetThreadAtexitCount(get_thread_atexit_count_func_t func) {
|
||||
(void)func;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
ThreadLifecycle::SetThreadAtexitCount(func);
|
||||
}
|
||||
|
||||
void APS5_VABI sceKernelSetThreadAtexitReport(thread_atexit_report_func_t func) {
|
||||
(void)func;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
ThreadLifecycle::SetThreadAtexitReport(func);
|
||||
}
|
||||
|
||||
void APS5_VABI sceKernelSetThreadDtors(thread_dtors_func_t dtors) {
|
||||
(void)dtors;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
ThreadLifecycle::SetThreadDtors(dtors);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void APS5_VABI _sceKernelRtldSetApplicationHeapAPI_nid_postfix(void* api[]) {
|
||||
registerApplicationHeapApi(api);
|
||||
}
|
||||
|
||||
int APS5_VABI _sceKernelRtldThreadAtexitDecrement_nid_postfix(std::uint64_t* counter) {
|
||||
(void)counter;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI _sceKernelRtldThreadAtexitIncrement_nid_postfix(std::uint64_t* counter) {
|
||||
(void)counter;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void APS5_VABI _sceKernelSetThreadAtexitCount_nid_postfix(get_thread_atexit_count_func_t callback) {
|
||||
ThreadLifecycle::SetThreadAtexitCount(callback);
|
||||
}
|
||||
|
||||
void APS5_VABI _sceKernelSetThreadAtexitReport_nid_postfix(thread_atexit_report_func_t callback) {
|
||||
ThreadLifecycle::SetThreadAtexitReport(callback);
|
||||
}
|
||||
|
||||
void APS5_VABI _sceKernelSetThreadDtors_nid_postfix(thread_dtors_func_t callback) {
|
||||
ThreadLifecycle::SetThreadDtors(callback);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,19 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI sceKernelIsAddressSanitizerEnabled(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
MallocReplace* APS5_VABI sceKernelGetSanitizerMallocReplaceExternal(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
static MallocReplace replacement{};
|
||||
return &replacement;
|
||||
}
|
||||
|
||||
NewReplace* APS5_VABI sceKernelGetSanitizerNewReplaceExternal(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return nullptr;
|
||||
static NewReplace replacement{};
|
||||
return &replacement;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
#include "prx/libc/include/General.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
|
||||
extern "C" int* APS5_VABI __error_nid_postfix();
|
||||
namespace {
|
||||
using GuestHandler = void (APS5_VABI *)(int);
|
||||
std::atomic<GuestHandler> handlers[32]{};
|
||||
static_assert(std::atomic<GuestHandler>::is_always_lock_free);
|
||||
std::atomic<std::uint32_t> blockedMask{0};
|
||||
static_assert(std::atomic<std::uint32_t>::is_always_lock_free);
|
||||
std::mutex registration;
|
||||
int NativeSignal(int guest) {
|
||||
switch (guest) {
|
||||
case 2: return SIGINT;
|
||||
case 4: return SIGILL;
|
||||
case 6: return SIGABRT;
|
||||
case 8: return SIGFPE;
|
||||
case 11: return SIGSEGV;
|
||||
case 15: return SIGTERM;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
void Dispatch(int native) {
|
||||
int guest = 0;
|
||||
for (int candidate : {2, 4, 6, 8, 11, 15})
|
||||
if (NativeSignal(candidate) == native) { guest = candidate; break; }
|
||||
if (!guest) return;
|
||||
#ifdef _WIN32
|
||||
// Preserve the guest's persistent registration across CRT delivery.
|
||||
std::signal(native, Dispatch);
|
||||
#endif
|
||||
if ((blockedMask.load() & (1u << guest)) != 0) return;
|
||||
const auto callback = handlers[guest].load();
|
||||
if (reinterpret_cast<std::uintptr_t>(callback) > 1) callback(guest);
|
||||
}
|
||||
}
|
||||
|
||||
struct GuestSignalSet {
|
||||
std::uint32_t bits[4];
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
GuestHandler APS5_VABI signal_nid_postfix(int guest, GuestHandler handler) {
|
||||
const auto invalid = reinterpret_cast<GuestHandler>(static_cast<std::uintptr_t>(-1));
|
||||
const int native = NativeSignal(guest);
|
||||
if (!native || handler == invalid) { *__error_nid_postfix() = 22; return invalid; }
|
||||
std::lock_guard lock(registration);
|
||||
const auto previous = handlers[guest].exchange(handler);
|
||||
const auto address = reinterpret_cast<std::uintptr_t>(handler);
|
||||
auto hostHandler = address == 0 ? SIG_DFL : address == 1 ? SIG_IGN : Dispatch;
|
||||
if (std::signal(native, hostHandler) == SIG_ERR) {
|
||||
handlers[guest].store(previous);
|
||||
*__error_nid_postfix() = 22;
|
||||
return invalid;
|
||||
}
|
||||
return previous;
|
||||
}
|
||||
int APS5_VABI raise_nid_postfix(int guest) {
|
||||
const int native = NativeSignal(guest);
|
||||
if (!native) { *__error_nid_postfix() = 22; return -1; }
|
||||
const int result = std::raise(native);
|
||||
if (result) *__error_nid_postfix() = 22;
|
||||
return result ? -1 : 0;
|
||||
}
|
||||
int APS5_VABI _sigprocmask_nid_postfix(int how, const GuestSignalSet* set, GuestSignalSet* previousSet) {
|
||||
std::lock_guard lock(registration);
|
||||
if (previousSet != nullptr) {
|
||||
previousSet->bits[0] = blockedMask.load();
|
||||
previousSet->bits[1] = 0;
|
||||
previousSet->bits[2] = 0;
|
||||
previousSet->bits[3] = 0;
|
||||
}
|
||||
if (set != nullptr) {
|
||||
switch (how) {
|
||||
case 1: blockedMask.fetch_or(set->bits[0]); break;
|
||||
case 2: blockedMask.fetch_and(~set->bits[0]); break;
|
||||
case 3: blockedMask.store(set->bits[0]); break;
|
||||
default: throw std::invalid_argument("_sigprocmask: invalid how");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
int APS5_VABI _is_signal_return_nid_postfix(std::uint64_t programCounter) {
|
||||
(void)programCounter;
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "prx/libkernel/DirectMemory/DirectMemory.hpp"
|
||||
#include <cstdint>
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
extern "C" int* APS5_VABI __error_nid_postfix();
|
||||
|
||||
extern "C" {
|
||||
// Guest long is 64 bits, including when the Windows host long is 32 bits.
|
||||
std::int64_t APS5_VABI sysconf_nid_postfix(int name) {
|
||||
const int saved = *__error_nid_postfix();
|
||||
std::int64_t result = -1;
|
||||
switch (name) {
|
||||
case 47: // _SC_PAGESIZE
|
||||
result = PS5_PAGE_SIZE;
|
||||
break;
|
||||
case 57: // _SC_NPROCESSORS_CONF
|
||||
case 58: // _SC_NPROCESSORS_ONLN
|
||||
#ifdef _WIN32
|
||||
result = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
|
||||
#else
|
||||
result = ::sysconf(name == 57 ? _SC_NPROCESSORS_CONF : _SC_NPROCESSORS_ONLN);
|
||||
#endif
|
||||
break;
|
||||
case 121: { // _SC_PHYS_PAGES: report host physical capacity in guest pages
|
||||
#ifdef _WIN32
|
||||
MEMORYSTATUSEX memory{};
|
||||
memory.dwLength = sizeof(memory);
|
||||
if (GlobalMemoryStatusEx(&memory)) result = memory.ullTotalPhys / PS5_PAGE_SIZE;
|
||||
#else
|
||||
const auto pages = ::sysconf(_SC_PHYS_PAGES);
|
||||
const auto pageSize = ::sysconf(_SC_PAGESIZE);
|
||||
if (pages > 0 && pageSize > 0)
|
||||
result = static_cast<std::uint64_t>(pages) * pageSize / PS5_PAGE_SIZE;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
*__error_nid_postfix() = 22;
|
||||
return -1;
|
||||
}
|
||||
if (result <= 0) { *__error_nid_postfix() = 5; return -1; }
|
||||
*__error_nid_postfix() = saved;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -241,12 +241,16 @@ int APS5_VABI nanosleep_nid_postfix(const KernelTimespec* rqtp, KernelTimespec*
|
|||
return sceKernelNanosleep(rqtp, rmtp);
|
||||
}
|
||||
|
||||
int APS5_VABI _nanosleep_nid_postfix(const KernelTimespec* rqtp, KernelTimespec* rmtp) {
|
||||
return sceKernelNanosleep(rqtp, rmtp);
|
||||
}
|
||||
|
||||
int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp) {
|
||||
if (tp == nullptr) {
|
||||
APS5_INVALID_ARG_EX;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (clockId == 0 || clockId == 9 || clockId == 10) {
|
||||
if (clockId == 0 || clockId == 9) {
|
||||
FILETIME ft{};
|
||||
GetSystemTimePreciseAsFileTime(&ft);
|
||||
std::uint64_t t = (static_cast<std::uint64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||
|
|
@ -256,6 +260,16 @@ int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp) {
|
|||
tp->tv_nsec = static_cast<std::int64_t>(t % 1000000000ULL);
|
||||
return 0;
|
||||
}
|
||||
if (clockId == 10 || clockId == 13) {
|
||||
FILETIME ft{};
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
std::uint64_t t = (static_cast<std::uint64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
||||
t -= 116444736000000000ULL;
|
||||
t *= 100ULL;
|
||||
tp->tv_sec = static_cast<std::int64_t>(t / 1000000000ULL);
|
||||
tp->tv_nsec = static_cast<std::int64_t>(t % 1000000000ULL);
|
||||
return 0;
|
||||
}
|
||||
if (clockId == 4 || clockId == 1 || clockId == 5 || clockId == 7 || clockId == 8 || clockId == 11 || clockId == 12) {
|
||||
std::uint64_t nanos = GetMonotonicNanos();
|
||||
tp->tv_sec = static_cast<std::int64_t>(nanos / 1000000000ULL);
|
||||
|
|
@ -268,9 +282,16 @@ int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp) {
|
|||
switch (clockId) {
|
||||
case 0:
|
||||
case 9:
|
||||
case 10:
|
||||
nativeId = CLOCK_REALTIME;
|
||||
break;
|
||||
case 10:
|
||||
case 13:
|
||||
#ifdef CLOCK_REALTIME_COARSE
|
||||
nativeId = CLOCK_REALTIME_COARSE;
|
||||
#else
|
||||
nativeId = CLOCK_REALTIME;
|
||||
#endif
|
||||
break;
|
||||
case 4:
|
||||
case 7:
|
||||
case 11:
|
||||
|
|
@ -336,7 +357,7 @@ int APS5_VABI clock_getres_nid_postfix(int clockId, KernelTimespec* res) {
|
|||
res->tv_nsec = 100LL;
|
||||
return 0;
|
||||
}
|
||||
if (clockId == 4 || clockId == 1 || clockId == 5 || clockId == 7 || clockId == 8 || clockId == 10 || clockId == 11 || clockId == 12) {
|
||||
if (clockId == 4 || clockId == 1 || clockId == 5 || clockId == 7 || clockId == 8 || clockId == 10 || clockId == 11 || clockId == 12 || clockId == 13) {
|
||||
static const std::uint64_t freq = [] {
|
||||
LARGE_INTEGER f{};
|
||||
QueryPerformanceFrequency(&f);
|
||||
|
|
@ -353,9 +374,16 @@ int APS5_VABI clock_getres_nid_postfix(int clockId, KernelTimespec* res) {
|
|||
switch (clockId) {
|
||||
case 0:
|
||||
case 9:
|
||||
case 10:
|
||||
nativeId = CLOCK_REALTIME;
|
||||
break;
|
||||
case 10:
|
||||
case 13:
|
||||
#ifdef CLOCK_REALTIME_COARSE
|
||||
nativeId = CLOCK_REALTIME_COARSE;
|
||||
#else
|
||||
nativeId = CLOCK_REALTIME;
|
||||
#endif
|
||||
break;
|
||||
case 4:
|
||||
case 7:
|
||||
case 11:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" {
|
||||
int APS5_VABI __inet_pton_nid_postfix(int, const char*, void*);
|
||||
const char* APS5_VABI __inet_ntop_nid_postfix(int, const void*, char*, std::uint32_t);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
}
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
int main() {
|
||||
for (int family : {2, 28}) {
|
||||
const char* input = family == 2 ? "192.0.2.17" : "2001:db8::17";
|
||||
std::array<unsigned char, 16> address{};
|
||||
Require(__inet_pton_nid_postfix(family, input, address.data()) == 1);
|
||||
char output[64]{};
|
||||
Require(__inet_ntop_nid_postfix(family, address.data(), output, sizeof(output)) == output);
|
||||
Require(std::strcmp(input, output) == 0);
|
||||
output[0] = 'x';
|
||||
Require(__inet_ntop_nid_postfix(family, address.data(), output, 1) == nullptr);
|
||||
Require(*__error_nid_postfix() == 28 && output[0] == 'x');
|
||||
const auto original = address;
|
||||
Require(__inet_pton_nid_postfix(family, "not-an-address", address.data()) == 0);
|
||||
Require(address == original);
|
||||
}
|
||||
std::array<unsigned char, 16> address{};
|
||||
Require(__inet_pton_nid_postfix(10, "::1", address.data()) == -1);
|
||||
Require(*__error_nid_postfix() == 47); // Linux AF_INET6 is not PS5 AF_INET6
|
||||
Require(__inet_pton_nid_postfix(2, "256.1.2.3", address.data()) == 0);
|
||||
Require(__inet_pton_nid_postfix(28, "::ffff:192.0.2.17", address.data()) == 1);
|
||||
Require(address[10] == 255 && address[11] == 255 && address[12] == 192 && address[15] == 17);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#include "prx/libSceVideoOut/include/BufferReuseTracker.hpp"
|
||||
#include <cstdlib>
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
int main() {
|
||||
BufferReuseTracker buffer;
|
||||
Require(buffer.IsComplete(buffer.Capture()));
|
||||
const auto first = buffer.Reserve();
|
||||
const auto second = buffer.Reserve();
|
||||
const auto fence = buffer.Capture();
|
||||
const auto future = buffer.Reserve();
|
||||
Require(!buffer.IsComplete(fence));
|
||||
buffer.Complete(second); // Out-of-order retirement must not release first.
|
||||
Require(!buffer.IsComplete(fence));
|
||||
buffer.Complete(first);
|
||||
Require(buffer.IsComplete(fence)); // Later reservations do not block this wait.
|
||||
Require(!buffer.IsComplete(buffer.Capture()));
|
||||
buffer.Complete(future); // Cancellation uses the same retirement operation.
|
||||
Require(buffer.IsComplete(buffer.Capture()));
|
||||
const auto next = buffer.Reserve();
|
||||
Require(next > future && buffer.IsComplete(fence));
|
||||
buffer.Complete(next);
|
||||
bool rejected = false;
|
||||
try { buffer.IsComplete(next + 1); }
|
||||
catch (const std::invalid_argument&) { rejected = true; }
|
||||
Require(rejected);
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cctype>
|
||||
#include <clocale>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
extern "C" {
|
||||
int APS5_VABI isupper_nid_postfix(int);
|
||||
int APS5_VABI islower_nid_postfix(int);
|
||||
int APS5_VABI isalpha_nid_postfix(int);
|
||||
int APS5_VABI isdigit_nid_postfix(int);
|
||||
int APS5_VABI isalnum_nid_postfix(int);
|
||||
int APS5_VABI isspace_nid_postfix(int);
|
||||
int APS5_VABI isblank_nid_postfix(int);
|
||||
int APS5_VABI iscntrl_nid_postfix(int);
|
||||
int APS5_VABI isprint_nid_postfix(int);
|
||||
int APS5_VABI isgraph_nid_postfix(int);
|
||||
int APS5_VABI ispunct_nid_postfix(int);
|
||||
int APS5_VABI isxdigit_nid_postfix(int);
|
||||
int APS5_VABI toupper_nid_postfix(int);
|
||||
int APS5_VABI tolower_nid_postfix(int);
|
||||
const short* APS5_VABI _Getptolower_nid_postfix();
|
||||
const short* APS5_VABI _Getptoupper_nid_postfix();
|
||||
}
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
int main() {
|
||||
Require(std::setlocale(LC_CTYPE, "C") != nullptr);
|
||||
for (int c = EOF; c <= 255; ++c) {
|
||||
Require(bool(isupper_nid_postfix(c)) == bool(std::isupper(c)));
|
||||
Require(bool(islower_nid_postfix(c)) == bool(std::islower(c)));
|
||||
Require(bool(isalpha_nid_postfix(c)) == bool(std::isalpha(c)));
|
||||
Require(bool(isdigit_nid_postfix(c)) == bool(std::isdigit(c)));
|
||||
Require(bool(isalnum_nid_postfix(c)) == bool(std::isalnum(c)));
|
||||
Require(bool(isspace_nid_postfix(c)) == bool(std::isspace(c)));
|
||||
Require(bool(isblank_nid_postfix(c)) == bool(std::isblank(c)));
|
||||
Require(bool(iscntrl_nid_postfix(c)) == bool(std::iscntrl(c)));
|
||||
Require(bool(isprint_nid_postfix(c)) == bool(std::isprint(c)));
|
||||
Require(bool(isgraph_nid_postfix(c)) == bool(std::isgraph(c)));
|
||||
Require(bool(ispunct_nid_postfix(c)) == bool(std::ispunct(c)));
|
||||
Require(bool(isxdigit_nid_postfix(c)) == bool(std::isxdigit(c)));
|
||||
Require(toupper_nid_postfix(c) == std::toupper(c));
|
||||
Require(tolower_nid_postfix(c) == std::tolower(c));
|
||||
Require(toupper_nid_postfix(c) == _Getptoupper_nid_postfix()[c]);
|
||||
Require(tolower_nid_postfix(c) == _Getptolower_nid_postfix()[c]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#include "prx/libc/include/GuestDirectory.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
extern "C" {
|
||||
void* APS5_VABI opendir_nid_postfix(const char*);
|
||||
GuestDirectoryEntry* APS5_VABI readdir_nid_postfix(void*);
|
||||
int APS5_VABI closedir_nid_postfix(void*);
|
||||
void APS5_VABI rewinddir_nid_postfix(void*);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
}
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
int main() {
|
||||
const auto root = std::filesystem::path("anyps5-directory-test-" +
|
||||
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
|
||||
Require(std::filesystem::create_directory(root));
|
||||
Require(std::filesystem::create_directory(root / "subdirectory"));
|
||||
{ std::ofstream file(root / "sample.txt"); file << "test"; }
|
||||
void* directory = opendir_nid_postfix(root.string().c_str());
|
||||
Require(directory != nullptr);
|
||||
std::map<std::string, int> entries;
|
||||
*__error_nid_postfix() = 13;
|
||||
while (auto* entry = readdir_nid_postfix(directory)) {
|
||||
Require(entry->nameLength == std::strlen(entry->name));
|
||||
Require(entry->recordLength == 8 + ((entry->nameLength + 4) & ~3));
|
||||
Require(entry->recordLength <= sizeof(*entry));
|
||||
entries[entry->name] = entry->type;
|
||||
}
|
||||
Require(*__error_nid_postfix() == 13); // EOF preserves errno.
|
||||
Require(entries.at("sample.txt") == 8);
|
||||
Require(entries.at("subdirectory") == 4);
|
||||
Require(entries.at(".") == 4 && entries.at("..") == 4);
|
||||
rewinddir_nid_postfix(directory);
|
||||
std::size_t count = 0;
|
||||
while (readdir_nid_postfix(directory)) ++count;
|
||||
Require(count == entries.size());
|
||||
Require(closedir_nid_postfix(directory) == 0);
|
||||
Require(opendir_nid_postfix((root / "missing").string().c_str()) == nullptr);
|
||||
Require(*__error_nid_postfix() == 2);
|
||||
Require(opendir_nid_postfix((root / "sample.txt").string().c_str()) == nullptr);
|
||||
Require(*__error_nid_postfix() == 20);
|
||||
Require(opendir_nid_postfix("") == nullptr && *__error_nid_postfix() == 2);
|
||||
Require(closedir_nid_postfix(nullptr) == -1 && *__error_nid_postfix() == 9);
|
||||
std::filesystem::remove(root / "sample.txt");
|
||||
std::filesystem::remove(root / "subdirectory");
|
||||
std::filesystem::remove(root);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdlib>
|
||||
#include <thread>
|
||||
extern "C" {
|
||||
void* APS5_VABI dlopen_nid_postfix(const char*, int);
|
||||
void* APS5_VABI dlsym_nid_postfix(void*, const char*);
|
||||
int APS5_VABI dlclose_nid_postfix(void*);
|
||||
char* APS5_VABI dlerror_nid_postfix();
|
||||
}
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
int main(int argc, char** argv) {
|
||||
Require(argc == 2);
|
||||
Require(dlerror_nid_postfix() == nullptr);
|
||||
Require(dlopen_nid_postfix(argv[1], 0x2000) == nullptr);
|
||||
Require(dlerror_nid_postfix() != nullptr);
|
||||
Require(dlerror_nid_postfix() == nullptr);
|
||||
Require(dlopen_nid_postfix("anyps5-missing-module-for-test.prx", 2) == nullptr);
|
||||
Require(dlerror_nid_postfix() != nullptr);
|
||||
void* executable = dlopen_nid_postfix(nullptr, 2);
|
||||
Require(executable != nullptr && dlclose_nid_postfix(executable) == 0);
|
||||
void* module = dlopen_nid_postfix(argv[1], 2 | 0x100);
|
||||
Require(module != nullptr);
|
||||
using Add = int (APS5_VABI *)(int, int);
|
||||
auto add = reinterpret_cast<Add>(dlsym_nid_postfix(module, "GuestModuleAdd"));
|
||||
Require(add && add(17, 25) == 42);
|
||||
Require(dlsym_nid_postfix(reinterpret_cast<void*>(-2), "GuestModuleAdd") == reinterpret_cast<void*>(add));
|
||||
Require(dlsym_nid_postfix(module, "missing_symbol") == nullptr);
|
||||
std::thread other([] { Require(dlerror_nid_postfix() == nullptr); });
|
||||
other.join();
|
||||
Require(dlerror_nid_postfix() != nullptr);
|
||||
Require(dlerror_nid_postfix() == nullptr);
|
||||
void* second = dlopen_nid_postfix(argv[1], 1);
|
||||
Require(second && second != module);
|
||||
Require(dlclose_nid_postfix(module) == 0);
|
||||
Require(dlsym_nid_postfix(module, "GuestModuleAdd") == nullptr);
|
||||
add = reinterpret_cast<Add>(dlsym_nid_postfix(second, "GuestModuleAdd"));
|
||||
Require(add && add(2, 3) == 5);
|
||||
Require(dlclose_nid_postfix(second) == 0);
|
||||
Require(dlclose_nid_postfix(second) == -1);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
extern "C" {
|
||||
char* APS5_VABI getenv_nid_postfix(const char*);
|
||||
int APS5_VABI setenv_nid_postfix(const char*, const char*, int);
|
||||
int APS5_VABI unsetenv_nid_postfix(const char*);
|
||||
int APS5_VABI putenv_nid_postfix(char*);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
}
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
int main() {
|
||||
const char* key = "ANYPS5_GUEST_ENV_TEST_4C27";
|
||||
#ifdef _WIN32
|
||||
Require(_putenv_s(key, "inherited") == 0);
|
||||
#else
|
||||
Require(::setenv(key, "inherited", 1) == 0);
|
||||
#endif
|
||||
Require(std::strcmp(getenv_nid_postfix(key), "inherited") == 0);
|
||||
Require(setenv_nid_postfix(key, "kept out", 0) == 0);
|
||||
Require(std::strcmp(getenv_nid_postfix(key), "inherited") == 0);
|
||||
char value[] = "copied";
|
||||
Require(setenv_nid_postfix(key, value, 1) == 0);
|
||||
value[0] = 'X';
|
||||
Require(std::strcmp(getenv_nid_postfix(key), "copied") == 0);
|
||||
Require(std::strcmp(std::getenv(key), "inherited") == 0);
|
||||
Require(setenv_nid_postfix(key, "", 1) == 0);
|
||||
Require(getenv_nid_postfix(key) && *getenv_nid_postfix(key) == 0);
|
||||
char borrowed[] = "ANYPS5_GUEST_ENV_TEST_4C27=one";
|
||||
Require(putenv_nid_postfix(borrowed) == 0);
|
||||
char* position = std::strchr(borrowed, '=') + 1;
|
||||
Require(getenv_nid_postfix(key) == position);
|
||||
std::memcpy(position, "two", 3);
|
||||
Require(std::strcmp(getenv_nid_postfix(key), "two") == 0);
|
||||
Require(setenv_nid_postfix("anyps5_guest_env_test_4c27", "lower", 1) == 0);
|
||||
Require(std::strcmp(getenv_nid_postfix(key), "two") == 0);
|
||||
Require(setenv_nid_postfix("bad=name", "x", 1) == -1);
|
||||
Require(*__error_nid_postfix() == 22);
|
||||
Require(unsetenv_nid_postfix("") == -1);
|
||||
char invalid[] = "no-separator";
|
||||
Require(putenv_nid_postfix(invalid) == -1);
|
||||
Require(unsetenv_nid_postfix(key) == 0);
|
||||
Require(getenv_nid_postfix(key) == nullptr);
|
||||
Require(unsetenv_nid_postfix(key) == 0);
|
||||
Require(unsetenv_nid_postfix("anyps5_guest_env_test_4c27") == 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
|
||||
extern "C" {
|
||||
char* APS5_VABI strerror_nid_postfix(int);
|
||||
int APS5_VABI strerror_r_nid_postfix(int, char*, std::size_t);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
}
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
int main() {
|
||||
*__error_nid_postfix() = 13;
|
||||
Require(std::strcmp(strerror_nid_postfix(35), "Resource temporarily unavailable") == 0);
|
||||
Require(*__error_nid_postfix() == 13);
|
||||
Require(std::strcmp(strerror_nid_postfix(78), "Function not implemented") == 0);
|
||||
char* parent = strerror_nid_postfix(22);
|
||||
std::thread worker([] {
|
||||
Require(std::strcmp(strerror_nid_postfix(45), "Operation not supported") == 0);
|
||||
});
|
||||
worker.join();
|
||||
Require(std::strcmp(parent, "Invalid argument") == 0);
|
||||
char buffer[128];
|
||||
for (int error = 0; error <= 96; ++error) {
|
||||
Require(strerror_r_nid_postfix(error, buffer, sizeof(buffer)) == 0);
|
||||
Require(buffer[0] && std::strstr(buffer, "Unknown") == nullptr);
|
||||
}
|
||||
Require(strerror_r_nid_postfix(-1, buffer, sizeof(buffer)) == 22);
|
||||
Require(std::strstr(buffer, "-1") != nullptr);
|
||||
Require(*__error_nid_postfix() == 13);
|
||||
char sentinel[] = "xyz";
|
||||
Require(strerror_r_nid_postfix(22, sentinel, 1) == 34);
|
||||
Require(sentinel[0] == 0 && sentinel[1] == 'y');
|
||||
sentinel[0] = 'x';
|
||||
Require(strerror_r_nid_postfix(22, sentinel, 0) == 34 && sentinel[0] == 'x');
|
||||
Require(strerror_r_nid_postfix(22, nullptr, 0) == 34);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
extern "C" {
|
||||
int APS5_VABI remove_nid_postfix(const char*);
|
||||
int APS5_VABI rename_nid_postfix(const char*, const char*);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
}
|
||||
static void Check(bool value, int line) {
|
||||
if (!value) {
|
||||
std::fprintf(stderr, "Filesystem check failed at line %d (guest errno %d)\n", line, *__error_nid_postfix());
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
#define Require(value) Check((value), __LINE__)
|
||||
int main() {
|
||||
const auto root = std::filesystem::path("anyps5-filesystem-test-" +
|
||||
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()));
|
||||
Require(std::filesystem::create_directory(root));
|
||||
const auto file = root / "file.txt";
|
||||
{ std::ofstream stream(file); stream << "retained until removal"; }
|
||||
Require(remove_nid_postfix(root.string().c_str()) == -1);
|
||||
Require(*__error_nid_postfix() == 66);
|
||||
Require(std::filesystem::is_regular_file(file));
|
||||
Require(remove_nid_postfix((file / "invalid").string().c_str()) == -1);
|
||||
Require(remove_nid_postfix("") == -1 && *__error_nid_postfix() == 2);
|
||||
Require(remove_nid_postfix(nullptr) == -1 && *__error_nid_postfix() == 14);
|
||||
const auto renamed = root / "renamed.txt";
|
||||
{ std::ofstream stream(renamed); stream << "old contents"; }
|
||||
Require(rename_nid_postfix(file.string().c_str(), renamed.string().c_str()) == 0);
|
||||
Require(!std::filesystem::exists(file));
|
||||
{ std::ifstream stream(renamed); std::string contents; std::getline(stream, contents);
|
||||
Require(contents == "retained until removal"); }
|
||||
Require(rename_nid_postfix(renamed.string().c_str(), renamed.string().c_str()) == 0);
|
||||
Require(rename_nid_postfix(file.string().c_str(), renamed.string().c_str()) == -1);
|
||||
Require(*__error_nid_postfix() == 2);
|
||||
Require(rename_nid_postfix(renamed.string().c_str(), file.string().c_str()) == 0);
|
||||
Require(remove_nid_postfix(file.string().c_str()) == 0);
|
||||
Require(!std::filesystem::exists(file));
|
||||
Require(remove_nid_postfix(file.string().c_str()) == -1 && *__error_nid_postfix() == 2);
|
||||
Require(remove_nid_postfix(root.string().c_str()) == 0);
|
||||
Require(!std::filesystem::exists(root));
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
extern "C" {
|
||||
double APS5_VABI atof_nid_postfix(const char*);
|
||||
float APS5_VABI strtof_nid_postfix(const char*, char**);
|
||||
long double APS5_VABI strtold_nid_postfix(const char*, char**);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
float APS5_VABI fmodf_nid_postfix(float, float);
|
||||
float APS5_VABI asinf_nid_postfix(float);
|
||||
float APS5_VABI acosf_nid_postfix(float);
|
||||
float APS5_VABI atan2f_nid_postfix(float, float);
|
||||
float APS5_VABI tanf_nid_postfix(float);
|
||||
float APS5_VABI log10f_nid_postfix(float);
|
||||
double APS5_VABI exp2_nid_postfix(double);
|
||||
double APS5_VABI ldexp_nid_postfix(double, int);
|
||||
double APS5_VABI scalbn_nid_postfix(double, int);
|
||||
float APS5_VABI scalbnf_nid_postfix(float, int);
|
||||
double APS5_VABI frexp_nid_postfix(double, int*);
|
||||
float APS5_VABI frexpf_nid_postfix(float, int*);
|
||||
std::int64_t APS5_VABI lround_nid_postfix(double);
|
||||
std::int64_t APS5_VABI lroundf_nid_postfix(float);
|
||||
std::int64_t APS5_VABI llround_nid_postfix(double);
|
||||
int APS5_VABI __isfinitef_nid_postfix(float);
|
||||
int APS5_VABI __isnormal_nid_postfix(double);
|
||||
int APS5_VABI __isnormalf_nid_postfix(float);
|
||||
int APS5_VABI __isinff_nid_postfix(float);
|
||||
}
|
||||
static void Require(bool value) { if (!value) std::abort(); }
|
||||
int main() {
|
||||
Require(atof_nid_postfix(" -12.5tail") == -12.5);
|
||||
char* end = nullptr;
|
||||
const char input[] = "0x1.8p+2 remainder";
|
||||
Require(strtof_nid_postfix(input, &end) == 6.f && end == input + 8);
|
||||
const char invalid[] = "invalid";
|
||||
Require(strtof_nid_postfix(invalid, &end) == 0.f && end == invalid);
|
||||
*__error_nid_postfix() = 0;
|
||||
Require(std::isinf(strtof_nid_postfix("1e1000", nullptr)));
|
||||
Require(*__error_nid_postfix() == 34);
|
||||
Require(strtold_nid_postfix("1.0000000000000000001!", &end) > 1.L && *end == '!');
|
||||
Require(fmodf_nid_postfix(5.5f, 2.f) == 1.5f);
|
||||
Require(fmodf_nid_postfix(-5.5f, 2.f) == -1.5f);
|
||||
Require(std::signbit(fmodf_nid_postfix(-4.f, 2.f)));
|
||||
Require(std::isnan(fmodf_nid_postfix(1.f, 0.f)));
|
||||
Require(std::abs(asinf_nid_postfix(0.5f) - 0.5235988f) < 0.000001f);
|
||||
Require(std::abs(acosf_nid_postfix(0.5f) - 1.0471976f) < 0.000001f);
|
||||
Require(std::abs(atan2f_nid_postfix(1.f, -1.f) - 2.3561945f) < 0.000001f);
|
||||
Require(tanf_nid_postfix(0.f) == 0.f);
|
||||
Require(log10f_nid_postfix(100.f) == 2.f);
|
||||
Require(exp2_nid_postfix(-3.) == 0.125);
|
||||
Require(ldexp_nid_postfix(0.75, 4) == 12.);
|
||||
Require(scalbn_nid_postfix(0.75, -2) == 0.1875);
|
||||
Require(scalbnf_nid_postfix(0.75f, 4) == 12.f);
|
||||
int exponent = 0;
|
||||
Require(frexp_nid_postfix(12., &exponent) == 0.75 && exponent == 4);
|
||||
Require(frexpf_nid_postfix(-12.f, &exponent) == -0.75f && exponent == 4);
|
||||
Require(lround_nid_postfix(4294967296.5) == INT64_C(4294967297));
|
||||
Require(lround_nid_postfix(-2.5) == -3);
|
||||
Require(lroundf_nid_postfix(4294967296.f) == INT64_C(4294967296));
|
||||
Require(lroundf_nid_postfix(2.5f) == 3);
|
||||
Require(llround_nid_postfix(-4294967296.5) == -INT64_C(4294967297));
|
||||
const auto infinity = std::numeric_limits<float>::infinity();
|
||||
const auto nan = std::numeric_limits<float>::quiet_NaN();
|
||||
Require(__isinff_nid_postfix(infinity) == 1 && __isinff_nid_postfix(-infinity) == 1);
|
||||
Require(__isinff_nid_postfix(nan) == 0 && __isinff_nid_postfix(1.f) == 0);
|
||||
Require(__isfinitef_nid_postfix(0.f) == 1 && __isfinitef_nid_postfix(infinity) == 0);
|
||||
Require(__isfinitef_nid_postfix(nan) == 0);
|
||||
Require(__isnormalf_nid_postfix(1.f) == 1 && __isnormalf_nid_postfix(0.f) == 0);
|
||||
Require(__isnormalf_nid_postfix(std::numeric_limits<float>::denorm_min()) == 0);
|
||||
Require(__isnormal_nid_postfix(1.) == 1 && __isnormal_nid_postfix(0.) == 0);
|
||||
Require(__isnormal_nid_postfix(std::numeric_limits<double>::denorm_min()) == 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "prx/libc/include/GuestAllocations.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
|
||||
extern "C" {
|
||||
void* APS5_VABI mmap_nid_postfix(void*, std::size_t, int, int, int, std::int64_t) noexcept;
|
||||
int APS5_VABI munmap_nid_postfix(void*, std::size_t) noexcept;
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
}
|
||||
|
||||
static void Require(bool condition) {
|
||||
if (!condition) {
|
||||
std::fputs("Guest memory check failed\n", stderr);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
constexpr std::size_t page = 0x4000;
|
||||
const auto failed = reinterpret_cast<void*>(static_cast<std::uintptr_t>(-1));
|
||||
const auto reject = [&](std::size_t length, int protection, int flags, int fd,
|
||||
std::int64_t offset, int error) {
|
||||
*__error_nid_postfix() = 0;
|
||||
Require(mmap_nid_postfix(nullptr, length, protection, flags, fd, offset) == failed);
|
||||
Require(*__error_nid_postfix() == error);
|
||||
};
|
||||
reject(0, 3, 0x1002, -1, 0, 22);
|
||||
reject(std::numeric_limits<std::size_t>::max(), 3, 0x1002, -1, 0, 22);
|
||||
reject(page, 8, 0x1002, -1, 0, 22);
|
||||
reject(page, 3, 0x1002, 0, 0, 22);
|
||||
reject(page, 3, 0x1002, -1, 1, 22);
|
||||
reject(page, 3, 0x1001, -1, 0, 45); // shared
|
||||
reject(page, 3, 0x1012, -1, 0, 45); // fixed
|
||||
reject(page, 3, 0x2, 0, 0, 45); // file-backed
|
||||
reject(page, 3, 0x22, -1, 0, 45); // Linux MAP_ANON is not guest MAP_ANON
|
||||
|
||||
auto* memory = static_cast<unsigned char*>(mmap_nid_postfix(nullptr, page * 3 - 1, 3, 0x1002, -1, 0));
|
||||
Require(memory != failed && (reinterpret_cast<std::uintptr_t>(memory) & (page - 1)) == 0);
|
||||
for (std::size_t i = 0; i < page * 3; ++i) Require(memory[i] == 0);
|
||||
memory[0] = 42;
|
||||
memory[page * 2] = 73;
|
||||
{
|
||||
GuestAllocations::Mutation mutation;
|
||||
const auto range = mutation.Find(memory);
|
||||
Require(range.bytes == page * 3 && range.readable && range.writable);
|
||||
}
|
||||
Require(munmap_nid_postfix(memory + 1, page) == -1 && *__error_nid_postfix() == 22);
|
||||
Require(munmap_nid_postfix(memory, 0) == -1 && *__error_nid_postfix() == 22);
|
||||
Require(memory[0] == 42);
|
||||
Require(munmap_nid_postfix(memory + page, 1) == 0); // round to one guest page
|
||||
Require(memory[0] == 42 && memory[page * 2] == 73);
|
||||
Require(munmap_nid_postfix(memory, page) == 0);
|
||||
Require(memory[page * 2] == 73);
|
||||
Require(munmap_nid_postfix(memory + page * 2, page) == 0);
|
||||
Require(munmap_nid_postfix(memory, page) == -1);
|
||||
for (int protection : {0, 1, 3, 5}) {
|
||||
void* mapped = mmap_nid_postfix(memory, 1, protection, 0x1002, -1, 0);
|
||||
Require(mapped != failed);
|
||||
{
|
||||
GuestAllocations::Mutation mutation;
|
||||
const auto range = mutation.Find(mapped);
|
||||
Require(range.readable == ((protection & 3) != 0));
|
||||
Require(range.writable == ((protection & 2) != 0));
|
||||
}
|
||||
Require(munmap_nid_postfix(mapped, 1) == 0);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue