summaryrefslogtreecommitdiff
path: root/kernel/lib/cxxabi.cpp
blob: 6d422e3e44d08f4c70f94b9ed3de9b2f098bcbb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
 * 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>
#include <kernel/kernel.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() {
    kernel::GetKernel().Panic("pure virtual function called");
}

/*
 * 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.
 * Guarded to avoid conflict with <new> header's placement new. */
#ifndef _LIBBASTION_PLACEMENT_NEW_DEFINED
#define _LIBBASTION_PLACEMENT_NEW_DEFINED
inline void* operator new(size_t, void* ptr)    noexcept { return ptr; }
inline void* operator new[](size_t, void* ptr)  noexcept { return ptr; }
#endif