Files
AnyPS5/core/libs/tests/ExceptionPersonality.cpp
T
mugurc 0ae0bbf244 test(libc): reach the C personality through its guest export
Guest C built with -fexceptions binds __gcc_personality_v0 to
__gcc_personality_v0_nid_postfix, and the unwinder recognizes the frame
by that address. exception_c_cleanup links the host personality, so it
passed without the export.

The new test builds a call-site table by hand and drives the routine the
way the unwinder does: through the _nid_postfix symbol and through
CallPersonality. It covers the cleanup phase installing the landing pad,
the search phase continuing, ranges before and after the call site, a
call site with no landing pad, no LSDA, a signal frame, an unsupported
version, and an unknown personality. Without the export it fails to link.
2026-10-08 13:16:41 +03:00

93 lines
3.3 KiB
C++

#include "prx/libc/include/exceptions/Unwind.hpp"
#include <cstdio>
#include <cstdlib>
namespace LibcUnwind {
_Unwind_Reason_Code CallPersonality(Word personality, _Unwind_Action actions, _Unwind_Exception* exception, _Unwind_Context* context);
}
static void Require(bool value) { if (!value) std::abort(); }
static const unsigned char cleanupTable[] = {0xff, 0xff, 0x01, 0x04, 0x10, 0x20, 0x40, 0x00};
static const unsigned char noLandingTable[] = {0xff, 0xff, 0x01, 0x04, 0x10, 0x20, 0x00, 0x00};
constexpr std::uintptr_t region = 0x10000;
struct Frame {
_Unwind_Context context {};
_Unwind_Exception exception {};
Frame(const unsigned char* table, std::uintptr_t resume, bool signalFrame = false) {
context.region = region;
context.lsda = reinterpret_cast<std::uintptr_t>(table);
context.registers[16] = region + resume;
context.signalFrame = signalFrame;
}
};
static _Unwind_Reason_Code Direct(Frame& frame, _Unwind_Action actions, int version = 1) {
return __gcc_personality_v0_nid_postfix(version, actions, 0, &frame.exception, &frame.context);
}
static _Unwind_Reason_Code Routed(Frame& frame, _Unwind_Action actions) {
return LibcUnwind::CallPersonality(reinterpret_cast<std::uintptr_t>(__gcc_personality_v0_nid_postfix), actions, &frame.exception, &frame.context);
}
static void RequireInstalled(Frame& frame) {
Require(frame.context.registers[0] == reinterpret_cast<std::uintptr_t>(&frame.exception));
Require(frame.context.registers[1] == 0);
Require(frame.context.registers[16] == region + 0x40);
}
int main() {
{
Frame frame(cleanupTable, 0x18);
Require(Direct(frame, _UA_CLEANUP_PHASE) == _URC_INSTALL_CONTEXT);
RequireInstalled(frame);
}
{
Frame frame(cleanupTable, 0x18);
Require(Routed(frame, _UA_CLEANUP_PHASE) == _URC_INSTALL_CONTEXT);
RequireInstalled(frame);
}
{
Frame frame(cleanupTable, 0x18);
Require(Direct(frame, _UA_SEARCH_PHASE) == _URC_CONTINUE_UNWIND);
Require(frame.context.registers[16] == region + 0x18);
}
{
Frame frame(cleanupTable, 0x18);
Require(Routed(frame, _UA_SEARCH_PHASE) == _URC_CONTINUE_UNWIND);
}
{
Frame before(cleanupTable, 0x10);
Require(Direct(before, _UA_CLEANUP_PHASE) == _URC_CONTINUE_UNWIND);
Frame after(cleanupTable, 0x31);
Require(Direct(after, _UA_CLEANUP_PHASE) == _URC_CONTINUE_UNWIND);
}
{
Frame frame(cleanupTable, 0x10, true);
Require(Direct(frame, _UA_CLEANUP_PHASE) == _URC_INSTALL_CONTEXT);
RequireInstalled(frame);
}
{
Frame frame(noLandingTable, 0x18);
Require(Direct(frame, _UA_CLEANUP_PHASE) == _URC_CONTINUE_UNWIND);
}
{
Frame frame(nullptr, 0x18);
Require(Direct(frame, _UA_CLEANUP_PHASE) == _URC_CONTINUE_UNWIND);
}
{
Frame frame(cleanupTable, 0x18);
Require(Direct(frame, _UA_CLEANUP_PHASE, 2) == _URC_FATAL_PHASE1_ERROR);
}
{
Frame frame(cleanupTable, 0x18);
Require(LibcUnwind::CallPersonality(0x1000, _UA_SEARCH_PHASE, &frame.exception, &frame.context) == _URC_FATAL_PHASE1_ERROR);
Require(LibcUnwind::CallPersonality(0x1000, _UA_CLEANUP_PHASE, &frame.exception, &frame.context) == _URC_FATAL_PHASE2_ERROR);
}
std::puts("exception personality tests passed");
return 0;
}