Merge pull request #11241 from edubox/feat/libc-future-category

feat(libc): export the error_category members and std::future_error
This commit is contained in:
Dean authored and GitHub committed 2026-10-27 11:04:49 +13:00
commit b8265bf2bc
5 files changed
+330 -3

No files matched your search

+10
View File
@@ -873,6 +873,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_generic_category PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(guest_future_category_tests ${LIBS_INCLUDE_DIR}/tests/GuestFutureCategory.cpp)
target_include_directories(guest_future_category_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_future_category_tests PRIVATE libc)
configure_windows_unwind(guest_future_category_tests)
add_test(NAME guest_future_category COMMAND guest_future_category_tests)
set_tests_properties(guest_future_category PROPERTIES TIMEOUT 10)
if(WIN32)
set_tests_properties(guest_future_category PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME guest_new_handler COMMAND guest_new_handler_tests)
if(WIN32)
set_tests_properties(guest_new_handler PROPERTIES ENVIRONMENT_MODIFICATION
+10
View File
@@ -98,6 +98,8 @@ struct ErrorCode { int reserved; int value; const CategoryObject* category; };
struct SystemErrorObject { ExceptionObject base; ErrorCode code; };
struct FutureErrorObject { const void* vtable; GuestLocale::String message; ErrorCode code; };
}
extern "C" {
@@ -1179,6 +1181,11 @@ const char* APS5_VABI _ZSt13_Syserror_mapi_nid_postfix(int value);
const LibcException::CategoryObject* APS5_VABI _ZSt16generic_categoryv_nid_postfix();
const LibcException::CategoryObject* APS5_VABI _ZSt15system_categoryv_nid_postfix();
GuestLocale::String* APS5_VABI _ZNKSt23_Generic_error_category7messageEi_nid_postfix( GuestLocale::String* result, const LibcException::CategoryObject* self, int value);
const LibcException::CategoryObject* APS5_VABI _ZSt15future_categoryv_nid_postfix();
LibcException::ErrorCode APS5_VABI _ZNKSt14error_category23default_error_conditionEi_nid_postfix(const LibcException::CategoryObject* self, int value);
bool APS5_VABI _ZNKSt14error_category10equivalentEiRKSt15error_condition_nid_postfix(const LibcException::CategoryObject* self, int value, const LibcException::ErrorCode* condition);
bool APS5_VABI _ZNKSt14error_category10equivalentERKSt10error_codei_nid_postfix(const LibcException::CategoryObject* self, const LibcException::ErrorCode* code, int value);
void APS5_VABI _ZNSt14error_categoryD2Ev_nid_postfix(LibcException::CategoryObject* self);
void APS5_VABI _ZNSt9exceptionD1Ev_nid_postfix(LibcException::ExceptionObject* self);
void APS5_VABI _ZNSt9exceptionD2Ev_nid_postfix(LibcException::ExceptionObject* self);
void APS5_VABI _ZNSt9exceptionD0Ev_nid_postfix(LibcException::ExceptionObject* self);
@@ -1280,6 +1287,7 @@ void APS5_VABI _ZNSt12length_errorC1ERKS__nid_postfix(LibcException::ExceptionOb
void APS5_VABI _ZNSt12length_errorC2ERKS__nid_postfix(LibcException::ExceptionObject* self, const LibcException::ExceptionObject* source);
LibcException::ExceptionObject* APS5_VABI _ZNSt12length_erroraSERKS__nid_postfix(LibcException::ExceptionObject* self, const LibcException::ExceptionObject* source);
void APS5_VABI _ZNSt12system_errorD1Ev_nid_postfix(LibcException::ExceptionObject* self);
void APS5_VABI _ZNSt12future_errorD1Ev_nid_postfix(LibcException::FutureErrorObject* self);
void APS5_VABI _ZNSt12system_errorD2Ev_nid_postfix(LibcException::ExceptionObject* self);
void APS5_VABI _ZNSt12system_errorD0Ev_nid_postfix(LibcException::ExceptionObject* self);
const char* APS5_VABI _ZNKSt12system_error4whatEv_nid_postfix(const LibcException::ExceptionObject* self);
@@ -1325,6 +1333,8 @@ int APS5_VABI dl_iterate_phdr_nid_postfix(int (*callback)(dl_phdr_info*, std::si
[[noreturn]] void APS5_VABI _ZSt16_Throw_Cpp_errori_nid_postfix(int code);
[[noreturn]] void APS5_VABI _Thrd_exit_nid_postfix(int code);
[[noreturn]] void APS5_VABI _ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE_nid_postfix(GuestExceptionPointerArgument exception);
[[noreturn]] void APS5_VABI _ZSt19_Throw_future_errorRKSt10error_code_nid_postfix(const LibcException::ErrorCode* code);
[[noreturn]] void APS5_VABI _ZSt25_Rethrow_future_exceptionSt13exception_ptr_nid_postfix(GuestExceptionPointerArgument exception);
[[noreturn]] void APS5_VABI _ZSt9terminatev_nid_postfix();
[[noreturn]] void APS5_VABI __cxa_call_terminate_nid_postfix(void* exception);
[[noreturn]] void APS5_VABI __cxa_pure_virtual_nid_postfix();
@@ -11,6 +11,8 @@
namespace LibcException {
struct TypeRecord { const void* const* vtable; const char* name; const TypeRecord* base; };
struct ClassTypeRecord { const void* const* vtable; const char* name; };
struct ExceptionVtable {
std::ptrdiff_t offset;
const TypeRecord* type;
@@ -18,6 +20,17 @@ struct ExceptionVtable {
void (APS5_VABI *deleteObject)(ExceptionObject*);
const char* (APS5_VABI *what)(const ExceptionObject*);
};
struct FutureErrorVtable {
std::ptrdiff_t offset;
const TypeRecord* type;
void (APS5_VABI *destroy)(FutureErrorObject*);
void (APS5_VABI *deleteObject)(FutureErrorObject*);
const char* (APS5_VABI *what)(const FutureErrorObject*);
void (APS5_VABI *doraise)(const FutureErrorObject*);
};
static_assert(sizeof(ClassTypeRecord) == 0x10);
static_assert(sizeof(FutureErrorVtable) == 0x30);
struct Message {
std::size_t length;
std::size_t capacity;
@@ -129,6 +142,7 @@ ExceptionObject* Assign(ExceptionObject* object, const ExceptionObject* source)
static_assert(sizeof(ErrorCode) == 16 && offsetof(ErrorCode, value) == 4 && offsetof(ErrorCode, category) == 8);
static_assert(sizeof(SystemErrorObject) == 32 && offsetof(SystemErrorObject, code) == 16);
static_assert(sizeof(FutureErrorObject) == 0x40 && offsetof(FutureErrorObject, message) == 0x8 && offsetof(FutureErrorObject, code) == 0x30);
struct CppError { int value; const char* message; };
static const char* GenericErrorMessage(int value) {
@@ -220,10 +234,8 @@ static void APS5_VABI CategoryDelete_nid_no_patch(CategoryObject*) {
}
static const char* APS5_VABI GenericCategoryName_nid_no_patch(const CategoryObject*) { return "generic"; }
static const char* APS5_VABI SystemCategoryName_nid_no_patch(const CategoryObject*) { return "system"; }
static GuestLocale::String* APS5_VABI CategoryMessage_nid_no_patch(GuestLocale::String* result, const CategoryObject*, int value) {
static GuestLocale::String* FillMessage(GuestLocale::String* result, const char* message) {
if (!result) throw std::invalid_argument("std::error_category::message: null result");
const char* message = GenericErrorMessage(value);
if (!message) message = "unknown error";
const auto length = std::strlen(message);
const auto capacity = length | std::size_t{15};
char* storage = length <= 15 ? result->buffer : static_cast<char*>(_Znwm_nid_postfix(capacity + 1));
@@ -234,6 +246,23 @@ static GuestLocale::String* APS5_VABI CategoryMessage_nid_no_patch(GuestLocale::
result->capacity = capacity;
return result;
}
static const char* FutureErrorMessage(int value) {
static constexpr const char* messages[] {"broken promise", "future already retrieved", "promise already satisfied", "no state"};
if (value < 1 || value > 4) return nullptr;
return messages[value - 1];
}
static GuestLocale::String* APS5_VABI CategoryMessage_nid_no_patch(GuestLocale::String* result, const CategoryObject*, int value) {
const char* message = GenericErrorMessage(value);
if (!message) message = "unknown error";
return FillMessage(result, message);
}
static GuestLocale::String* APS5_VABI FutureCategoryMessage_nid_no_patch(GuestLocale::String* result, const CategoryObject*, int value) {
const char* message = FutureErrorMessage(value);
if (!message) message = GenericErrorMessage(value);
if (!message) message = "unknown error";
return FillMessage(result, message);
}
static const char* APS5_VABI FutureCategoryName_nid_no_patch(const CategoryObject*) { return "future"; }
static ErrorCode APS5_VABI CategoryCondition_nid_no_patch(const CategoryObject* self, int value) { return {0, value, self}; }
static bool APS5_VABI CategoryEquivalentCondition_nid_no_patch(const CategoryObject* self, int value, const ErrorCode* condition) {
using Condition = ErrorCode (APS5_VABI *)(const CategoryObject*, int);
@@ -637,4 +666,89 @@ const char* APS5_VABI _ZNSt8__sce_v213_Syserror_mapEi_nid_postfix(int value) {
const char* message = LibcException::GenericErrorMessage(value);
return message ? message : "unknown error";
}
LibcException::ClassTypeRecord _ZTISt14error_category_nid_postfix {LibcException::ClassTypeVtable + 2, "St14error_category"};
LibcException::TypeRecord _ZTISt22_Future_error_category_nid_postfix {LibcException::SingleTypeVtable + 2, "St22_Future_error_category", &LibcException::GenericCategoryType};
static void APS5_VABI FutureCategoryDelete_nid_no_patch(LibcException::CategoryObject*) {
throw std::invalid_argument("std::error_category: the future category object is not heap allocated");
}
extern const void* const _ZTVSt22_Future_error_category_nid_postfix[] {
nullptr, &_ZTISt22_Future_error_category_nid_postfix,
reinterpret_cast<const void*>(LibcException::CategoryDestroy_nid_no_patch), reinterpret_cast<const void*>(FutureCategoryDelete_nid_no_patch),
reinterpret_cast<const void*>(LibcException::FutureCategoryName_nid_no_patch), reinterpret_cast<const void*>(LibcException::FutureCategoryMessage_nid_no_patch),
reinterpret_cast<const void*>(LibcException::CategoryCondition_nid_no_patch),
reinterpret_cast<const void*>(LibcException::CategoryEquivalentCondition_nid_no_patch),
reinterpret_cast<const void*>(LibcException::CategoryEquivalentCode_nid_no_patch)
};
static const LibcException::CategoryObject FutureCategory {_ZTVSt22_Future_error_category_nid_postfix + 2};
const LibcException::CategoryObject* APS5_VABI _ZSt15future_categoryv_nid_postfix() {
return &FutureCategory;
}
LibcException::ErrorCode APS5_VABI _ZNKSt14error_category23default_error_conditionEi_nid_postfix(const LibcException::CategoryObject* self, int value) {
return LibcException::CategoryCondition_nid_no_patch(self, value);
}
bool APS5_VABI _ZNKSt14error_category10equivalentEiRKSt15error_condition_nid_postfix(const LibcException::CategoryObject* self, int value, const LibcException::ErrorCode* condition) {
return LibcException::CategoryEquivalentCondition_nid_no_patch(self, value, condition);
}
bool APS5_VABI _ZNKSt14error_category10equivalentERKSt10error_codei_nid_postfix(const LibcException::CategoryObject* self, const LibcException::ErrorCode* code, int value) {
return LibcException::CategoryEquivalentCode_nid_no_patch(self, code, value);
}
void APS5_VABI _ZNSt14error_categoryD2Ev_nid_postfix(LibcException::CategoryObject*) {}
LibcException::TypeRecord _ZTISt12future_error_nid_postfix {LibcException::SingleTypeVtable + 2, "St12future_error", &_ZTISt11logic_error_nid_postfix};
static void DestroyFutureErrorString(GuestLocale::String* message) {
if (message->capacity > 15) _ZdlPv_nid_postfix(message->pointer);
message->reserved = 0;
message->size = 0;
message->capacity = 15;
std::memset(message->buffer, 0, sizeof(message->buffer));
}
static void APS5_VABI DestroyFutureError_nid_no_patch(LibcException::FutureErrorObject* self) {
DestroyFutureErrorString(&self->message);
}
static void APS5_VABI DeleteFutureError_nid_no_patch(LibcException::FutureErrorObject* self) {
DestroyFutureErrorString(&self->message);
std::free(self);
}
static const char* APS5_VABI FutureErrorWhat_nid_no_patch(const LibcException::FutureErrorObject* self) {
return LibcException::FutureErrorMessage(self->code.value);
}
static void CopyFutureError(LibcException::FutureErrorObject* self, const LibcException::FutureErrorObject* source) {
*self = *source;
if (source->message.capacity > 15) {
const auto capacity = source->message.capacity;
auto* storage = static_cast<char*>(_Znwm_nid_postfix(capacity + 1));
std::memcpy(storage, source->message.pointer, source->message.size + 1);
self->message.pointer = storage;
}
}
[[noreturn]] static void APS5_VABI RaiseFutureError_nid_no_patch(const LibcException::FutureErrorObject* self) {
auto* object = static_cast<LibcException::FutureErrorObject*>(__cxa_allocate_exception_nid_postfix(sizeof(LibcException::FutureErrorObject)));
try {
CopyFutureError(object, self);
} catch (...) {
__cxa_free_exception_nid_postfix(object);
throw;
}
__cxa_throw_nid_postfix(object, reinterpret_cast<std::type_info*>(&_ZTISt12future_error_nid_postfix),
reinterpret_cast<decltype(LibcException::Header::destructor)>(DestroyFutureError_nid_no_patch));
}
LibcException::FutureErrorVtable _ZTVSt12future_error_nid_postfix {
0, &_ZTISt12future_error_nid_postfix,
DestroyFutureError_nid_no_patch, DeleteFutureError_nid_no_patch, FutureErrorWhat_nid_no_patch, RaiseFutureError_nid_no_patch
};
[[noreturn]] void APS5_VABI _ZSt19_Throw_future_errorRKSt10error_code_nid_postfix(const LibcException::ErrorCode* code) {
auto* object = static_cast<LibcException::FutureErrorObject*>(__cxa_allocate_exception_nid_postfix(sizeof(LibcException::FutureErrorObject)));
object->vtable = &_ZTVSt12future_error_nid_postfix.destroy;
object->message = {};
object->message.capacity = 15;
object->code = *code;
__cxa_throw_nid_postfix(object, reinterpret_cast<std::type_info*>(&_ZTISt12future_error_nid_postfix),
reinterpret_cast<decltype(LibcException::Header::destructor)>(DestroyFutureError_nid_no_patch));
}
[[noreturn]] void APS5_VABI _ZSt25_Rethrow_future_exceptionSt13exception_ptr_nid_postfix(GuestExceptionPointerArgument exception) {
_ZSt17rethrow_exceptionNSt15__exception_ptr13exception_ptrE_nid_postfix(exception);
}
void APS5_VABI _ZNSt12future_errorD1Ev_nid_postfix(LibcException::FutureErrorObject* self) {
DestroyFutureErrorString(&self->message);
}
}
+192
View File
@@ -0,0 +1,192 @@
#include "prx/libc/include/Api.hpp"
#include "prx/libc/include/ApplicationHeap.hpp"
#include "prx/libc/include/GuestLocale.hpp"
#include <array>
#include <bit>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <new>
#include <stdexcept>
using Message = GuestLocale::String* (APS5_VABI *)(GuestLocale::String*, const LibcException::CategoryObject*, int);
using Name = const char* (APS5_VABI *)(const LibcException::CategoryObject*);
using Raise = void (APS5_VABI *)(const LibcException::FutureErrorObject*);
bool failAllocation = false;
void* APS5_VABI TestAllocate(std::size_t size) { return failAllocation ? nullptr : std::malloc(size); }
void APS5_VABI TestFree(void* pointer) { std::free(pointer); }
void* APS5_VABI TestCalloc(std::size_t count, std::size_t size) { return std::calloc(count, size); }
void* APS5_VABI TestReallocate(void* pointer, std::size_t size) { return std::realloc(pointer, size); }
void* APS5_VABI TestAlign(std::size_t, std::size_t size) { return std::malloc(size); }
void* APS5_VABI TestRealign(void* pointer, std::size_t size, std::size_t) { return std::realloc(pointer, size); }
int APS5_VABI TestPosixAlign(void** pointer, std::size_t, std::size_t size) {
*pointer = std::malloc(size);
return *pointer ? 0 : 12;
}
struct TypeRecord { const void* vtable; const char* name; const TypeRecord* base; };
struct ClassTypeRecord { const void* vtable; const char* name; };
extern "C" {
extern TypeRecord _ZTISt12future_error_nid_postfix;
extern TypeRecord _ZTISt22_Future_error_category_nid_postfix;
extern ClassTypeRecord _ZTISt14error_category_nid_postfix;
extern std::uintptr_t _ZTVSt12future_error_nid_postfix[6];
}
static void Require(bool value, int line) {
if (value) return;
std::fprintf(stderr, "future category check failed at line %d\n", line);
std::exit(1);
}
#define REQUIRE(value) Require((value), __LINE__)
static void CheckMessage(Message message, const LibcException::CategoryObject* category, int value, const char* expected) {
constexpr std::uint64_t marker = 0xA55AA55AA55AA55Aull;
struct Guarded { std::uint64_t before; GuestLocale::String result; std::uint64_t after; };
Guarded guarded {marker, {}, marker};
std::memset(&guarded.result, 0xcd, sizeof(guarded.result));
*__error_nid_postfix() = 61;
REQUIRE(message(&guarded.result, category, value) == &guarded.result);
REQUIRE(*__error_nid_postfix() == 61);
REQUIRE(guarded.before == marker && guarded.after == marker);
const auto length = std::strlen(expected);
REQUIRE(guarded.result.reserved == 0 && guarded.result.size == length);
REQUIRE(guarded.result.capacity >= length && (guarded.result.capacity & 15) == 15);
const char* text = guarded.result.capacity <= 15 ? guarded.result.buffer : guarded.result.pointer;
REQUIRE(std::memcmp(text, expected, length + 1) == 0);
REQUIRE((guarded.result.capacity <= 15) == (length <= 15));
if (length > 15) _ZdlPv_nid_postfix(guarded.result.pointer);
}
static void CheckBaseCategory() {
REQUIRE(sizeof(ClassTypeRecord) == 0x10);
REQUIRE(std::strcmp(_ZTISt14error_category_nid_postfix.name, "St14error_category") == 0);
const auto* future = _ZSt15future_categoryv_nid_postfix();
const auto* generic = _ZSt16generic_categoryv_nid_postfix();
REQUIRE(future != nullptr && generic != nullptr && future != generic);
const LibcException::ErrorCode condition {0x7fffffff, 22, future};
REQUIRE(_ZNKSt14error_category10equivalentEiRKSt15error_condition_nid_postfix(future, 22, &condition));
REQUIRE(!_ZNKSt14error_category10equivalentEiRKSt15error_condition_nid_postfix(future, 23, &condition));
const LibcException::ErrorCode otherCondition {0, 22, generic};
REQUIRE(!_ZNKSt14error_category10equivalentEiRKSt15error_condition_nid_postfix(future, 22, &otherCondition));
const LibcException::ErrorCode code {0x12345678, 22, future};
REQUIRE(_ZNKSt14error_category10equivalentERKSt10error_codei_nid_postfix(future, &code, 22));
REQUIRE(!_ZNKSt14error_category10equivalentERKSt10error_codei_nid_postfix(future, &code, 23));
const LibcException::ErrorCode otherCode {0, 22, generic};
REQUIRE(!_ZNKSt14error_category10equivalentERKSt10error_codei_nid_postfix(future, &otherCode, 22));
const auto mapped = _ZNKSt14error_category23default_error_conditionEi_nid_postfix(future, 7);
REQUIRE(mapped.reserved == 0 && mapped.value == 7 && mapped.category == future);
_ZNSt14error_categoryD2Ev_nid_postfix(nullptr);
}
static void CheckFutureCategory() {
const auto* future = _ZSt15future_categoryv_nid_postfix();
const auto* type = static_cast<const TypeRecord*>(static_cast<const void* const*>(future->vtable)[-1]);
REQUIRE(std::strcmp(type->name, "St22_Future_error_category") == 0);
REQUIRE(type->base && std::strcmp(type->base->name, "St23_Generic_error_category") == 0);
REQUIRE(std::strcmp(_ZTISt22_Future_error_category_nid_postfix.name, "St22_Future_error_category") == 0);
REQUIRE(_ZTISt22_Future_error_category_nid_postfix.base && std::strcmp(_ZTISt22_Future_error_category_nid_postfix.base->name, "St23_Generic_error_category") == 0);
const auto name = std::bit_cast<Name>(static_cast<const void* const*>(future->vtable)[2]);
REQUIRE(std::strcmp(name(future), "future") == 0);
const auto message = std::bit_cast<Message>(static_cast<const void* const*>(future->vtable)[3]);
struct Expected { int value; const char* text; };
static constexpr Expected errors[] {
{1, "broken promise"}, {2, "future already retrieved"}, {3, "promise already satisfied"}, {4, "no state"}
};
for (const auto& error : errors) CheckMessage(message, future, error.value, error.text);
for (int value : {0, 5, 22, 99}) {
const char* generic = _ZSt13_Syserror_mapi_nid_postfix(value);
CheckMessage(message, future, value, generic ? generic : "unknown error");
}
GuestLocale::String first {}, second {};
REQUIRE(message(&first, future, 2) == &first && message(&second, future, 3) == &second);
REQUIRE(first.pointer != second.pointer);
_ZdlPv_nid_postfix(second.pointer);
REQUIRE(std::strcmp(first.pointer, "future already retrieved") == 0);
_ZdlPv_nid_postfix(first.pointer);
bool rejected = false;
try { message(nullptr, future, 2); }
catch (const std::invalid_argument&) { rejected = true; }
REQUIRE(rejected);
}
static void CheckFutureError() {
REQUIRE(sizeof(LibcException::FutureErrorObject) == 0x40);
REQUIRE(offsetof(LibcException::FutureErrorObject, message) == 0x8);
REQUIRE(offsetof(LibcException::FutureErrorObject, code) == 0x30);
REQUIRE(std::strcmp(_ZTISt12future_error_nid_postfix.name, "St12future_error") == 0);
REQUIRE(_ZTISt12future_error_nid_postfix.base && std::strcmp(_ZTISt12future_error_nid_postfix.base->name, "St11logic_error") == 0);
REQUIRE(_ZTVSt12future_error_nid_postfix[0] == 0);
REQUIRE(_ZTVSt12future_error_nid_postfix[1] == reinterpret_cast<std::uintptr_t>(&_ZTISt12future_error_nid_postfix));
for (int slot = 2; slot < 6; ++slot) REQUIRE(_ZTVSt12future_error_nid_postfix[slot] != 0);
const auto* future = _ZSt15future_categoryv_nid_postfix();
LibcException::ErrorCode code {0x12345678, 2, future};
bool caught = false;
try { _ZSt19_Throw_future_errorRKSt10error_code_nid_postfix(&code); }
catch (const std::exception& error) {
caught = true;
REQUIRE(error.what() != nullptr && std::strcmp(error.what(), "future already retrieved") == 0);
}
REQUIRE(caught);
code.value = 99;
caught = false;
try { _ZSt19_Throw_future_errorRKSt10error_code_nid_postfix(&code); }
catch (const std::exception& error) {
caught = true;
REQUIRE(error.what() == nullptr);
}
REQUIRE(caught);
const auto raise = std::bit_cast<Raise>(_ZTVSt12future_error_nid_postfix[5]);
LibcException::FutureErrorObject source {};
source.message.capacity = 24;
source.message.size = 24;
source.message.pointer = static_cast<char*>(_Znwm_nid_postfix(25));
std::memcpy(source.message.pointer, "future already retrieved", 25);
source.code = {0x12345678, 2, future};
failAllocation = true;
bool allocationFailure = false;
try { raise(&source); }
catch (const std::bad_alloc&) { allocationFailure = true; }
failAllocation = false;
REQUIRE(allocationFailure);
REQUIRE(std::strcmp(source.message.pointer, "future already retrieved") == 0);
_ZdlPv_nid_postfix(source.message.pointer);
LibcException::FutureErrorObject object {};
object.message.capacity = 15;
_ZNSt12future_errorD1Ev_nid_postfix(&object);
REQUIRE(object.message.reserved == 0 && object.message.size == 0 && object.message.capacity == 15);
REQUIRE(object.message.buffer[0] == 0);
}
int main() {
const std::array<void*, 10> api {
reinterpret_cast<void*>(&TestAllocate), reinterpret_cast<void*>(&TestFree),
reinterpret_cast<void*>(&TestCalloc), reinterpret_cast<void*>(&TestReallocate),
reinterpret_cast<void*>(&TestAlign), reinterpret_cast<void*>(&TestRealign),
reinterpret_cast<void*>(&TestPosixAlign), nullptr, nullptr, nullptr};
ApplicationHeapRegister_nid_no_patch(api.data());
CheckBaseCategory();
CheckFutureCategory();
CheckFutureError();
std::puts("guest future category, error_category base members and future_error layout: PASS");
return 0;
}
+1
View File
@@ -178,6 +178,7 @@
- [_WStoul](../../../core/libs/prx/libc/src/Strings.cpp), [_Iswctype](../../../core/libs/prx/libc/src/RuneLocale.cpp), [swscanf_s and swscanf](../../../core/libs/prx/libc/src/ScanningWide.cpp) (libc) - `_WStoul` is `wcstoul` and throws for a base other than 0 or 2 to 36. `_Iswctype` takes the class numbers of Dinkumware's [xwcc.h](https://github.com/superna9999/ti-cgt6x/blob/cgt6x_7_3_4/include/xwcc.h) (1 `alnum` to 11 `xdigit`, 12 `blank`) and throws for any other value, including the FreeBSD masks `wctype` returns; the numbering is not confirmed on the console's `libc.prx`. `swscanf_s` follows C11 7.29.2.2 and K.3.9.1.5, but reads the size argument as `unsigned int` like `sscanf_s` where K.3.9.1.5 specifies `rsize_t`; the console's width is unverified. A runtime-constraint violation (null buffer, format or receiving pointer) only returns `EOF`, and a too small array gets a null first element as in `sscanf_s`. A literal at end of input returns the number of assignments, including zero, rather than `EOF` as FreeBSD does, as reported from the console scanner in #8479; `%%` at end of input still returns `EOF`. A literal mismatch is a matching failure, where FreeBSD's `vfwscanf` reports an input failure. An incomplete floating exponent such as `1e+` converts its mantissa, as reported from the console scanner in #8479; other incomplete exponent forms are not separately verified. A short `%c` field is currently a matching failure without storing its available prefix, while #9321 reports that the console stores a short `%3lc` field; its return count, partial-output extent and precedence are unverified. An integer field that is only a prefix, such as `0x`, does not convert; FreeBSD's `vfwscanf` pushes back the `x` and converts the `0`. Integers out of range are truncated from `strtoll`/`strtoull`. `%p` follows FreeBSD 11's [vfwscanf.c](https://github.com/freebsd/freebsd-src/blob/4ab2e064d7950be84256d671a7ae93f87cc6aa36/lib/libc/stdio/vfwscanf.c#L610-L615): a hexadecimal integer with an optional `0x`, converted as unsigned and stored as a pointer; a length modifier on it throws, where FreeBSD ignores it. `%S` and `%C`, with any length modifier or `*`, end the scan as a matching failure, as the console's Dinkumware scanner in PPSA21564's `libc.prx` treats them as invalid conversions; this was read from the module, not run on a console, and whether input ending before them is an input failure instead is unverified. It throws for unknown length modifiers, scanset ranges, `%n` with suppression or width, and narrow `%s`, `%c` or `%[` input that the C-locale `wcrtomb` cannot convert. `swscanf` runs the same scanner without the size arguments of `%s`, `%c` and `%[`, as FreeBSD's [swscanf.c](https://github.com/freebsd/freebsd-src/blob/d106002967eff712894ab8b5337bbbff106ce5e0/lib/libc/stdio/swscanf.c) passes its arguments to `vswscanf`, so everything above except the sizes applies to it; it also keeps the null buffer, format and receiving pointer checks returning `EOF`, where C leaves those undefined. Neither variant is confirmed on a PS5 title
- [floating scan errno](../../../core/libs/prx/libc/src/ScanningWide.cpp) (libc) leaves narrow `sscanf` inheriting host `ERANGE` for out-of-range `%f`, `%e` and `%lf`; wide `%Lf` retains host `strtold` errno, so glibc's `ERANGE` for the representable `1e-4950` differs from the bundled library. Wide float and double conversions restore the previous `errno` after `strtof`/`strtod`, as observed in the bundled `libc.prx`; converted values and long-double range handling are retained.
- [flock](../../../core/libs/prx/libc/src/FileLock.cpp) (libkernel) on Windows shares one lock state between duplicated descriptors, following FreeBSD 11.4's [flock(2)](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/sys/flock.2). Independent opens have separate states; closing a descriptor or reopening a stream drops only its reference. Lock transitions on one open file description are serialized. The lock is a host [LockFileEx](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex) lock on one byte at offset 2^63 - 2, which no file reaches, so it excludes other `flock` calls and leaves reads, writes and mapped views alone, like FreeBSD's advisory locks. Console behavior, signal interruption and concurrent closure of a descriptor while its lock call is pending are unverified.
- [std::error_category, std::future_category and std::future_error](../../../core/libs/prx/libc/src/exception/StandardExceptions.cpp) (libc) - measured from PPSA02530's `libc.prx`, except that the future category's deleting destructor throws instead of freeing it, the string destructor frees the block instead of the console header path, and a `_Doraise` copy of a heap message and header size are unverified. `error_code` keeps the value in the second dword, so `default_error_condition` shifts it into the high half and `equivalent` compares that dword and the category. `_Future_error_category` derives from `_Generic_error_category`, is `future` and maps 1..4 to `broken promise`, `future already retrieved`, `promise already satisfied` and `no state`; other values use the generic map, but `future_error::what()` returns null outside 1..4. `future_error` is 0x40 bytes (vtable, `GuestLocale::String` at 0x8, `error_code` at 0x30). `_Rethrow_future_exception` delegates to `rethrow_exception`.
- [std::_Throw_Cpp_error](../../../core/libs/prx/libc/src/exception/StandardExceptions.cpp) (libc) follows Dinkumware `thread0.cpp` ([VC12](https://github.com/ganboing/crt.vc12/blob/f1d47126/src/thread0.cpp), [STL](https://github.com/microsoft/STL/blob/da52dc0f/stl/src/thread0.cpp)), without console verification. Codes outside 0..6 throw `std::out_of_range`, the deleting destructor throws, and STREAMS errnos absent from FreeBSD 11.4 [`errno.h`](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/sys/errno.h) (`ENODATA`, `ENOSR`, `ENOSTR`, `ETIME`) are omitted. Codes 0..6 throw a 32-byte [VC12 `system_error`](https://github.com/ganboing/crt.vc12/blob/f1d47126/src/system_error) with FreeBSD errnos (16, 22, 3, 12, 1, 11, 35), a static `generic_category` (not `_Generic_object`), and unverified 40-byte [GuestLocale::String](../../../core/libs/prx/libc/include/GuestLocale.hpp) messages from [`syserror.cpp`](https://github.com/ganboing/crt.vc12/blob/f1d47126/src/syserror.cpp)
- [Call tracing](../../../core/libs/prx/libc/src/specifics/x86_64/CallTrace.cpp) (`--trace-imports`, `APS5_TRACE_CALLS`) records only the entry of a call, not its arguments or result. One process-wide ring keeps the last 65536 calls, so a busy thread pushes the others out; the reporting thread's own calls are listed only while they are still in that ring. Calls are counted per traced target; an import bound to a target already traced under another name is counted under the first name. The thunk saves the integer argument registers and `xmm0` to `xmm7`; wider vector arguments survive only while the recording code avoids AVX, which is untested. Linux and macOS have no crash report, so they print the trace only on `SIGABRT` (guest or host `abort()`), not on `SIGSEGV`; a caller in a module loaded after the last traced import prints without a module name, and a thread aborting while another writes the report waits for it at most 2 seconds. An arm64 libc build omits the recorder
- [_Dtest](../../../core/libs/prx/libc/src/Math.cpp) (libc) follows Dinkumware double classification matching _FDtest: finite values return -1, subnormals -2, zeros 0, infinities 1, and NaNs 2.