Skip to content

Crash (0xC0000005) when building mimalloc as a DLL with Intel ICX compiler on Windows #1268

@GoldJohnKing

Description

@GoldJohnKing

Environment

  • Compiler: Intel oneAPI DPC++/C++ Compiler (ICX), which is LLVM/Clang-based and defines both _MSC_VER and __clang__
  • Platform: Windows x64
  • Build: mimalloc as a shared library (MI_SHARED_LIB=1) with malloc override (MI_MALLOC_OVERRIDE=1)
  • MSVC: Works correctly (tested with v145 toolset)

Symptom

When mimalloc is compiled as a DLL using Intel ICX, the process crashes immediately at startup:

Exception thrown at 0x00007FFE7F06AA73 (ntdll.dll) in arma3_x64.exe:
0xC0000005: Access violation writing location 0x0000000000000024.

The call stack shows only ntdll.dll and kernel32.dll frames — no user code frames are visible.

Defining MI_WIN_NO_RAW_DLLMAIN eliminates the crash entirely.

Root Cause

When MI_WIN_NO_RAW_DLLMAIN is not defined, mimalloc uses the CRT's _pRawDllMain mechanism for DLL initialization:

// src/prim/windows/prim.c
static BOOL NTAPI mi_dll_main_raw(PVOID module, DWORD reason, LPVOID reserved) {
    mi_win_main(module, reason, reserved);
    return TRUE;
}

extern "C" PVOID _pRawDllMain = &mi_dll_main_raw;

The CRT's DllMainCRTStartup reads _pRawDllMain and calls it before any other CRT initialization. This is the point where mimalloc initializes its thread-local default heap (_mi_heap_default), switching it from _mi_heap_empty (whose tld field is NULL) to _mi_heap_main (which has a valid tld).

ICX issues a warning on this line:

warning: implicit conversion between pointer-to-function and pointer-to-object
is a Microsoft extension [-Wmicrosoft-cast]

More critically, the ICX-compiled DLL does not appear to deliver _pRawDllMain to the CRT correctly. As a result, the CRT never calls mi_dll_main_raw, and the entire initialization chain (mi_win_mainmi_win_tls_init_mi_auto_process_initmi_process_init) is skipped.

The subsequent crash at address 0x24 is a NULL-pointer dereference through _mi_heap_empty.tld:

Structure Field Offset
mi_tld_t segments (starts at offset 0x20) 0x20
mi_segments_tld_t spans[0] (+ 0x04 into segments) 0x24

Any allocation attempt dereferences heap->tld->segments... → writes to NULL + 0x240xC0000005.

Workaround

Define MI_WIN_NO_RAW_DLLMAIN in the preprocessor definitions. This makes mimalloc use an explicit DllMain entry point instead of _pRawDllMain, which works correctly with ICX:

#elif defined(MI_SHARED_LIB)
  BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
    mi_win_main((PVOID)inst,reason,reserved);
    return TRUE;
  }

Suggested Fix

Auto-detect ICX/Clang when building as a shared library on Windows and use DllMain instead of _pRawDllMain. The following can be added before the #ifndef MI_WIN_NO_RAW_DLLMAIN check in src/prim/windows/prim.c:

#if defined(__clang__) && defined(MI_SHARED_LIB) && !defined(MI_WIN_NO_RAW_DLLMAIN)
  #define MI_WIN_NO_RAW_DLLMAIN 1
#endif

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions