From 150837e7e83081d3089b6a82130076d0cdf8a013 Mon Sep 17 00:00:00 2001 From: Andrew Kaster Date: Sun, 22 Dec 2019 02:06:50 -0700 Subject: [PATCH] LibC: Prevent assertions in malloc/free at exit time This is a bit sad, but, with the Allocators as static globals their destructors were running before some user code. Which doesn't really make much sense, as none of the members of (at least the basic one) do any real heavy lifting or have many resources to RAII. To avoid the problem, just mmap the memory for the global arrays of Allocators in __malloc_init and let the Kernel collect the memory when we're done with the process. --- Libraries/LibC/crt0.cpp | 9 +++++++++ Libraries/LibC/malloc.cpp | 18 ++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Libraries/LibC/crt0.cpp b/Libraries/LibC/crt0.cpp index 90f57b75edf..5d03ca9ca72 100644 --- a/Libraries/LibC/crt0.cpp +++ b/Libraries/LibC/crt0.cpp @@ -3,6 +3,8 @@ #include #include +//#define GLOBAL_DTORS_DEBUG + extern "C" { int main(int, char**, char**); @@ -83,11 +85,18 @@ void __cxa_finalize(void* dso_handle) int entry_index = __exit_entry_count; +#ifdef GLOBAL_DTORS_DEBUG + dbgprintf("__cxa_finalize: %d entries in the finalizer list\n", entry_index); +#endif + while (--entry_index >= 0) { auto& exit_entry = __exit_entries[entry_index]; bool needs_calling = !exit_entry.has_been_called && (!dso_handle || dso_handle == exit_entry.dso_handle); if (needs_calling) { +#ifdef GLOBAL_DTORS_DEBUG + dbgprintf("__cxa_finalize: calling entry[%d] %p(%p) dso: %p\n", entry_index, exit_entry.method, exit_entry.parameter, exit_entry.dso_handle); +#endif exit_entry.method(exit_entry.parameter); exit_entry.has_been_called = true; } diff --git a/Libraries/LibC/malloc.cpp b/Libraries/LibC/malloc.cpp index df4a547e106..ae508528bc1 100644 --- a/Libraries/LibC/malloc.cpp +++ b/Libraries/LibC/malloc.cpp @@ -99,8 +99,9 @@ struct BigAllocator { Vector blocks; }; -static Allocator g_allocators[num_size_classes]; -static BigAllocator g_big_allocators[1]; +// Allocators will be mmapped in __malloc_init +Allocator* g_allocators = nullptr; +BigAllocator* g_big_allocators = nullptr; static Allocator* allocator_for_size(size_t size, size_t& good_size) { @@ -369,5 +370,18 @@ void __malloc_init() s_scrub_free = false; if (getenv("LIBC_LOG_MALLOC")) s_log_malloc = true; + + g_allocators = (Allocator*)mmap_with_name(nullptr, sizeof(Allocator) * num_size_classes, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, "LibC Allocators"); + for (size_t i = 0; i < num_size_classes; ++i) { + new (&g_allocators[i]) Allocator(); + g_allocators[i].size = size_classes[i]; + } + + g_big_allocators = (BigAllocator*)mmap_with_name(nullptr, sizeof(BigAllocator), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0, "LibC BigAllocators"); + new (g_big_allocators) (BigAllocator); + + // We could mprotect the mmaps here with atexit, but, since this method is called in _start before + // _init and __init_array entries, our mprotect method would always be the last thing run before _exit. } + }