diff options
Diffstat (limited to 'kernel/lib/cxxabi.cpp')
| -rw-r--r-- | kernel/lib/cxxabi.cpp | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/kernel/lib/cxxabi.cpp b/kernel/lib/cxxabi.cpp new file mode 100644 index 0000000..e38813f --- /dev/null +++ b/kernel/lib/cxxabi.cpp @@ -0,0 +1,85 @@ +/* + * kernel/lib/cxxabi.cpp + * + * C++ ABI runtime support for a freestanding kernel. + * + * The compiler emits calls to these functions for: + * - Pure virtual function calls (__cxa_pure_virtual) + * - Global object destructors (__cxa_atexit, __dso_handle) + * - new/delete operators + * + * Compile with -fno-exceptions -fno-rtti, so we don't need + * exception handling or typeinfo support. + */ + +#include <stddef.h> +#include <stdint.h> + +extern "C" { + +/* + * Called when a pure virtual function is invoked. + * This should never happen in correct code — it's a fatal error. + */ +[[noreturn]] void __cxa_pure_virtual() { + /* TODO: call kernel_panic("pure virtual function called") */ + for (;;) { + #if defined(__x86_64__) + asm volatile("hlt"); + #elif defined(__aarch64__) + asm volatile("wfi"); + #endif + } +} + +/* + * Static local variable guards (thread-safe init in the Itanium ABI). + * + * The ABI stores the "initialized" flag in the least-significant byte of the + * 64-bit guard variable (Itanium C++ ABI 3.3.2 and 3.3.3 ). On little-endian targets + * (x86_64, AArch64) that byte is at the lowest address, so casting the + * guard pointer to uint8_t* and reading/writing byte 0 is correct. + * + * In a single-CPU kernel with no preemption, the full acquire/release + * protocol is unnecessary — we just check and set the byte. + */ +int __cxa_guard_acquire(uint64_t* guard) { + return !(*reinterpret_cast<const uint8_t*>(guard)); +} + +void __cxa_guard_release(uint64_t* guard) { + *reinterpret_cast<uint8_t*>(guard) = 1; +} + +void __cxa_guard_abort(uint64_t* guard) { + (void)guard; +} + +/* + * atexit support — for global object destructors. + * In a kernel, we never "exit", so this is a no-op. + */ +int __cxa_atexit(void (*)(void*), void*, void*) { + return 0; +} + +void* __dso_handle = nullptr; + +} /* extern "C" */ + +/* + * Placement new — always needed. + * Sized new/delete — routed through kernel heap once available (Phase 3). + */ + +// [[nodiscard]]: ignoring the returned pointer from new is always a bug. +[[nodiscard]] void* operator new(size_t size) { (void)size; return nullptr; /* TODO: kmalloc */ } +[[nodiscard]] void* operator new[](size_t size) { (void)size; return nullptr; /* TODO: kmalloc */ } +void operator delete(void* ptr) noexcept { (void)ptr; /* TODO: kfree */ } +void operator delete[](void* ptr) noexcept { (void)ptr; /* TODO: kfree */ } +void operator delete(void* ptr, size_t) noexcept { (void)ptr; /* TODO: kfree */ } +void operator delete[](void* ptr, size_t) noexcept { (void)ptr; /* TODO: kfree */ } + +/* Placement new — doesn't allocate, just returns the pointer */ +inline void* operator new(size_t, void* ptr) noexcept { return ptr; } +inline void* operator new[](size_t, void* ptr) noexcept { return ptr; } |
