feat(libc): implement generic error category messages
This commit is contained in:
parent
e99a1b0aa9
commit
e3cef332e6
|
|
@ -34,7 +34,7 @@ if(BUILD_TESTING)
|
|||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAmpr>")
|
||||
endif()
|
||||
|
||||
if(MINGW)
|
||||
if(MINGW OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
enable_language(ASM)
|
||||
add_test_executable(guest_ampr_cpp_error_tests
|
||||
${LIBS_INCLUDE_DIR}/tests/GuestAmprCppError.cpp
|
||||
|
|
@ -43,7 +43,10 @@ if(BUILD_TESTING)
|
|||
target_link_libraries(guest_ampr_cpp_error_tests PRIVATE libSceAmpr libkernel libc)
|
||||
configure_windows_unwind(guest_ampr_cpp_error_tests)
|
||||
add_test(NAME guest_ampr_cpp_error COMMAND guest_ampr_cpp_error_tests)
|
||||
set_tests_properties(guest_ampr_cpp_error PROPERTIES TIMEOUT 10 ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAmpr>")
|
||||
set_tests_properties(guest_ampr_cpp_error PROPERTIES TIMEOUT 10)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_ampr_cpp_error PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAmpr>")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -442,6 +442,16 @@ if(BUILD_TESTING)
|
|||
target_include_directories(guest_new_handler_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_new_handler_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_new_handler_tests)
|
||||
add_test_executable(guest_generic_category_tests ${LIBS_INCLUDE_DIR}/tests/GuestGenericCategory.cpp)
|
||||
target_include_directories(guest_generic_category_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_generic_category_tests PRIVATE libc)
|
||||
configure_windows_unwind(guest_generic_category_tests)
|
||||
add_test(NAME guest_generic_category COMMAND guest_generic_category_tests)
|
||||
set_tests_properties(guest_generic_category PROPERTIES TIMEOUT 10)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_generic_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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "prx/libc/include/exceptions/Runtime.hpp"
|
||||
#include "prx/libc/include/GuestLocale.hpp"
|
||||
#include <cstdio>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
|
|
@ -6,6 +7,8 @@
|
|||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
extern "C" void* APS5_VABI _Znwm_nid_postfix(std::size_t size);
|
||||
|
||||
namespace LibcException {
|
||||
struct TypeRecord { const void* const* vtable; const char* name; const TypeRecord* base; };
|
||||
struct ExceptionObject { const void* vtable; const char* message; };
|
||||
|
|
@ -129,6 +132,86 @@ struct SystemErrorObject { ExceptionObject base; ErrorCode code; };
|
|||
static_assert(sizeof(ErrorCode) == 16 && sizeof(SystemErrorObject) == 32 && offsetof(SystemErrorObject, code) == 16);
|
||||
struct CppError { int value; const char* message; };
|
||||
|
||||
static const char* GenericErrorMessage(int value) {
|
||||
static constexpr CppError errors[] {
|
||||
{1, "operation not permitted"},
|
||||
{2, "no such file or directory"},
|
||||
{3, "no such process"},
|
||||
{4, "interrupted"},
|
||||
{5, "io error"},
|
||||
{6, "no such device or address"},
|
||||
{7, "argument list too long"},
|
||||
{8, "executable format error"},
|
||||
{9, "bad file descriptor"},
|
||||
{10, "no child process"},
|
||||
{11, "resource deadlock would occur"},
|
||||
{12, "not enough memory"},
|
||||
{13, "permission denied"},
|
||||
{14, "bad address"},
|
||||
{16, "device or resource busy"},
|
||||
{17, "file exists"},
|
||||
{18, "cross device link"},
|
||||
{19, "no such device"},
|
||||
{20, "not a directory"},
|
||||
{21, "is a directory"},
|
||||
{22, "invalid argument"},
|
||||
{23, "too many files open in system"},
|
||||
{24, "too many files open"},
|
||||
{25, "inappropriate io control operation"},
|
||||
{26, "text file busy"},
|
||||
{27, "file too large"},
|
||||
{28, "no space on device"},
|
||||
{29, "invalid seek"},
|
||||
{30, "read only file system"},
|
||||
{31, "too many links"},
|
||||
{32, "broken pipe"},
|
||||
{33, "argument out of domain"},
|
||||
{34, "result out of range"},
|
||||
{35, "operation would block"},
|
||||
{36, "operation in progress"},
|
||||
{37, "connection already in progress"},
|
||||
{38, "not a socket"},
|
||||
{39, "destination address required"},
|
||||
{40, "message size"},
|
||||
{41, "wrong protocol type"},
|
||||
{42, "no protocol option"},
|
||||
{43, "protocol not supported"},
|
||||
{45, "not supported"},
|
||||
{47, "address family not supported"},
|
||||
{48, "address in use"},
|
||||
{49, "address not available"},
|
||||
{50, "network down"},
|
||||
{51, "network unreachable"},
|
||||
{52, "network reset"},
|
||||
{53, "connection aborted"},
|
||||
{54, "connection reset"},
|
||||
{55, "no buffer space"},
|
||||
{56, "already connected"},
|
||||
{57, "not connected"},
|
||||
{60, "timed out"},
|
||||
{61, "connection refused"},
|
||||
{62, "too many symbolic link levels"},
|
||||
{63, "filename too long"},
|
||||
{65, "host unreachable"},
|
||||
{66, "directory not empty"},
|
||||
{77, "no lock available"},
|
||||
{78, "function not supported"},
|
||||
{82, "identifier removed"},
|
||||
{83, "no message"},
|
||||
{84, "value too large"},
|
||||
{85, "operation canceled"},
|
||||
{86, "illegal byte sequence"},
|
||||
{89, "bad message"},
|
||||
{91, "no link"},
|
||||
{92, "protocol error"},
|
||||
{95, "state not recoverable"},
|
||||
{96, "owner dead"}
|
||||
};
|
||||
for (const auto& error : errors)
|
||||
if (error.value == value) return error.message;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const TypeRecord ErrorCategoryType {ClassTypeVtable + 2, "St14error_category", nullptr};
|
||||
const TypeRecord GenericCategoryType {SingleTypeVtable + 2, "St23_Generic_error_category", &ErrorCategoryType};
|
||||
static void APS5_VABI GenericCategoryDestroy_nid_no_patch(CategoryObject*) {}
|
||||
|
|
@ -136,8 +219,18 @@ static void APS5_VABI GenericCategoryDelete_nid_no_patch(CategoryObject*) {
|
|||
throw std::invalid_argument("std::_Generic_error_category: the generic category object is not heap allocated");
|
||||
}
|
||||
static const char* APS5_VABI GenericCategoryName_nid_no_patch(const CategoryObject*) { return "generic"; }
|
||||
static void* APS5_VABI GenericCategoryMessage_nid_no_patch(void* result, const CategoryObject*, int) {
|
||||
NotImplemented_nid_no_patch("std::_Generic_error_category::message");
|
||||
static GuestLocale::String* APS5_VABI GenericCategoryMessage_nid_no_patch(GuestLocale::String* result, const CategoryObject*, int value) {
|
||||
if (!result) throw std::invalid_argument("std::_Generic_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));
|
||||
std::memcpy(storage, message, length + 1);
|
||||
result->reserved = 0;
|
||||
if (length > 15) result->pointer = storage;
|
||||
result->size = length;
|
||||
result->capacity = capacity;
|
||||
return result;
|
||||
}
|
||||
static ErrorCode APS5_VABI GenericCategoryCondition_nid_no_patch(const CategoryObject* self, int value) { return {value, self}; }
|
||||
|
|
@ -159,6 +252,16 @@ const CategoryObject GenericCategory {GenericCategoryVtable + 2};
|
|||
}
|
||||
|
||||
extern "C" {
|
||||
const char* APS5_VABI _ZSt13_Syserror_mapi_nid_postfix(int value) {
|
||||
return LibcException::GenericErrorMessage(value);
|
||||
}
|
||||
const LibcException::CategoryObject* APS5_VABI _ZSt16generic_categoryv_nid_postfix() {
|
||||
return &LibcException::GenericCategory;
|
||||
}
|
||||
GuestLocale::String* APS5_VABI _ZNKSt23_Generic_error_category7messageEi_nid_postfix(
|
||||
GuestLocale::String* result, const LibcException::CategoryObject* self, int value) {
|
||||
return LibcException::GenericCategoryMessage_nid_no_patch(result, self, value);
|
||||
}
|
||||
LibcException::TypeRecord _ZTISt8ios_base_nid_postfix {LibcException::ClassTypeVtable + 2, "St8ios_base", nullptr};
|
||||
LibcException::TypeRecord _ZTISt9exception_nid_postfix {LibcException::ClassTypeVtable + 2, "St9exception", nullptr};
|
||||
LibcException::ExceptionVtable _ZTVSt9exception_nid_postfix {0, &_ZTISt9exception_nid_postfix, LibcException::GuestDestroyPlain_nid_no_patch, LibcException::GuestDeletePlain_nid_no_patch, LibcException::GuestPlainWhat_nid_no_patch};
|
||||
|
|
@ -382,7 +485,7 @@ const char* APS5_VABI _ZNKSt8ios_base7failure4whatEv_nid_postfix(const LibcExcep
|
|||
throw std::out_of_range("_Throw_Cpp_error: " + std::to_string(code) + " is not a _Cpp_error code");
|
||||
const auto& error = errors[code];
|
||||
char message[128];
|
||||
std::snprintf(message, sizeof(message), "%s: %s", error.message, error.message);
|
||||
std::snprintf(message, sizeof(message), "%s: %s", error.message, LibcException::GenericErrorMessage(error.value));
|
||||
auto* object = static_cast<LibcException::SystemErrorObject*>(__cxa_allocate_exception_nid_postfix(sizeof(LibcException::SystemErrorObject)));
|
||||
LibcException::Construct(&object->base, _ZTVSt12system_error_nid_postfix, message);
|
||||
object->code = {error.value, &LibcException::GenericCategory};
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@ GuestCatch:
|
|||
call FixtureFail
|
||||
ud2
|
||||
.cfi_endproc
|
||||
#ifdef _WIN32
|
||||
.section .rdata,"dr"
|
||||
#else
|
||||
.section .rodata
|
||||
#endif
|
||||
.Lcatch_lsda:
|
||||
.byte 0xff, 0x80
|
||||
.uleb128 .Lcatch_types_end-.Lcatch_types_offset
|
||||
|
|
@ -48,3 +52,6 @@ GuestCatch:
|
|||
.quad FixtureCatchType
|
||||
.quad FixtureOtherType
|
||||
.Lcatch_types_end:
|
||||
#ifndef _WIN32
|
||||
.section .note.GNU-stack,"",@progbits
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "prx/libc/include/GuestLocale.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
|
@ -29,7 +32,7 @@ static constexpr Expected Errors[] {
|
|||
{12, "not enough memory: not enough memory"},
|
||||
{1, "operation not permitted: operation not permitted"},
|
||||
{11, "resource deadlock would occur: resource deadlock would occur"},
|
||||
{35, "resource unavailable try again: resource unavailable try again"}
|
||||
{35, "resource unavailable try again: operation would block"}
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
|
@ -41,6 +44,7 @@ extern const unsigned char _ZTISt12system_error_nid_postfix[];
|
|||
void APS5_VABI _ZNSt12system_errorC1ERKS__nid_postfix(SystemError* self, const SystemError* source);
|
||||
SystemError* APS5_VABI _ZNSt12system_erroraSERKS__nid_postfix(SystemError* self, const SystemError* source);
|
||||
void APS5_VABI _ZNSt12system_errorD1Ev_nid_postfix(SystemError* self);
|
||||
void APS5_VABI _ZdlPv_nid_postfix(void* pointer);
|
||||
void APS5_VABI GuestCatch(int code);
|
||||
|
||||
const void* FixtureCatchType = nullptr;
|
||||
|
|
@ -86,14 +90,11 @@ static void CheckCategory(const SystemError& error) {
|
|||
Require(equivalentCode(category, &error.code, error.code.value), 28);
|
||||
Require(!equivalentCode(category, &error.code, error.code.value + 1), 29);
|
||||
Require(!equivalentCode(category, &foreign, error.code.value), 30);
|
||||
bool messageThrew = false;
|
||||
try {
|
||||
unsigned char result[32] {};
|
||||
Slot<CategoryMessage>(category->vtable, 3)(result, category, error.code.value);
|
||||
} catch (const std::runtime_error&) {
|
||||
messageThrew = true;
|
||||
}
|
||||
Require(messageThrew, 31);
|
||||
GuestLocale::String result {};
|
||||
Require(Slot<CategoryMessage>(category->vtable, 3)(&result, category, error.code.value) == &result, 33);
|
||||
const char* data = result.capacity <= 15 ? result.buffer : result.pointer;
|
||||
Require(std::strcmp(data, std::strstr(FixtureExpected->what, ": ") + 2) == 0, 34);
|
||||
if (result.capacity > 15) _ZdlPv_nid_postfix(result.pointer);
|
||||
bool deleteThrew = false;
|
||||
try {
|
||||
Slot<CategoryDelete>(category->vtable, 1)(category);
|
||||
|
|
@ -148,6 +149,8 @@ static void CheckOutOfRange(int code) {
|
|||
}
|
||||
|
||||
int main() {
|
||||
const std::array<void*, 10> api {};
|
||||
ApplicationHeapRegister_nid_no_patch(api.data());
|
||||
FixtureOtherType = _ZTISt11logic_error_nid_postfix;
|
||||
for (int code = 0; code < static_cast<int>(std::size(Errors)); ++code) Catch(_ZTISt12system_error_nid_postfix, code);
|
||||
Catch(_ZTISt13runtime_error_nid_postfix, 5);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
#include "prx/libc/include/GuestLocale.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
struct Category { const void* const* vtable; };
|
||||
struct ErrorCode { int value; const Category* category; };
|
||||
using Message = GuestLocale::String* (APS5_VABI *)(GuestLocale::String*, const Category*, int);
|
||||
using Name = const char* (APS5_VABI *)(const Category*);
|
||||
using Condition = ErrorCode (APS5_VABI *)(const Category*, int);
|
||||
|
||||
extern "C" {
|
||||
const char* APS5_VABI _ZSt13_Syserror_mapi_nid_postfix(int value);
|
||||
const Category* APS5_VABI _ZSt16generic_categoryv_nid_postfix();
|
||||
GuestLocale::String* APS5_VABI _ZNKSt23_Generic_error_category7messageEi_nid_postfix(
|
||||
GuestLocale::String* result, const Category* self, int value);
|
||||
int* APS5_VABI __error_nid_postfix();
|
||||
void APS5_VABI _ZdlPv_nid_postfix(void* pointer);
|
||||
}
|
||||
|
||||
static void Require(bool value, int line) {
|
||||
if (value) return;
|
||||
std::fprintf(stderr, "generic category check failed at line %d\n", line);
|
||||
std::exit(1);
|
||||
}
|
||||
#define REQUIRE(value) Require((value), __LINE__)
|
||||
|
||||
static void CheckMessage(Message message, const Category* 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);
|
||||
}
|
||||
|
||||
int main() {
|
||||
const std::array<void*, 10> api {};
|
||||
ApplicationHeapRegister_nid_no_patch(api.data());
|
||||
const Category* category = _ZSt16generic_categoryv_nid_postfix();
|
||||
REQUIRE(category && category == _ZSt16generic_categoryv_nid_postfix());
|
||||
REQUIRE(std::strcmp(reinterpret_cast<Name>(category->vtable[2])(category), "generic") == 0);
|
||||
const auto message = reinterpret_cast<Message>(category->vtable[3]);
|
||||
const auto condition = reinterpret_cast<Condition>(category->vtable[4])(category, 35);
|
||||
REQUIRE(condition.value == 35 && condition.category == category);
|
||||
|
||||
struct Expected { int value; const char* text; };
|
||||
const Expected expected[] {
|
||||
{3, "no such process"}, {14, "bad address"}, {13, "permission denied"},
|
||||
{25, "inappropriate io control operation"}, {35, "operation would block"},
|
||||
{45, "not supported"}, {47, "address family not supported"}, {60, "timed out"},
|
||||
{78, "function not supported"}, {95, "state not recoverable"}, {96, "owner dead"}
|
||||
};
|
||||
for (const auto& error : expected) {
|
||||
*__error_nid_postfix() = 61;
|
||||
const char* mapped = _ZSt13_Syserror_mapi_nid_postfix(error.value);
|
||||
REQUIRE(mapped && std::strcmp(mapped, error.text) == 0 && *__error_nid_postfix() == 61);
|
||||
CheckMessage(message, category, error.value, error.text);
|
||||
CheckMessage(_ZNKSt23_Generic_error_category7messageEi_nid_postfix, category, error.value, error.text);
|
||||
}
|
||||
|
||||
unsigned known = 0;
|
||||
for (int value = 1; value <= 96; ++value) {
|
||||
const char* mapped = _ZSt13_Syserror_mapi_nid_postfix(value);
|
||||
if (mapped) ++known;
|
||||
CheckMessage(message, category, value, mapped ? mapped : "unknown error");
|
||||
}
|
||||
REQUIRE(known == 72);
|
||||
for (int value : {std::numeric_limits<int>::min(), -1, 0, 15, 44, 97, std::numeric_limits<int>::max()}) {
|
||||
*__error_nid_postfix() = 61;
|
||||
REQUIRE(_ZSt13_Syserror_mapi_nid_postfix(value) == nullptr && *__error_nid_postfix() == 61);
|
||||
CheckMessage(message, category, value, "unknown error");
|
||||
}
|
||||
GuestLocale::String first {}, second {};
|
||||
REQUIRE(message(&first, category, 25) == &first && message(&second, category, 11) == &second);
|
||||
REQUIRE(first.pointer != second.pointer);
|
||||
_ZdlPv_nid_postfix(second.pointer);
|
||||
REQUIRE(std::strcmp(first.pointer, "inappropriate io control operation") == 0);
|
||||
_ZdlPv_nid_postfix(first.pointer);
|
||||
bool rejected = false;
|
||||
try { message(nullptr, category, 22); }
|
||||
catch (const std::invalid_argument&) { rejected = true; }
|
||||
REQUIRE(rejected);
|
||||
std::puts("guest generic category, FreeBSD error map and message ownership: PASS");
|
||||
}
|
||||
|
|
@ -62,4 +62,4 @@
|
|||
- [daemon](../../../core/libs/prx/libc/src/Process.cpp) (libc) throws: forking into the background is not supported. PPSA85300 imports it and fails to load without it. VLC 3.0 calls it only when its `daemon` option is set ([libvlc.c](https://github.com/videolan/vlc/blob/6de05adcbaf2e8b85fe86aad4169393098628119/src/libvlc.c#L190-L197)), and the title starts libVLC with `--ignore-config` and without `--daemon` ([player.cc](https://github.com/ZiZc3/VLC/blob/f3253020a64368153233ede29183014c0b6c8979/src/player.cc#L960-L981))
|
||||
- [_WStoul](../../../core/libs/prx/libc/src/Strings.cpp), [_Iswctype](../../../core/libs/prx/libc/src/RuneLocale.cpp) and [swscanf_s](../../../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 mismatch is a matching failure and a field that is only a prefix, such as `0x` or `1e`, does not convert, as C11 requires; FreeBSD's `vfwscanf` reports an input failure for the first and converts the second. Integers out of range are truncated from `strtoll`/`strtoull`. It throws for `%p`, `%S`, `%C`, unknown length modifiers, scanset ranges, `%n` with suppression or width, and narrow `%s`, `%c` or `%[` input that the C-locale `wcrtomb` cannot convert
|
||||
- [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 host's [LockFileEx](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex) locks also restrict ordinary reads and writes through other handles, unlike FreeBSD's advisory locks, and do not restrict mapped views. Console behavior, signal interruption and concurrent closure of a descriptor while its lock call is pending are unverified.
|
||||
- [std::_Throw_Cpp_error](../../../core/libs/prx/libc/src/exception/StandardExceptions.cpp) (libSceAmpr) follows Dinkumware's `thread0.cpp` as shipped with [Visual C++ 2013](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/thread0.cpp) and in [microsoft/STL](https://github.com/microsoft/STL/blob/da52dc0fcadbd83690cfd1ed32408d2847439493/stl/src/thread0.cpp), without console verification. `_Cpp_error` codes 0 to 6 throw a guest `std::system_error` whose `error_code` holds the FreeBSD errno of the `errc` value (EBUSY 16, EINVAL 22, ESRCH 3, ENOMEM 12, EPERM 1, EDEADLK 11, EAGAIN 35) and the generic category, and whose `what()` is the table message, `": "` and the generic category's message for that errno, taken to be the same text as in VC12's [`syserror.cpp`](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/syserror.cpp); another code throws `std::out_of_range` on the host instead of reading past the table. The guest `system_error` is taken to be [VC12's](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/system_error): the 16-byte `runtime_error` followed by `error_code {int, const error_category*}`, 32 bytes, so its copy constructor and assignment copy the code too. The generic category is a static libc object with VC12's `error_category` virtual order (destructors, `name`, `message`, `default_error_condition`, the two `equivalent`), not the console's `_Error_objects<int>::_Generic_object`, and no `generic_category()` export returns it, so it compares unequal to a title's own `generic_category()`. Its `message` throws, as the guest `std::string` layout is not modelled, and so does its deleting destructor
|
||||
- [std::_Throw_Cpp_error](../../../core/libs/prx/libc/src/exception/StandardExceptions.cpp) (libSceAmpr) follows Dinkumware's `thread0.cpp` as shipped with [Visual C++ 2013](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/thread0.cpp) and in [microsoft/STL](https://github.com/microsoft/STL/blob/da52dc0fcadbd83690cfd1ed32408d2847439493/stl/src/thread0.cpp), without console verification. `_Cpp_error` codes 0 to 6 throw a guest `std::system_error` whose `error_code` holds the FreeBSD errno of the `errc` value (EBUSY 16, EINVAL 22, ESRCH 3, ENOMEM 12, EPERM 1, EDEADLK 11, EAGAIN 35) and the generic category, and whose `what()` is the table message, `": "` and the generic category's message for that errno, selected by the first matching errno entry in VC12's [`syserror.cpp`](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/syserror.cpp); another code throws `std::out_of_range` on the host instead of reading past the table. The guest `system_error` is taken to be [VC12's](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/system_error): the 16-byte `runtime_error` followed by `error_code {int, const error_category*}`, 32 bytes, so its copy constructor and assignment copy the code too. The generic category is a static libc object with VC12's `error_category` virtual order (destructors, `name`, `message`, `default_error_condition`, the two `equivalent`), not the console's `_Error_objects<int>::_Generic_object`, and the new `generic_category()` export returns that object; a title's inline category object in a bundled libc can still have a different identity. Its `message` and direct message export construct the existing [GuestLocale::String](../../../core/libs/prx/libc/include/GuestLocale.hpp) model: a reserved eight-byte word, 16-byte inline buffer, length and capacity, with guest `operator new` storage above 15 bytes. This 40-byte string layout and virtual return ABI have not been verified against a console library. The deleting destructor still throws. `_Syserror_map` covers the 72 distinct Dinkumware `errc` values present in FreeBSD 11.4 [errno.h](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/sys/errno.h), returning null for other values; category messages use `unknown error` for those. First-match aliases are `EAGAIN`/`EWOULDBLOCK` -> `operation would block` and `ENOTSUP`/`EOPNOTSUPP` -> `not supported`. STREAMS-only errno assignments absent from that header (`ENODATA`, `ENOSR`, `ENOSTR`, `ETIME`) are not inferred
|
||||
|
|
|
|||
Loading…
Reference in New Issue