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
84
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; }
|