The Pedigree Project 0.1
cppsupport.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/Log.h"
21#include "pedigree/kernel/compiler.h"
22#include "pedigree/kernel/core/SlamAllocator.h"
23#include "pedigree/kernel/core/cppsupport.h"
24#include "pedigree/kernel/machine/Trace.h"
25#include "pedigree/kernel/process/Scheduler.h"
26#include "pedigree/kernel/processor/Processor.h"
27#include "pedigree/kernel/processor/VirtualAddressSpace.h"
28#include "pedigree/kernel/processor/types.h"
29#include "pedigree/kernel/utilities/MemoryTracing.h"
30#include "pedigree/kernel/utilities/utility.h"
31
32#if PEDIGREE_HOSTED_DARWIN
33#include <mach-o/getsect.h>
34#include <mach-o/ldsyms.h>
35#endif
36
39#define DEBUG_ALLOCATOR_CHECK_UNDERFLOWS 1
40
41// We need to use __builtin_frame_* with non-zero arguments in some cases here.
42#if __GNUC__ && !defined(__clang__)
43#pragma GCC diagnostic ignored "-Wframe-address"
44#endif
45
46// Required for G++ to link static init/destructors.
47#if !HOSTED
48extern "C" void* __dso_handle;
49#endif
50
51#if !PEDIGREE_HOSTED_DARWIN
52// Defined in the linker.
53extern uintptr_t start_kernel_ctors;
54extern uintptr_t end_kernel_ctors;
55extern uintptr_t start_kernel_dtors;
56extern uintptr_t end_kernel_dtors;
57#endif
58
59#if PEDIGREE_HOSTED_DARWIN
60static void runKernelConstructorSection(const char* section) {
61 unsigned long size = 0;
62 uint8_t* contents = getsectiondata(&_mh_execute_header, "__DATA", section, &size);
63 auto functions = reinterpret_cast<void (**)()>(contents);
64 const size_t count = size / sizeof(*functions);
65 for (size_t i = 0; i < count; ++i) {
66 functions[i]();
67 }
68}
69#endif
70
73void initialiseConstructors() {
74#if PEDIGREE_HOSTED_DARWIN
75 runKernelConstructorSection("__pedigree_init");
76#else
77 // Constructor list is defined in the linker script.
78 // The .ctors section is just an array of function pointers.
79 // iterate through, calling each in turn.
80 uintptr_t* iterator = &start_kernel_ctors;
81 while (iterator < &end_kernel_ctors) {
82 void (*fp)(void) = reinterpret_cast<void (*)(void)>(*iterator);
83 fp();
84 iterator++;
85 }
86#endif
87}
88
89void runKernelDestructors() {
90#if PEDIGREE_HOSTED_DARWIN
91 runKernelConstructorSection("__pedigree_term");
92#else
93 uintptr_t* iterator = &start_kernel_dtors;
94 while (iterator < &end_kernel_dtors) {
95 void (*fp)(void) = reinterpret_cast<void (*)(void)>(*iterator);
96 fp();
97 iterator++;
98 }
99#endif
100}
101
103static bool traceAllocations = MEMORY_TRACING;
104
105void startTracingAllocations() {
106 if constexpr (MEMORY_TRACING) {
107 traceAllocations = true;
108 }
109}
110
111void stopTracingAllocations() {
112 if constexpr (MEMORY_TRACING) {
113 traceAllocations = false;
114 }
115}
116
117void toggleTracingAllocations() {
118 if constexpr (MEMORY_TRACING) {
119 traceAllocations = !traceAllocations;
120 }
121}
122
123void traceAllocation(void* ptr, MemoryTracing::AllocationTrace type, size_t size) {
124 // Don't trace if the feature is completely disabled.
125 if constexpr (!MEMORY_TRACING) {
126 return;
127 }
128
129 // Don't trace if we're not allowed to.
130 if (!traceAllocations)
131 return;
132
133 // Ignore physical allocations just for now.
134 switch (type) {
135 case MemoryTracing::Allocation:
136 case MemoryTracing::Free:
137 case MemoryTracing::Metadata:
138 break;
139 default:
140 return; // ignore
141 }
142
144
146 entry.data.type = type;
147 entry.data.sz = size & 0xFFFFFFFFU;
148 entry.data.ptr = reinterpret_cast<uintptr_t>(ptr);
149 for (size_t i = 0; i < MemoryTracing::num_backtrace_entries; ++i) {
150 entry.data.bt[i] = 0;
151 }
152
153#define BT_FRAME(M, N) \
154 do { \
155 if (M && !entry.data.bt[M - 1]) \
156 break; \
157 void* frame_addr = __builtin_frame_address(N); \
158 if (!(frame_addr && va.isMapped(frame_addr))) { \
159 entry.data.bt[M] = 0; \
160 break; \
161 } \
162 entry.data.bt[M] = reinterpret_cast<uintptr_t>(__builtin_return_address(N)) & 0xFFFFFFFFU; \
163 } while (0)
164
165 // we want to skip the allocate()/free() call and get a little bit of
166 // context
167 if (MemoryTracing::num_backtrace_entries >= 1)
168 BT_FRAME(0, 1);
169 if (MemoryTracing::num_backtrace_entries >= 2)
170 BT_FRAME(1, 2);
171 if (MemoryTracing::num_backtrace_entries >= 3)
172 BT_FRAME(2, 3);
173 if (MemoryTracing::num_backtrace_entries >= 4)
174 BT_FRAME(3, 4);
175 if (MemoryTracing::num_backtrace_entries >= 5)
176 BT_FRAME(4, 5);
177
178 __asm__ __volatile__("pushfq; cli" ::: "memory");
179
180 for (size_t i = 0; i < sizeof entry.buf; ++i) {
181 __asm__ __volatile__("outb %%al, %%dx" ::"Nd"(0x2E8), "a"(entry.buf[i]));
182 }
183
184 __asm__ __volatile__("popf" ::: "memory");
185}
186
193void traceMetadata(NormalStaticString str, void* p1, void* p2) {
194 // Removed for now - this can be provided by scripts/addr2line.py now
195}
196
198extern "C" EXPORTED_PUBLIC void atexit(void (*f)(void*), void* p, void* d);
199void atexit(void (*f)(void*), void* p, void* d) {}
200
203extern "C" EXPORTED_PUBLIC void __cxa_pure_virtual() NORETURN;
204void __cxa_pure_virtual() {
206 TRACE("Pure virtual function call made");
207
208 EMIT_IF(HOSTED) {
209 asm volatile("int $3");
210 }
211
212 FATAL_NOLOCK("Pure virtual function call made");
213}
214
215// The compiler tests byte zero before calling these Itanium ABI entry points.
216#if !HAS_THREAD_SANITIZER
217extern "C" EXPORTED_PUBLIC int __cxa_guard_acquire(uint64_t* guard);
218extern "C" EXPORTED_PUBLIC void __cxa_guard_release(uint64_t* guard);
219extern "C" EXPORTED_PUBLIC void __cxa_guard_abort(uint64_t* guard);
220
221int __cxa_guard_acquire(uint64_t* guard) {
222 auto* bytes = reinterpret_cast<uint8_t*>(guard);
223 while (true) {
224 if (__atomic_load_n(bytes, __ATOMIC_ACQUIRE))
225 return 0;
226
227 uint8_t expected = 0;
228 if (__atomic_compare_exchange_n(bytes + 1, &expected, uint8_t{1}, false, __ATOMIC_ACQUIRE,
229 __ATOMIC_RELAXED)) {
230 // The preceding initializer may have published between our first read
231 // and claiming the now-idle guard.
232 if (__atomic_load_n(bytes, __ATOMIC_ACQUIRE)) {
233 __atomic_store_n(bytes + 1, uint8_t{0}, __ATOMIC_RELEASE);
234 return 0;
235 }
236 return 1;
237 }
238
239#if THREADS
240 if (Processor::isInitialised() >= 2 &&
241 Processor::executionContext() == ExecutionContext::WaitableThread) {
243 } else
244#endif
245 {
246 // Early constructors cannot depend on a scheduler or another local
247 // static. IRQ paths must not reenter their interrupted initializer.
249 }
250 }
251}
252
253void __cxa_guard_release(uint64_t* guard) {
254 auto* bytes = reinterpret_cast<uint8_t*>(guard);
255 __atomic_store_n(bytes, uint8_t{1}, __ATOMIC_RELEASE);
256 __atomic_store_n(bytes + 1, uint8_t{0}, __ATOMIC_RELEASE);
257}
258
259void __cxa_guard_abort(uint64_t* guard) {
260 auto* bytes = reinterpret_cast<uint8_t*>(guard);
261 __atomic_store_n(bytes + 1, uint8_t{0}, __ATOMIC_RELEASE);
262}
263#endif
264
265#ifndef HOSTED_SYSTEM_MALLOC
266#define HOSTED_SYSTEM_MALLOC 0
267#endif
268
269#if HOSTED
270
271#if HOSTED_SYSTEM_MALLOC
272// already using the system malloc so just define our versions as hosted_*
273#define INDIR_MALLOC hosted_malloc
274#define INDIR_CALLOC hosted_calloc
275#define INDIR_FREE hosted_free
276#define INDIR_REALLOC hosted_realloc
277#else
278#define INDIR_MALLOC _malloc
279#define INDIR_CALLOC _calloc
280#define INDIR_FREE _free
281#define INDIR_REALLOC _realloc
282#endif // HOSTED_SYSTEM_MALLOC != 0
283
284#else
285#define INDIR_MALLOC malloc
286#define INDIR_CALLOC calloc
287#define INDIR_FREE free
288#define INDIR_REALLOC realloc
289#endif // HOSTED
290
291#if HOSTED
292#define PEDIGREE_ALLOCATOR_NOTHROW
293#else
294#define PEDIGREE_ALLOCATOR_NOTHROW C_NOTHROW
295#endif
296
297extern "C" PEDIGREE_ALLOCATOR_NOTHROW void* INDIR_MALLOC(size_t sz) {
298 return reinterpret_cast<void*>(new uint8_t[sz]);
299}
300
301extern "C" PEDIGREE_ALLOCATOR_NOTHROW void* INDIR_CALLOC(size_t num, size_t sz) {
302 void* result = reinterpret_cast<void*>(new uint8_t[num * sz]);
303 ByteSet(result, 0, num * sz);
304 return result;
305}
306
307extern "C" PEDIGREE_ALLOCATOR_NOTHROW void INDIR_FREE(void* p) {
308 if (p == 0)
309 return;
310 // SlamAllocator::instance().free(reinterpret_cast<uintptr_t>(p));
311 delete[] reinterpret_cast<uint8_t*>(p);
312}
313
314extern "C" PEDIGREE_ALLOCATOR_NOTHROW void* INDIR_REALLOC(void* p, size_t sz) {
315 if (p == 0)
316 return INDIR_MALLOC(sz);
317 if (sz == 0) {
318 INDIR_FREE(p);
319 return 0;
320 }
321
322 // Don't attempt to read past the end of the source buffer if we can help it
323 size_t copySz = SlamAllocator::instance().allocSize(reinterpret_cast<uintptr_t>(p));
324 if (copySz > sz)
325 copySz = sz;
326
328 void* tmp = INDIR_MALLOC(sz);
329 MemoryCopy(tmp, p, copySz);
330 INDIR_FREE(p);
331
332 return tmp;
333}
334
335#undef PEDIGREE_ALLOCATOR_NOTHROW
336
337EXPORTED_PUBLIC void* operator new(size_t, void* memory) noexcept {
338 return memory;
339}
340EXPORTED_PUBLIC void* operator new[](size_t, void* memory) noexcept {
341 return memory;
342}
343EXPORTED_PUBLIC void operator delete(void*, void*) noexcept {}
344EXPORTED_PUBLIC void operator delete[](void*, void*) noexcept {}
345
346#if !HOSTED_SYSTEM_MALLOC
347namespace std {
348enum class align_val_t : size_t {};
349}
350
351void* operator new(size_t size) {
352 void* ret = reinterpret_cast<void*>(SlamAllocator::instance().allocate(size));
353 return ret;
354}
355void* operator new[](size_t size) {
356 void* ret = reinterpret_cast<void*>(SlamAllocator::instance().allocate(size));
357 return ret;
358}
359void* operator new(size_t size, std::align_val_t align) {
361 void* ret = reinterpret_cast<void*>(SlamAllocator::instance().allocate(size));
362 return ret;
363}
364static void delete_shared(void* p) noexcept {
365 if (p == 0)
366 return;
367 uintptr_t mem = reinterpret_cast<uintptr_t>(p);
368 // We want to attempt to delete even if this is not a valid pointer if
369 // allocations are being traced, so we can catch the bad free and get a
370 // backtrace for it.
371 if (traceAllocations || SlamAllocator::instance().isPointerValid(mem)) {
372 SlamAllocator::instance().free(mem);
373 } else {
374 if (SlamAllocator::instance().isWithinHeap(mem)) {
375 FATAL("delete_shared failed as pointer was invalid: " << p);
376 } else {
377 // less critical - still annoying
378 PEDANTRY("delete_shared failed as pointer was not in the kernel heap: " << p);
379 }
380 }
381}
382void operator delete(void* p) noexcept {
383 delete_shared(p);
384}
385void operator delete[](void* p) noexcept {
386 delete_shared(p);
387}
388void operator delete(void* p, size_t sz) noexcept {
389 delete_shared(p);
390}
391void operator delete(void* p, std::align_val_t align) noexcept {
392 delete_shared(p);
393}
394void operator delete(void* p, size_t sz, std::align_val_t align) noexcept {
395 delete_shared(p);
396}
397void operator delete[](void* p, size_t sz) noexcept {
398 delete_shared(p);
399}
400#endif
401
402#if HOSTED && !HOSTED_SYSTEM_MALLOC
403extern "C" {
404void* __wrap_malloc(size_t sz) {
405 return INDIR_MALLOC(sz);
406}
407
408void* __wrap_realloc(void* p, size_t sz) {
409 return INDIR_REALLOC(p, sz);
410}
411
412void __wrap_free(void* p) {
413 return INDIR_FREE(p);
414}
415}
416#endif
static void pause()
static ExecutionContext executionContext()
Definition Processor.cc:109
static size_t isInitialised()
Definition Processor.cc:105
static Scheduler & instance()
Definition Scheduler.h:96
void yield()
Definition Scheduler.cc:236
uintptr_t allocate(size_t nBytes)
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
Definition mem.c:283