Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions core/libs/prx/libSceAjm.native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ if(BUILD_TESTING)
target_link_libraries(guest_ajm_tests PRIVATE libSceAjm.native libc)
configure_windows_unwind(guest_ajm_tests)
add_test(NAME guest_ajm COMMAND guest_ajm_tests)
if(UNIX)
add_test_executable(guest_ajm_teardown_tests ${LIBS_INCLUDE_DIR}/tests/GuestAjmTeardown.cpp)
target_include_directories(guest_ajm_teardown_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_ajm_teardown_tests PRIVATE libc ${CMAKE_DL_LIBS})
add_dependencies(guest_ajm_teardown_tests libSceAjm.native)
add_test(NAME guest_ajm_teardown COMMAND guest_ajm_teardown_tests $<TARGET_FILE:libSceAjm.native>)
endif()
if(WIN32)
set_tests_properties(guest_ajm PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAjm.native>")
Expand Down
8 changes: 4 additions & 4 deletions core/libs/prx/libSceAjm.native/src/Ajm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,13 @@ struct Instance {
}
};

std::mutex g_lock;
std::map<std::uint32_t, std::unique_ptr<Instance>> g_instances;
std::mutex& g_lock = *new std::mutex();
std::map<std::uint32_t, std::unique_ptr<Instance>>& g_instances = *new std::map<std::uint32_t, std::unique_ptr<Instance>>();
std::atomic<std::uint32_t> g_nextContext{1};
std::atomic<std::uint32_t> g_nextInstance{1};
std::atomic<std::uint32_t> g_nextBatch{1};
std::mutex g_batchLock;
std::set<std::uint32_t> g_batches;
std::mutex& g_batchLock = *new std::mutex();
std::set<std::uint32_t>& g_batches = *new std::set<std::uint32_t>();

enum class JobKind : std::uint32_t {
Initialize = 1,
Expand Down
14 changes: 7 additions & 7 deletions core/libs/prx/libkernel/Apr/src/Apr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ struct AprFile {
std::uint64_t size;
};

std::mutex g_filesLock;
std::vector<AprFile> g_files;
std::unordered_map<std::string, std::uint32_t> g_idsByPath;
std::mutex& g_filesLock = *new std::mutex();
std::vector<AprFile>& g_files = *new std::vector<AprFile>();
std::unordered_map<std::string, std::uint32_t>& g_idsByPath = *new std::unordered_map<std::string, std::uint32_t>();

// File sizes by directory, listed on first use. Titles resolve thousands of package paths at startup,
// and one size query per path took over 20 s; a listing costs one directory read. Names are compared
// case-insensitively, like the host file system.
std::mutex g_directoriesLock;
std::unordered_map<std::string, std::unordered_map<std::string, std::uint64_t>> g_directories;
std::mutex& g_directoriesLock = *new std::mutex();
std::unordered_map<std::string, std::unordered_map<std::string, std::uint64_t>>& g_directories = *new std::unordered_map<std::string, std::unordered_map<std::string, std::uint64_t>>();

std::string _foldCase(std::string text) {
for (auto& character : text) {
Expand Down Expand Up @@ -164,7 +164,7 @@ void _writeAddress(const Apr::WriteAddressCommand& command) {

std::array<std::atomic<std::uint32_t>, 256> g_counters{};

std::mutex g_counterWrites;
std::mutex& g_counterWrites = *new std::mutex();

std::uint32_t _counter(std::uint32_t index) {
return g_counters[index % g_counters.size()].load(std::memory_order_acquire);
Expand Down Expand Up @@ -263,7 +263,7 @@ struct AmmState {
};

AmmState& _amm() {
static AmmState state;
static auto& state = *new AmmState();
return state;
}

Expand Down
81 changes: 81 additions & 0 deletions core/libs/tests/GuestAjmTeardown.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <dlfcn.h>

namespace {

using Initialize = int (APS5_VABI*)(std::int64_t, std::uint32_t*);
using InstanceCreate = int (APS5_VABI*)(std::uint32_t, std::uint32_t, std::uint64_t, std::uint32_t*);
using InstanceDestroy = int (APS5_VABI*)(std::uint32_t, std::uint32_t);
using BatchInitialize = int (APS5_VABI*)(void*, std::size_t, AjmBatchInfo*);
using BatchJobClearContext = int (APS5_VABI*)(AjmBatchInfo*, std::uint32_t, void*);
using BatchStart = int (APS5_VABI*)(std::uint32_t, const AjmBatchInfo*, int, AjmBatchError*, std::uint32_t*);
using BatchWait = int (APS5_VABI*)(std::uint32_t, std::uint32_t, std::uint32_t, AjmBatchError*);

struct Functions {
Initialize initialize;
InstanceCreate instanceCreate;
InstanceDestroy instanceDestroy;
BatchInitialize batchInitialize;
BatchJobClearContext batchJobClearContext;
BatchStart batchStart;
BatchWait batchWait;
};

struct SidebandResult {
std::int32_t result;
std::int32_t internalResult;
};

Functions ajm{};
std::uint32_t context = 0;
std::uint32_t instance = 0;

[[noreturn]] void Fail(const char* message) {
std::fprintf(stderr, "%s\n", message);
std::fflush(stderr);
std::_Exit(1);
}

template <typename T>
T Resolve(void* library, const char* name) {
auto* symbol = dlsym(library, name);
if (symbol == nullptr) Fail(name);
return reinterpret_cast<T>(symbol);
}

void UseAfterStaticTeardown() {
alignas(16) std::uint8_t buffer[0x100]{};
AjmBatchInfo info{};
SidebandResult sideband{-1, -1};
if (ajm.batchInitialize(buffer, sizeof(buffer), &info) != 0 || ajm.batchJobClearContext(&info, instance, &sideband) != 0) Fail("a batch cannot be built after static teardown");
std::uint32_t batch = 0;
AjmBatchError error{};
if (ajm.batchStart(context, &info, 0, &error, &batch) != 0) Fail("sceAjmBatchStart failed after static teardown");
if (sideband.result != 0) Fail("an instance created before exit is unknown after static teardown");
if (ajm.batchWait(context, batch, 0, &error) != 0) Fail("sceAjmBatchWait does not find a batch started after static teardown");
if (ajm.instanceDestroy(context, instance) != 0) Fail("sceAjmInstanceDestroy does not find the instance after static teardown");
}

}

int main(int argc, char** argv) {
if (argc != 2) Fail("usage: guest_ajm_teardown_tests <libSceAjm.native>");
if (std::atexit(UseAfterStaticTeardown) != 0) Fail("atexit failed");
auto* library = dlopen(argv[1], RTLD_NOW);
if (library == nullptr) Fail(dlerror());
ajm = {
Resolve<Initialize>(library, "sceAjmInitialize"),
Resolve<InstanceCreate>(library, "sceAjmInstanceCreate"),
Resolve<InstanceDestroy>(library, "sceAjmInstanceDestroy"),
Resolve<BatchInitialize>(library, "sceAjmBatchInitialize"),
Resolve<BatchJobClearContext>(library, "sceAjmBatchJobClearContext"),
Resolve<BatchStart>(library, "sceAjmBatchStart"),
Resolve<BatchWait>(library, "sceAjmBatchWait"),
};
if (ajm.initialize(0, &context) != 0 || ajm.instanceCreate(context, 0, 0, &instance) != 0) Fail("AJM setup failed");
return 0;
}
1 change: 1 addition & 0 deletions docs/dev/TechnicalDebt.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,4 @@ Throughout the project, every function at every stage either **does exactly what
- [sceVideodec2CreateDecoder](../../core/libs/prx/libSceVideodec2/Export.cpp) (libSceVideodec2) decodes HEVC (codec type 974921) and VP9 (2382845) through FFmpeg, besides H.264. The tags come from SharpProspero's `Videodec2CodecType` and EVO-PLAYER-PS5's `sce_videodec2.h`, which records them from hardware runs. HEVC input is converted from length-prefixed NAL units to Annex B as for H.264, and VP9 frames are passed through. Output is the same 8-bit NV12 for every codec; a decoder for HEVC Main10 or VP9 Profile 2 is created, but a frame deeper than 8 bits throws, since the console's 10-bit output layout is unknown, and `QueryDecoderMemoryInfo` reports the 8-bit frame size for it. `sceVideodec2GetPictureInfo` and `sceVideodec2GetAvcPictureInfo` throw for HEVC and VP9 pictures, which use other layouts (`SceVideodec2HevcPictureInfo`, `SceVideodec2Vp9PictureInfo`), and `sceVideodec2GetHevcPictureInfo` and `sceVideodec2GetVp9PictureInfo` are not exported
- [syslog](../../core/libs/prx/libc/src/Syslog.cpp) (libc) writes each message to `stderr` as `[syslog:<priority>] <message>`, with a newline added when missing, instead of sending it to the console's system log. Priority and facility handling follow FreeBSD's `vsyslog`: `%m` expands to the guest errno's message, unknown bits are reported (as priority 0x23, since FreeBSD passes `LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID` as the priority) and masked, a missing facility becomes `LOG_USER`, and errno is preserved. `openlog`, `setlogmask` and `closelog` are not implemented, so the identity, options and mask they set are not applied
- [sigaction](../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) records the handler, flags and mask of every signal from 1 to 128 and reports them back, following FreeBSD's `sigaction` (`EINVAL` for other signals, and for a handler on `SIGKILL` or `SIGSTOP`); `signal` records its handler with `SA_RESTART`, as FreeBSD's libc does. Only handlers without `SA_SIGINFO` on the signals `signal` already delivers (`SIGINT`, `SIGILL`, `SIGABRT`, `SIGFPE`, `SIGSEGV`, `SIGTERM`) run, the same way `signal` handlers do; their mask and flags are not applied. `SA_SIGINFO` handlers and other signals (`SIGBUS`, `SIGHUP`, ...) are recorded but never called, since that needs FreeBSD's `siginfo_t` and `ucontext_t`; `raise` of an `SA_SIGINFO` handler on a signal `signal` delivers throws instead of terminating, and `raise` of the other signals still returns `EINVAL`
- [AJM](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp), [APR](../../core/libs/prx/libkernel/Apr/src/Apr.cpp) and [NGS2](../../core/libs/prx/libSceNgs2.native/src/System.cpp) state is allocated with `new` and never freed on purpose: title threads still call into these libraries while `std::exit` runs static destructors, so ordinary statics would be destroyed under them. Keep them leaked; `guest_ajm_teardown` covers the AJM case on Unix.
Loading