summaryrefslogtreecommitdiff
path: root/kernel/lib/cxxabi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/lib/cxxabi.cpp')
-rw-r--r--kernel/lib/cxxabi.cpp85
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; }