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
6 changes: 3 additions & 3 deletions core/libs/prx/libSceAmpr/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ void APS5_VABI _Cnd_destroy_nid_postfix(void** handle);
int APS5_VABI _Cnd_wait_nid_postfix(void** condition, void** mutex);
int APS5_VABI _Cnd_signal_nid_postfix(void** handle);
int APS5_VABI wcsrtombs_s_nid_postfix(std::size_t* result, char* destination, std::size_t capacity, const std::uint16_t** source, std::size_t limit, void* state);
[[noreturn]] void APS5_VABI __cxa_call_terminate_nid_postfix(void* exception);
int APS5_VABI _Cnd_init_nid_postfix(void** handle);
int SignalCondition_nid_no_patch(void** handle);
[[noreturn]] void ThrowCppError_nid_no_patch(int code);
Expand Down Expand Up @@ -994,9 +995,8 @@ int APS5_VABI wcstombs_s_nid_postfix(std::size_t* result, char* destination, std
return wcsrtombs_s_nid_postfix(result, destination, capacity, &source, limit, state.data());
}

int APS5_VABI __cxa_call_unexpected_nid_postfix(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
[[noreturn]] void APS5_VABI __cxa_call_unexpected_nid_postfix(void* unwindException) {
__cxa_call_terminate_nid_postfix(unwindException);
}

APS5_EXPORT("0hlfW1O4Aa4", sceAmprLocaleconv);
Expand Down
30 changes: 27 additions & 3 deletions core/libs/tests/GuestAmprThreads.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "SceTypes.hpp"
#include "prx/libc/include/exceptions/Runtime.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <array>
#include <atomic>
Expand Down Expand Up @@ -76,6 +77,9 @@ int APS5_VABI _Mtx_unlock_nid_postfix(void** handle);
GuestTerminate APS5_VABI _ZSt13set_terminatePFvvE_nid_postfix(GuestTerminate handler);
const GuestTypeInfo* APS5_VABI __cxa_current_exception_type_nid_postfix();
void* APS5_VABI __cxa_current_primary_exception_nid_postfix();
LibcException::Header* APS5_VABI __cxa_init_primary_exception_nid_postfix(void* object, std::type_info* type, void (*destructor)(void*));
[[noreturn]] void APS5_VABI __cxa_call_unexpected_nid_postfix(void* unwindException);
extern const unsigned char _ZTVN10__cxxabiv117__class_type_infoE_nid_postfix[];
}

static constexpr int ThrdSuccess = 0;
Expand Down Expand Up @@ -487,18 +491,35 @@ static void APS5_VABI ExitWithSystemErrorCode() {
}

template <typename TCall>
static int GuestSystemErrorCode(TCall call) {
static int TerminateExitCode(GuestTerminate handler, TCall call) {
const pid_t child = fork();
Require(child >= 0);
if (child == 0) {
_ZSt13set_terminatePFvvE_nid_postfix(ExitWithSystemErrorCode);
_ZSt13set_terminatePFvvE_nid_postfix(handler);
call();
_exit(2);
}
int status = 0;
Require(waitpid(child, &status, 0) == child);
return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
}

static const GuestTypeInfo UnexpectedType{_ZTVN10__cxxabiv117__class_type_infoE_nid_postfix + 16, "10Unexpected"};

static void APS5_VABI ExitWithCaughtType() {
const auto* type = __cxa_current_exception_type_nid_postfix();
_exit(type == &UnexpectedType ? 42 : type == nullptr ? 43 : 1);
}

static void TestCallUnexpected() {
Require(TerminateExitCode(ExitWithCaughtType, [] {
void* object = __cxa_allocate_exception_nid_postfix(sizeof(int));
auto* type = reinterpret_cast<std::type_info*>(const_cast<GuestTypeInfo*>(&UnexpectedType));
LibcException::Header* header = __cxa_init_primary_exception_nid_postfix(object, type, nullptr);
__cxa_call_unexpected_nid_postfix(&header->unwind);
}) == 42);
Require(TerminateExitCode(ExitWithCaughtType, [] { __cxa_call_unexpected_nid_postfix(nullptr); }) == 43);
}
#endif

static void TestPadErrors() {
Expand All @@ -515,7 +536,7 @@ static void TestPadErrors() {
Require(Throws<std::invalid_argument>([&] { _ZNSt4_Pad7_LaunchEPP7pthread_nid_postfix(&pad, nullptr); }));
Require(!pad.started && order.load() == 0);
#ifndef _WIN32
Require(GuestSystemErrorCode([&] { _ZNSt4_Pad8_ReleaseEv_nid_postfix(&pad); }) == GuestEbusy);
Require(TerminateExitCode(ExitWithSystemErrorCode, [&] { _ZNSt4_Pad8_ReleaseEv_nid_postfix(&pad); }) == GuestEbusy);
#endif
Require(!pad.started);

Expand All @@ -538,5 +559,8 @@ int main() {
TestSpinLock();
TestWcstombs();
TestConditionSignal();
#ifndef _WIN32
TestCallUnexpected();
#endif
return 0;
}
1 change: 1 addition & 0 deletions docs/dev/debt/libSceAmpr.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [AMPR command sizes](../../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - commands use the emulator's own encoding with an 8-byte header, so `Nop(n)` takes 8 + 4n bytes where the console record is n dwords including its header (`NopWithData(n)`: (n + 1) dwords). The measure functions agree with the appended sizes, but a title that pads to an exact offset or the end of the buffer with `Nop(remaining / 4)` gets `SCE_AMPR_ERROR_BUFFER_FULL` or a shifted offset. The at-start flag of the `_04_00` writes is ignored; commands run in order, each after the previous one completes
- [APR map begin and end](../../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - as in ampr_emu, `MapBegin`/`MapDirectBegin` map when they run and `MapEnd` only closes the region; while a region is open a second begin and every completion write return `EPERM`, the at-start `_04_00` writes are accepted
- [AMPR markers](../../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - signatures taken from the [ampr_emu](https://github.com/drakmor/ampr_emu) reimplementation, not confirmed on a title: `sceAmprCommandBufferSetMarkerWithColor` takes the color by pointer, the push and measure variants by value. Colors are not recorded
- [__cxa_call_unexpected](../../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - takes the in-flight exception's unwind header and never returns, as [libcxxabi 18.1.8](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_personality.cpp#L1171-L1211) does. No `std::set_unexpected` is exported and the exception header's unexpected handler is never set, so the handler is always the default one, which calls `std::terminate` ([cxa_default_handlers.cpp](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_default_handlers.cpp#L88-L99)); it therefore forwards to libc's `__cxa_call_terminate`, which begins the catch and runs the current terminate handler. That matches libcxxabi except for which terminate handler runs: there `std::terminate` after `__cxa_begin_catch` ([cxa_exception.cpp L470-L477](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_exception.cpp#L470-L477)) runs the handler saved in the caught exception's header ([cxa_handlers.cpp L72-L93](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_handlers.cpp#L72-L93)), which `__cxa_throw` and `__cxa_init_primary_exception` set to `std::get_terminate()` when the exception is thrown ([cxa_exception.cpp L209-L214](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_exception.cpp#L209-L214), [L279](https://github.com/llvm/llvm-project/blob/3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff/libcxxabi/src/cxa_exception.cpp#L279)), while here the current global handler runs, as AnyPS5's own `std::terminate` does. The two differ only if `std::set_terminate` was called between the throw and this call. A null header terminates because this emulator's libc `__cxa_begin_catch` terminates on null (libcxxabi does not define that case). Dynamic exception specifications that would let a replaced unexpected handler throw a permitted type are not modelled

### Functional

Expand Down
Loading