ZXFoundation™ 26h2
Loading...
Searching...
No Matches
cxxabi.cxxm
Go to the documentation of this file.
1/// SPDX-License-Identifier: Apache-2.0
2/// @file cxxabi.cxxm
3/// @brief minimal cxxabi implementation
4
5export module zxfoundation.base.cxxrtse.cxxabi;
6import zxfoundation.base.types;
7import zxfoundation.sys.syschk.core;
8import zxfoundation.sys.printk.core;
9import zxfoundation.sync.qspinlock.core;
10import arch.s390x.cpu.processor;
11import arch.s390x.cpu.clock;
12import arch.s390x.cpu.sync;
13import lib.static_vector;
14import lib.error;
15import std;
16
17using zxfoundation::sys::printk::pinfo;
18
19namespace {
20
21constexpr usize CXA_ATEXIT_MAX = 256;
22
23struct AtExitEntry {
24 void (*destructor)(void*);
25 void* obj;
26 void* dso;
27};
28
29lib::static_vector<AtExitEntry, CXA_ATEXIT_MAX> g_atexit_table{};
30
31alignas(4) zxfoundation::sync::qspinlock::qspinlock g_atexit_lock{};
32auto atexit_lock() noexcept -> void { g_atexit_lock.lock(); }
33auto atexit_unlock() noexcept -> void { g_atexit_lock.unlock(); }
34
35} // anonymous namespace
36
37no_mangle {
38
39extern void (*__init_array_start[])() __attribute__((weak));
40
41extern void (*__init_array_end[])() __attribute__((weak));
42
43extern void (*__fini_array_start[])() __attribute__((weak));
44
45extern void (*__fini_array_end[])() __attribute__((weak));
46
47extern void *__dso_handle __attribute__((weak));
48
49}
50
51no_mangle {
52
53auto __cxa_atexit(void (*destructor)(void*), void* obj, void* dso) -> int {
54 if (!destructor) return 0;
55 atexit_lock();
56 const bool ok = g_atexit_table.push_back({ destructor, obj, dso }).has_value();
57 atexit_unlock();
58 if (!ok) [[unlikely]] zxfoundation::sys::syschk::syschk_fatal(lib::kernel_error::from_generic(lib::generic_error::internal_error),
59 "cxxrtse: __cxa_atexit: oom trying to handle atexit.");
60 return 0;
61}
62
63auto __cxa_finalize(void* dso) -> void {
64 atexit_lock();
65 const usize snap = g_atexit_table.size();
66 atexit_unlock();
67
68 for (long i = static_cast<long>(snap) - 1; i >= 0; --i) {
69 atexit_lock();
70 AtExitEntry e = g_atexit_table[static_cast<usize>(i)];
71 if (!e.destructor || (dso && e.dso != dso)) { atexit_unlock(); continue; }
72 g_atexit_table[static_cast<usize>(i)].destructor = nullptr;
73 atexit_unlock();
74 e.destructor(e.obj);
75 }
76}
77
78static constexpr u64 GUARD_INITIALIZED = 1ULL << 0;
79static constexpr u64 GUARD_IN_PROGRESS = 1ULL << 1;
80
81auto __cxa_guard_acquire(u64* g) -> int {
82 if (__atomic_load_n(g, __ATOMIC_ACQUIRE) & GUARD_INITIALIZED) return 0;
83
84 u64 expected = 0;
85 if (__atomic_compare_exchange_n(g, &expected,
86 GUARD_IN_PROGRESS,
87 false,
88 __ATOMIC_ACQUIRE,
89 __ATOMIC_RELAXED)) {
90 return 1; // We won — caller must initialize.
91 }
92
93 while (!(__atomic_load_n(g, __ATOMIC_ACQUIRE) & GUARD_INITIALIZED))
94 arch::s390x::cpu::sync::cpu_relax();
95 return 0;
96}
97
98auto __cxa_guard_release(u64* g) -> void {
99 __atomic_store_n(g, GUARD_INITIALIZED, __ATOMIC_RELEASE);
100}
101
102auto __cxa_guard_abort(u64* g) -> void {
103 __atomic_store_n(g, 0ULL, __ATOMIC_RELEASE);
104}
105
106[[noreturn]] auto __cxa_pure_virtual() -> void {
107 zxfoundation::sys::syschk::syschk_fatal(
108 lib::kernel_error::from_generic(lib::generic_error::code::internal_error),
109 "cxxabi: __cxa_pure_virtual(): call to pure virtual function. "
110 "Object is being used before its derived constructor completed "
111 "or the vtable is corrupt."
112 );
113}
114
115[[noreturn]] auto __cxa_deleted_virtual() -> void {
116 zxfoundation::sys::syschk::syschk_fatal(
117 lib::kernel_error::from_generic(lib::generic_error::code::internal_error),
118 "cxxabi: __cxa_deleted_virtual(): call to explicitly deleted virtual function. "
119 "The vtable is corrupt or an invalid base pointer was used."
120 );
121}
122
123[[noreturn]] auto __cxa_bad_typeid() -> void {
124 zxfoundation::sys::syschk::syschk_fatal(
125 lib::kernel_error::from_generic(lib::generic_error::code::internal_error),
126 "cxxabi: __cxa_bad_typeid(): typeid applied to a null pointer. "
127 "A pre-compiled object file likely contains RTTI usage."
128 );
129}
130
131[[noreturn]] auto __cxa_throw(void *, void *, void (*)(void *)) -> void {
132 zxfoundation::sys::syschk::syschk_fatal(
133 lib::kernel_error::from_generic(lib::generic_error::code::internal_error),
134 "cxxabi: __cxa_throw(3): C++ exception thrown in a no-exception kernel context. "
135 "A pre-compiled object file likely "
136 "contains exception-throwing code that is incompatible with FoundationKit."
137 );
138}
139
140[[noreturn]] auto __cxa_rethrow() -> void {
141 zxfoundation::sys::syschk::syschk_fatal(
142 lib::kernel_error::from_generic(lib::generic_error::code::internal_error),
143 "cxxabi: __cxa_rethrow(): rethrow attempted in a no-exception context. "
144 );
145}
146
147auto __cxa_begin_catch(void*) -> void* {
148 zxfoundation::sys::syschk::syschk_fatal(
149 lib::kernel_error::from_generic(lib::generic_error::code::internal_error),
150 "cxxabi: __cxa_begin_catch(): catch block entered in a no-exception context."
151 );
152}
153
154auto __cxa_end_catch() -> void {
155 zxfoundation::sys::syschk::syschk_fatal(
156 lib::kernel_error::from_generic(lib::generic_error::code::internal_error),
157 "cxxabi: __cxa_end_catch(): end-catch reached in a no-exception context."
158 );
159}
160
161auto __cxa_allocate_exception(u64) -> void* {
162 zxfoundation::sys::syschk::syschk_fatal(
163 lib::kernel_error::from_generic(lib::generic_error::code::internal_error),
164 "cxxabi: __cxa_allocate_exception(): exception object allocation attempted "
165 "in a no-exception context."
166 );
167}
168
169auto __cxa_free_exception(void*) -> void* {
170 zxfoundation::sys::syschk::syschk_fatal(
171 lib::kernel_error::from_generic(lib::generic_error::code::internal_error),
172 "cxxabi: __cxa_free_exception(): exception object deallocation attempted "
173 "in a no-exception context."
174 );
175}
176
177/// @brief Global stack-protector guard value read by every `-fstack-protector`
178/// function prologue/epilogue.
179u64 __stack_chk_guard = 0x595A465353500001ULL; // "ZXFSSP" + version tag
180
181[[gnu::no_stack_protector, noreturn]] auto __stack_chk_fail() -> void {
182 zxfoundation::sys::syschk::syschk_fatal(
183 lib::kernel_error::from_memory(lib::memory_error::ssp_check_fail),
184 "cxxrtse: stack smashing detected"
185 );
186 __builtin_unreachable();
187}
188
189} // no_mangle
190
191export {
192
193namespace zxfoundation::base::cxxrtse::cxxabi {
194
195 /// @brief Install the kernel-wide stack-protector guard entropy.
196 /// @note One value is computed once on the BSP and used kernel-wide; APs do
197 /// not recompute it. This is what makes the guard migration-safe
198 /// across CPUs (see the `__stack_chk_guard` rationale above).
199 [[gnu::no_stack_protector]] void ssp_init() noexcept {
200 __stack_chk_guard =
201 arch::s390x::cpu::clock::stck() ^ 0xC0FFEE00FACADE42ULL;
202 }
203
204 /// @brief Read the active kernel-wide stack-protector guard value.
205 /// @return The current `__stack_chk_guard`.
206 [[nodiscard]] auto stack_guard() noexcept -> u64 {
207 return __stack_chk_guard;
208 }
209
210 void run_global_constructor() noexcept {
211 if (&__init_array_end == nullptr) [[unlikely]] {
212 pinfo("cxxabi: __init_array_start or __init_array_end "
213 "symbols are missing. No global constructors will be called. "
214 "Check the kernel linker script.");
215 return;
216 }
217
218 const auto count = static_cast<usize>(
219 __init_array_end - __init_array_start);
220
221 pinfo("cxxabi: running {} global constructors.", count);
222
223 for (usize i = 0; i < count; ++i) {
224 void (*fn)() = __init_array_start[i];
225 if (fn == nullptr) [[unlikely]] {
226 pinfo("cxxabi: null constructor pointer at index {}. "
227 "Possible linker script alignment issue. Skipping.", i);
228 continue;
229 }
230 fn();
231 }
232
233 pinfo("cxxabi: all constructors completed.");
234 }
235
236 void run_fini_array() noexcept {
237 if (!&__fini_array_end) [[unlikely]] {
238 pinfo("cxxabi: __fini_array_start or __fini_array_end "
239 "symbols are missing. No fini_array functions will be called.");
240 return;
241 }
242
243 const auto count = static_cast<usize>(
244 __fini_array_end - __fini_array_start);
245
246 pinfo("cxxabi: running {} fini_array entries (reverse order).", count);
247
248 // Reverse walk (LIFO).
249 for (usize i = count; i > 0; --i) {
250 void (*fn)() = __fini_array_start[i - 1];
251 if (fn == nullptr) [[unlikely]] {
252 pinfo("cxxabi: null fini_array pointer at index {}. Skipping.",
253 i - 1);
254 continue;
255 }
256 fn();
257 }
258
259 pinfo("cxxabi: all fini_array entries completed.");
260 }
261
262 void run_global_destructors() noexcept {
263 pinfo("cxxabi: running all registered atexit destructors.");
264 __cxa_finalize(nullptr);
265 run_fini_array();
266 pinfo("cxxabi: all destructors completed.");
267 }
268
269}
270
271}