From 36c874e29619bd65191f581ad0ee8f047be3bf86 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Sat, 5 Sep 2026 19:54:11 +0300 Subject: [PATCH] feat(libc): implement exception unwinding + array cleanup --- core/libs/prx/libc/src/CxxAbiSupport.cpp | 83 ++++++-------- .../prx/libc/src/exception/ArraySupport.cpp | 102 ++++++++++++++++++ core/libs/prx/libc/src/exception/Exports.cpp | 18 ++++ core/libs/prx/libc/src/exception/Runtime.cpp | 12 ++- core/libs/prx/libc/src/exception/Unwind.cpp | 24 +++-- 5 files changed, 176 insertions(+), 63 deletions(-) create mode 100644 core/libs/prx/libc/src/exception/ArraySupport.cpp create mode 100644 core/libs/prx/libc/src/exception/Exports.cpp diff --git a/core/libs/prx/libc/src/CxxAbiSupport.cpp b/core/libs/prx/libc/src/CxxAbiSupport.cpp index 88ed21c8c..09bd9e680 100644 --- a/core/libs/prx/libc/src/CxxAbiSupport.cpp +++ b/core/libs/prx/libc/src/CxxAbiSupport.cpp @@ -1,7 +1,5 @@ -#include "exception/Unwind.cpp" -#include "exception/Runtime.cpp" -#include "exception/Personality.cpp" -#include "exception/StandardExceptions.cpp" +#include "exception/Runtime.hpp" +#include #include #include #include @@ -17,57 +15,44 @@ #include #include +namespace LibcGuard { +thread_local std::uint64_t* active[64] {}; +thread_local std::size_t depth {}; +} + extern "C" { int __cxa_guard_acquire_nid_postfix(std::uint64_t* guardObject) { - (void)guardObject; - return 0; + auto* bytes = reinterpret_cast(guardObject); + std::atomic_ref initialized(bytes[0]); + std::atomic_ref lock(bytes[1]); + if (initialized.load(std::memory_order_acquire)) return 0; + for (std::size_t i = 0; i < LibcGuard::depth; ++i) + if (LibcGuard::active[i] == guardObject) LibcException::Terminate(); + for (;;) { + unsigned char expected = 0; + if (lock.compare_exchange_weak(expected, 1, std::memory_order_acquire)) break; + if (initialized.load(std::memory_order_acquire)) return 0; + std::this_thread::yield(); + } + if (initialized.load(std::memory_order_acquire)) { lock.store(0, std::memory_order_release); return 0; } + if (LibcGuard::depth == 64) LibcException::Terminate(); + LibcGuard::active[LibcGuard::depth++] = guardObject; + return 1; } -void __cxa_guard_release_nid_postfix(std::uint64_t* guardObject) { (void)guardObject; } -void __cxa_guard_abort_nid_postfix(std::uint64_t* guardObject) { (void)guardObject; } - -void* __cxa_vec_new_nid_postfix(std::size_t n, std::size_t s, std::size_t p, void (*ctor)(void*), void (*dtor)(void*)) { - (void)n; (void)s; (void)p; (void)ctor; (void)dtor; - return nullptr; +void __cxa_guard_release_nid_postfix(std::uint64_t* guardObject) { + if (!LibcGuard::depth || LibcGuard::active[LibcGuard::depth - 1] != guardObject) LibcException::Terminate(); + --LibcGuard::depth; + auto* bytes = reinterpret_cast(guardObject); + std::atomic_ref(bytes[0]).store(1, std::memory_order_release); + std::atomic_ref(bytes[1]).store(0, std::memory_order_release); } - -void* __cxa_vec_new2_nid_postfix(std::size_t n, std::size_t s, std::size_t p, void (*ctor)(void*), void (*dtor)(void*), void* (*alloc)(std::size_t), void (*dealloc)(void*)) { - (void)n; (void)s; (void)p; (void)ctor; (void)dtor; (void)alloc; (void)dealloc; - return nullptr; -} - -void* __cxa_vec_new3_nid_postfix(std::size_t n, std::size_t s, std::size_t p, void (*ctor)(void*), void (*dtor)(void*), void* (*alloc)(std::size_t), void (*dealloc)(void*, std::size_t)) { - (void)n; (void)s; (void)p; (void)ctor; (void)dtor; (void)alloc; (void)dealloc; - return nullptr; -} - -void __cxa_vec_ctor_nid_postfix(void* arr, std::size_t n, std::size_t s, void (*ctor)(void*), void (*dtor)(void*)) { - (void)arr; (void)n; (void)s; (void)ctor; (void)dtor; -} - -void __cxa_vec_dtor_nid_postfix(void* arr, std::size_t n, std::size_t s, void (*dtor)(void*)) { - (void)arr; (void)n; (void)s; (void)dtor; -} - -void __cxa_vec_cleanup_nid_postfix(void* arr, std::size_t n, std::size_t s, void (*dtor)(void*)) { - (void)arr; (void)n; (void)s; (void)dtor; -} - -void __cxa_vec_delete_nid_postfix(void* arr, std::size_t s, std::size_t p, void (*dtor)(void*)) { - (void)arr; (void)s; (void)p; (void)dtor; -} - -void __cxa_vec_delete2_nid_postfix(void* arr, std::size_t s, std::size_t p, void (*dtor)(void*), void (*dealloc)(void*)) { - (void)arr; (void)s; (void)p; (void)dtor; (void)dealloc; -} - -void __cxa_vec_delete3_nid_postfix(void* arr, std::size_t s, std::size_t p, void (*dtor)(void*), void (*dealloc)(void*, std::size_t)) { - (void)arr; (void)s; (void)p; (void)dtor; (void)dealloc; -} - -void __cxa_vec_cctor_nid_postfix(void* dst, void* src, std::size_t n, std::size_t s, void (*cctor)(void*, void*), void (*dtor)(void*)) { - (void)dst; (void)src; (void)n; (void)s; (void)cctor; (void)dtor; +void __cxa_guard_abort_nid_postfix(std::uint64_t* guardObject) { + if (!LibcGuard::depth || LibcGuard::active[LibcGuard::depth - 1] != guardObject) LibcException::Terminate(); + --LibcGuard::depth; + auto* bytes = reinterpret_cast(guardObject); + std::atomic_ref(bytes[1]).store(0, std::memory_order_release); } void* __cxa_demangle_nid_postfix(const char* mangled, char* buf, std::size_t* len, int* status) { diff --git a/core/libs/prx/libc/src/exception/ArraySupport.cpp b/core/libs/prx/libc/src/exception/ArraySupport.cpp new file mode 100644 index 000000000..1f3529fa2 --- /dev/null +++ b/core/libs/prx/libc/src/exception/ArraySupport.cpp @@ -0,0 +1,102 @@ +#include "Runtime.hpp" +#include + +extern "C" { +void __cxa_vec_cleanup_nid_postfix(void* array, std::size_t count, std::size_t size, void (*destroy)(void*)) { + if (!destroy) return; + try { + while (count) destroy(static_cast(array) + --count * size); + } catch (...) { LibcException::Terminate(); } +} +void __cxa_vec_ctor_nid_postfix(void* array, std::size_t count, std::size_t size, void (*construct)(void*), void (*destroy)(void*)) { + if (!construct) return; + std::size_t complete = 0; + try { + for (; complete < count; ++complete) construct(static_cast(array) + complete * size); + } catch (...) { + __cxa_vec_cleanup_nid_postfix(array, complete, size, destroy); + throw; + } +} +void __cxa_vec_cctor_nid_postfix(void* destination, void* source, std::size_t count, std::size_t size, void (*copy)(void*, void*), void (*destroy)(void*)) { + if (!copy) return; + std::size_t complete = 0; + try { + for (; complete < count; ++complete) + copy(static_cast(destination) + complete * size, static_cast(source) + complete * size); + } catch (...) { + __cxa_vec_cleanup_nid_postfix(destination, complete, size, destroy); + throw; + } +} +void __cxa_vec_dtor_nid_postfix(void* array, std::size_t count, std::size_t size, void (*destroy)(void*)) { + if (!destroy) return; + bool unwinding = LibcException::globals.uncaught != 0; + try { + while (count) destroy(static_cast(array) + --count * size); + } catch (...) { + if (unwinding) LibcException::Terminate(); + __cxa_vec_cleanup_nid_postfix(array, count, size, destroy); + throw; + } +} +} + +namespace LibcException { +std::size_t ArraySize(std::size_t count, std::size_t size, std::size_t padding) { + if ((padding && padding < sizeof(std::size_t)) || (size && count > (std::numeric_limits::max() - padding) / size)) + __cxa_throw_bad_array_new_length_nid_postfix(); + return count * size + padding; +} +void* ArrayAllocate(std::size_t size) { + void* result = std::malloc(size ? size : 1); + if (!result) _ZSt11_Xbad_allocv_nid_postfix(); + return result; +} +template +void* NewArray(std::size_t count, std::size_t size, std::size_t padding, void (*construct)(void*), void (*destroy)(void*), void* (*allocate)(std::size_t), Deallocate deallocate) { + std::size_t total = ArraySize(count, size, padding); + auto* storage = static_cast(allocate(total)); + if (!storage) return nullptr; + auto* array = storage + padding; + if (padding) std::memcpy(array - sizeof(count), &count, sizeof(count)); + try { __cxa_vec_ctor_nid_postfix(array, count, size, construct, destroy); } + catch (...) { deallocate(storage, total); throw; } + return array; +} +template +void DeleteArray(void* array, std::size_t size, std::size_t padding, void (*destroy)(void*), Deallocate deallocate) { + if (!array) return; + auto* storage = static_cast(array) - padding; + std::size_t count = 0; + if (padding) { + if (padding < sizeof(count)) Terminate(); + std::memcpy(&count, static_cast(array) - sizeof(count), sizeof(count)); + } + std::size_t total = ArraySize(count, size, padding); + try { if (padding) __cxa_vec_dtor_nid_postfix(array, count, size, destroy); } + catch (...) { deallocate(storage, total); throw; } + deallocate(storage, total); +} +} + +extern "C" { +void* __cxa_vec_new_nid_postfix(std::size_t n, std::size_t s, std::size_t p, void (*ctor)(void*), void (*dtor)(void*)) { + return LibcException::NewArray(n, s, p, ctor, dtor, LibcException::ArrayAllocate, [](void* ptr, std::size_t) { std::free(ptr); }); +} +void* __cxa_vec_new2_nid_postfix(std::size_t n, std::size_t s, std::size_t p, void (*ctor)(void*), void (*dtor)(void*), void* (*alloc)(std::size_t), void (*dealloc)(void*)) { + return LibcException::NewArray(n, s, p, ctor, dtor, alloc, [dealloc](void* ptr, std::size_t) { dealloc(ptr); }); +} +void* __cxa_vec_new3_nid_postfix(std::size_t n, std::size_t s, std::size_t p, void (*ctor)(void*), void (*dtor)(void*), void* (*alloc)(std::size_t), void (*dealloc)(void*, std::size_t)) { + return LibcException::NewArray(n, s, p, ctor, dtor, alloc, dealloc); +} +void __cxa_vec_delete_nid_postfix(void* array, std::size_t s, std::size_t p, void (*dtor)(void*)) { + LibcException::DeleteArray(array, s, p, dtor, [](void* ptr, std::size_t) { std::free(ptr); }); +} +void __cxa_vec_delete2_nid_postfix(void* array, std::size_t s, std::size_t p, void (*dtor)(void*), void (*dealloc)(void*)) { + LibcException::DeleteArray(array, s, p, dtor, [dealloc](void* ptr, std::size_t) { dealloc(ptr); }); +} +void __cxa_vec_delete3_nid_postfix(void* array, std::size_t s, std::size_t p, void (*dtor)(void*), void (*dealloc)(void*, std::size_t)) { + LibcException::DeleteArray(array, s, p, dtor, dealloc); +} +} diff --git a/core/libs/prx/libc/src/exception/Exports.cpp b/core/libs/prx/libc/src/exception/Exports.cpp new file mode 100644 index 000000000..a4aa368f5 --- /dev/null +++ b/core/libs/prx/libc/src/exception/Exports.cpp @@ -0,0 +1,18 @@ +#include "Unwind.cpp" +#include "Runtime.cpp" +#include "Personality.cpp" +#include "StandardExceptions.cpp" +#include "ArraySupport.cpp" + +asm( +".local __cxa_allocate_exception\n.set __cxa_allocate_exception,__cxa_allocate_exception_nid_postfix\n" +".local __cxa_free_exception\n.set __cxa_free_exception,__cxa_free_exception_nid_postfix\n" +".local __cxa_throw\n.set __cxa_throw,__cxa_throw_nid_postfix\n" +".local __cxa_begin_catch\n.set __cxa_begin_catch,__cxa_begin_catch_nid_postfix\n" +".local __cxa_end_catch\n.set __cxa_end_catch,__cxa_end_catch_nid_postfix\n" +".local __cxa_rethrow\n.set __cxa_rethrow,__cxa_rethrow_nid_postfix\n" +".local __gxx_personality_v0\n.set __gxx_personality_v0,__gxx_personality_v0_nid_postfix\n" +".local _Unwind_Resume\n.set _Unwind_Resume,_Unwind_Resume_nid_postfix\n" +".local _ZSt9terminatev\n.set _ZSt9terminatev,_ZSt9terminatev_nid_postfix\n" +".local __cxa_call_terminate\n.set __cxa_call_terminate,__cxa_call_terminate_nid_postfix\n" +); diff --git a/core/libs/prx/libc/src/exception/Runtime.cpp b/core/libs/prx/libc/src/exception/Runtime.cpp index 41e2fe459..8ea4aabb9 100644 --- a/core/libs/prx/libc/src/exception/Runtime.cpp +++ b/core/libs/prx/libc/src/exception/Runtime.cpp @@ -5,7 +5,7 @@ namespace LibcException { inline std::atomic terminateHandler {std::abort}; [[noreturn]] void Terminate() { auto handler = terminateHandler.load(std::memory_order_acquire); - if (handler) handler(); + try { if (handler) handler(); } catch (...) {} std::abort(); } @@ -14,13 +14,15 @@ void Release(void* object) { auto* header = FromObject(object); auto* allocation = AllocationOf(header); if (allocation->references.fetch_sub(1, std::memory_order_acq_rel) == 1) { - if (header->destructor) header->destructor(object); + try { if (header->destructor) header->destructor(object); } + catch (...) { Terminate(); } allocation->~Allocation(); std::free(allocation); } } -void Cleanup(_Unwind_Reason_Code, _Unwind_Exception* exception) { +void Cleanup(_Unwind_Reason_Code reason, _Unwind_Exception* exception) { + if (reason != _URC_FOREIGN_EXCEPTION_CAUGHT) Terminate(); auto* header = FromUnwind(exception); if (exception->exception_class == DependentClass) { Release(reinterpret_cast(header->type)); @@ -233,6 +235,10 @@ void* __dynamic_cast_nid_postfix(const void* source, const __cxxabiv1::__class_t return publicSource.count == 1 && publicSource.found == source ? target.found : nullptr; } +[[noreturn]] void __cxa_call_terminate_nid_postfix(void* exception) { + __cxa_begin_catch_nid_postfix(exception); + LibcException::Terminate(); +} [[noreturn]] void __cxa_pure_virtual_nid_postfix() { LibcException::Terminate(); } [[noreturn]] void __cxa_deleted_virtual_nid_postfix() { LibcException::Terminate(); } } diff --git a/core/libs/prx/libc/src/exception/Unwind.cpp b/core/libs/prx/libc/src/exception/Unwind.cpp index d505e7a25..a78fe9e93 100644 --- a/core/libs/prx/libc/src/exception/Unwind.cpp +++ b/core/libs/prx/libc/src/exception/Unwind.cpp @@ -284,8 +284,7 @@ bool Step(_Unwind_Context& context) { return true; } -_Unwind_Reason_Code PhaseTwo(_Unwind_Context context, _Unwind_Exception* exception, bool resume) { - bool skip = resume; +_Unwind_Reason_Code PhaseTwo(_Unwind_Context context, _Unwind_Exception* exception) { for (unsigned depth = 0; depth < 65536; ++depth) { Frame frame; Rules rules; if (!GetRules(context, frame, rules)) { @@ -295,10 +294,7 @@ _Unwind_Reason_Code PhaseTwo(_Unwind_Context context, _Unwind_Exception* excepti } return _URC_END_OF_STACK; } - if (skip) { - if (context.cfa == exception->private_2 && !exception->private_1) skip = false; - else if (exception->private_1 && context.cfa == context.registers[0]) skip = false; - } else { + { auto actions = _UA_CLEANUP_PHASE; if (exception->private_1) { actions = _Unwind_Action(actions | _UA_FORCE_UNWIND); @@ -312,7 +308,13 @@ _Unwind_Reason_Code PhaseTwo(_Unwind_Context context, _Unwind_Exception* excepti if (result != _URC_CONTINUE_UNWIND) return _URC_FATAL_PHASE2_ERROR; } } - if (!Step(context)) return _URC_END_OF_STACK; + if (!Step(context)) { + if (exception->private_1) { + auto stop = reinterpret_cast<_Unwind_Stop_Fn>(exception->private_1); + return stop(1, _Unwind_Action(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK), exception->exception_class, exception, &context, reinterpret_cast(exception->private_2)); + } + return _URC_FATAL_PHASE2_ERROR; + } } return _URC_FATAL_PHASE2_ERROR; } @@ -332,7 +334,7 @@ _Unwind_Reason_Code _Unwind_RaiseException_nid_postfix(_Unwind_Exception* except auto result = __gxx_personality_v0_nid_postfix(1, _UA_SEARCH_PHASE, exception->exception_class, exception, &context); if (result == _URC_HANDLER_FOUND) { exception->private_2 = context.cfa; - return LibcUnwind::PhaseTwo(start, exception, false); + return LibcUnwind::PhaseTwo(start, exception); } if (result != _URC_CONTINUE_UNWIND) return _URC_FATAL_PHASE1_ERROR; } @@ -344,8 +346,8 @@ _Unwind_Reason_Code _Unwind_RaiseException_nid_postfix(_Unwind_Exception* except [[noreturn]] void _Unwind_Resume_nid_postfix(_Unwind_Exception* exception) { _Unwind_Context context; LibcCaptureRegisters(context.registers); - if (!LibcUnwind::Step(context) || !LibcUnwind::Step(context)) std::abort(); - LibcUnwind::PhaseTwo(context, exception, false); + if (!LibcUnwind::Step(context)) std::abort(); + LibcUnwind::PhaseTwo(context, exception); std::abort(); } @@ -382,7 +384,7 @@ _Unwind_Reason_Code _Unwind_ForcedUnwind_nid_postfix(_Unwind_Exception* exceptio if (!LibcUnwind::Step(context)) return _URC_FATAL_PHASE2_ERROR; exception->private_1 = reinterpret_cast(stop); exception->private_2 = reinterpret_cast(argument); - return LibcUnwind::PhaseTwo(context, exception, false); + return LibcUnwind::PhaseTwo(context, exception); } _Unwind_Reason_Code _Unwind_Backtrace_nid_postfix(_Unwind_Trace_Fn trace, void* argument) {