From 6bfb1c656c6da4eaa8dab4d8fc0197f1ed946483 Mon Sep 17 00:00:00 2001 From: daanx Date: Fri, 2 May 2025 08:40:21 -0700 Subject: [PATCH 1/4] allow size==0 for mi_prim_free (issue #1041) --- src/os.c | 14 +++++++------- src/prim/emscripten/prim.c | 4 ++-- src/prim/unix/prim.c | 5 +++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/os.c b/src/os.c index 894e3a45..be7e532c 100644 --- a/src/os.c +++ b/src/os.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -167,8 +167,8 @@ static void mi_os_free_huge_os_pages(void* p, size_t size); static void mi_os_prim_free(void* addr, size_t size, size_t commit_size) { mi_assert_internal((size % _mi_os_page_size()) == 0); - if (addr == NULL || size == 0) return; // || _mi_os_is_huge_reserved(addr) - int err = _mi_prim_free(addr, size); + if (addr == NULL) return; // || _mi_os_is_huge_reserved(addr) + int err = _mi_prim_free(addr, size); // allow size==0 (issue #1041) if (err != 0) { _mi_warning_message("unable to free OS memory (error: %d (0x%x), size: 0x%zx bytes, address: %p)\n", err, err, size, addr); } @@ -186,10 +186,10 @@ void _mi_os_free_ex(void* addr, size_t size, bool still_committed, mi_memid_t me void* base = addr; // different base? (due to alignment) if (memid.mem.os.base != base) { - mi_assert(memid.mem.os.base <= addr); + mi_assert(memid.mem.os.base <= addr); base = memid.mem.os.base; const size_t diff = (uint8_t*)addr - (uint8_t*)memid.mem.os.base; - if (memid.mem.os.size==0) { + if (memid.mem.os.size==0) { csize += diff; } if (still_committed) { @@ -733,8 +733,8 @@ static int mi_os_numa_node_get(void) { } int _mi_os_numa_node(void) { - if mi_likely(mi_atomic_load_relaxed(&mi_numa_node_count) == 1) { - return 0; + if mi_likely(mi_atomic_load_relaxed(&mi_numa_node_count) == 1) { + return 0; } else { return mi_os_numa_node_get(); diff --git a/src/prim/emscripten/prim.c b/src/prim/emscripten/prim.c index 82147de7..a8677cbc 100644 --- a/src/prim/emscripten/prim.c +++ b/src/prim/emscripten/prim.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen, Alon Zakai +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen, Alon Zakai This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -58,7 +58,7 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config) { extern void emmalloc_free(void*); int _mi_prim_free(void* addr, size_t size) { - MI_UNUSED(size); + if (size==0) return 0; emmalloc_free(addr); return 0; } diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index e3888e73..f93e458a 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -1,5 +1,5 @@ /* ---------------------------------------------------------------------------- -Copyright (c) 2018-2023, Microsoft Research, Daan Leijen +Copyright (c) 2018-2025, Microsoft Research, Daan Leijen This is free software; you can redistribute it and/or modify it under the terms of the MIT license. A copy of the license can be found in the file "LICENSE" at the root of this distribution. @@ -70,7 +70,7 @@ terms of the MIT license. A copy of the license can be found in the file #define MADV_FREE POSIX_MADV_FREE #endif - + //------------------------------------------------------------------------------------ // Use syscalls for some primitives to allow for libraries that override open/read/close etc. // and do allocation themselves; using syscalls prevents recursion when mimalloc is @@ -186,6 +186,7 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config ) //--------------------------------------------- int _mi_prim_free(void* addr, size_t size ) { + if (size==0) return 0; bool err = (munmap(addr, size) == -1); return (err ? errno : 0); } From 2c34250f43c0c06cdc3c405781eed90daf008361 Mon Sep 17 00:00:00 2001 From: daanx Date: Fri, 2 May 2025 08:55:16 -0700 Subject: [PATCH 2/4] extend override test on windows --- test/main-override-dep.cpp | 38 +++++++++++++++++++++++++++++++++++++- test/main-override.cpp | 5 +++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/test/main-override-dep.cpp b/test/main-override-dep.cpp index e92f6fc4..d89e3fca 100644 --- a/test/main-override-dep.cpp +++ b/test/main-override-dep.cpp @@ -1,6 +1,7 @@ // Issue #981: test overriding allocation in a DLL that is compiled independent of mimalloc. // This is imported by the `mimalloc-test-override` project. #include +#include #include "main-override-dep.h" std::string TestAllocInDll::GetString() @@ -10,6 +11,41 @@ std::string TestAllocInDll::GetString() const char* t = "test"; memcpy(test, t, 4); std::string r = test; + std::cout << "override-dep: GetString: " << r << "\n"; delete[] test; return r; -} \ No newline at end of file +} + + +class Static { +private: + void* p; +public: + Static() { + printf("override-dep: static constructor\n"); + p = malloc(64); + return; + } + ~Static() { + free(p); + printf("override-dep: static destructor\n"); + return; + } +}; + +static Static s = Static(); + + +#include + +BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) { + (void)(reserved); + (void)(module); + if (reason==DLL_PROCESS_ATTACH) { + printf("override-dep: dll attach\n"); + } + else if (reason==DLL_PROCESS_DETACH) { + printf("override-dep: dll detach\n"); + } + return TRUE; +} diff --git a/test/main-override.cpp b/test/main-override.cpp index db594acc..576f47bc 100644 --- a/test/main-override.cpp +++ b/test/main-override.cpp @@ -37,7 +37,7 @@ static void test_thread_local(); // issue #944 static void test_mixed1(); // issue #942 static void test_stl_allocators(); -#if x_WIN32 +#if _WIN32 #include "main-override-dep.h" static void test_dep(); // issue #981: test overriding in another DLL #else @@ -145,11 +145,12 @@ static bool test_stl_allocator1() { struct some_struct { int i; int j; double z; }; -#if x_WIN32 +#if _WIN32 static void test_dep() { TestAllocInDll t; std::string s = t.GetString(); + std::cout << "test_dep GetString: " << s << "\n"; } #endif From f3e92b1edd851a4d1e2e2c4bbada87f2855dc834 Mon Sep 17 00:00:00 2001 From: daanx Date: Fri, 2 May 2025 16:12:20 -0700 Subject: [PATCH 3/4] Use second user TLS slot to avoid using reserved fields in the TEB (issue #1078) --- include/mimalloc/prim.h | 15 ++++++++------- src/prim/windows/prim.c | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/include/mimalloc/prim.h b/include/mimalloc/prim.h index bddd66e9..b0ddc2d0 100644 --- a/include/mimalloc/prim.h +++ b/include/mimalloc/prim.h @@ -208,19 +208,20 @@ static inline void mi_prim_tls_slot_set(size_t slot, void* value) mi_attr_noexce #elif _WIN32 && MI_WIN_USE_FIXED_TLS && !defined(MI_WIN_USE_FLS) // On windows we can store the thread-local heap at a fixed TLS slot to avoid -// thread-local initialization checks in the fast path. This uses a fixed location -// in the TCB though (last user-reserved slot by default) which may clash with other applications. - +// thread-local initialization checks in the fast path. +// We always use the second user TLS slot (the first one is always allocated already), +// and at initialization (`windows/prim.c`) we call TlsAlloc and verify +// we indeed get the second slot (and fail otherwise). +// Todo: we could make the Tls slot completely dynamic but that would require +// an extra read of the static Tls slot instead of using a constant offset. #define MI_HAS_TLS_SLOT 2 // 2 = we can reliably initialize the slot (saving a test on each malloc) #if MI_WIN_USE_FIXED_TLS > 1 #define MI_TLS_SLOT (MI_WIN_USE_FIXED_TLS) #elif MI_SIZE_SIZE == 4 -#define MI_TLS_SLOT (0x710) // Last user-reserved slot -// #define MI_TLS_SLOT (0xF0C) // Last TlsSlot (might clash with other app reserved slot) +#define MI_TLS_SLOT (0x0E18) // Second User TLS slot #else -#define MI_TLS_SLOT (0x888) // Last user-reserved slot -// #define MI_TLS_SLOT (0x1678) // Last TlsSlot (might clash with other app reserved slot) +#define MI_TLS_SLOT (0x1488) // Second User TLS slot #endif static inline void* mi_prim_tls_slot(size_t slot) mi_attr_noexcept { diff --git a/src/prim/windows/prim.c b/src/prim/windows/prim.c index a080f4bc..7daa09ef 100644 --- a/src/prim/windows/prim.c +++ b/src/prim/windows/prim.c @@ -631,8 +631,20 @@ static void NTAPI mi_win_main(PVOID module, DWORD reason, LPVOID reserved) { MI_UNUSED(reserved); MI_UNUSED(module); #if MI_TLS_SLOT >= 2 - if ((reason==DLL_PROCESS_ATTACH || reason==DLL_THREAD_ATTACH) && mi_prim_get_default_heap() == NULL) { - _mi_heap_set_default_direct((mi_heap_t*)&_mi_heap_empty); + if (reason==DLL_PROCESS_ATTACH) { + const DWORD tls_slot = TlsAlloc(); + if (tls_slot != 1) { + _mi_error_message(EFAULT, "unable to allocate the second TLS slot (rebuild without MI_WIN_USE_FIXED_TLS?)\n"); + } + } + if (reason==DLL_PROCESS_ATTACH || reason==DLL_THREAD_ATTACH) { + if (mi_prim_get_default_heap() == NULL) { + _mi_heap_set_default_direct((mi_heap_t*)&_mi_heap_empty); + } + #if MI_DEBUG + void* const p = TlsGetValue(1); + mi_assert_internal(p == (void*)&_mi_heap_empty); + #endif } #endif if (reason==DLL_PROCESS_ATTACH) { From 417e8176bddce86c2d53656c5552f5bb96304c46 Mon Sep 17 00:00:00 2001 From: daanx Date: Fri, 2 May 2025 16:27:00 -0700 Subject: [PATCH 4/4] add unix large page size constant and adjust aligment to the large page size for large allocations --- src/prim/unix/prim.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/prim/unix/prim.c b/src/prim/unix/prim.c index f93e458a..f3ccb013 100644 --- a/src/prim/unix/prim.c +++ b/src/prim/unix/prim.c @@ -70,6 +70,7 @@ terms of the MIT license. A copy of the license can be found in the file #define MADV_FREE POSIX_MADV_FREE #endif +#define MI_UNIX_LARGE_PAGE_SIZE (2*MI_MiB) // TODO: can we query the OS for this? //------------------------------------------------------------------------------------ // Use syscalls for some primitives to allow for libraries that override open/read/close etc. @@ -156,7 +157,7 @@ void _mi_prim_mem_init( mi_os_mem_config_t* config ) } #endif } - config->large_page_size = 2*MI_MiB; // TODO: can we query the OS for this? + config->large_page_size = MI_UNIX_LARGE_PAGE_SIZE; config->has_overcommit = unix_detect_overcommit(); config->has_partial_free = true; // mmap can free in parts config->has_virtual_reserve = true; // todo: check if this true for NetBSD? (for anonymous mmap with PROT_NONE) @@ -386,6 +387,9 @@ int _mi_prim_alloc(void* hint_addr, size_t size, size_t try_alignment, bool comm mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); mi_assert_internal(commit || !allow_large); mi_assert_internal(try_alignment > 0); + if (hint_addr == NULL && size >= 8*MI_UNIX_LARGE_PAGE_SIZE && try_alignment > 1 && _mi_is_power_of_two(try_alignment) && try_alignment < MI_UNIX_LARGE_PAGE_SIZE) { + try_alignment = MI_UNIX_LARGE_PAGE_SIZE; // try to align along large page size for larger allocations + } *is_zero = true; int protect_flags = (commit ? (PROT_WRITE | PROT_READ) : PROT_NONE); @@ -433,7 +437,7 @@ int _mi_prim_decommit(void* start, size_t size, bool* needs_recommit) { int err = 0; // decommit: use MADV_DONTNEED as it decreases rss immediately (unlike MADV_FREE) err = unix_madvise(start, size, MADV_DONTNEED); - #if !MI_DEBUG && !MI_SECURE + #if !MI_DEBUG && MI_SECURE<=2 *needs_recommit = false; #else *needs_recommit = true;