From 85f087317f973f57fe630171062518c7589323bf Mon Sep 17 00:00:00 2001 From: rcj1 Date: Thu, 6 Aug 2026 11:19:04 -0700 Subject: [PATCH 1/8] fix native aot unload on unix --- src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 15 +++++++++++++++ .../SmokeTests/SharedLibrary/SharedLibrary.cpp | 3 +-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index c6fa26c221a210..5e9987fc74bca7 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -853,6 +853,21 @@ HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer) int st = dladdr(pointer, &info); if (st != 0) { +#if defined(RTLD_NODELETE) + if (info.dli_fname == nullptr) + { + return NULL; + } + + // NativeAOT runtime state cannot be safely unloaded. + void* module = dlopen(info.dli_fname, RTLD_LAZY | RTLD_NODELETE); + if (module == nullptr) + { + return NULL; + } + dlclose(module); +#endif + moduleHandle = info.dli_fbase; } #endif //!defined(HOST_WASM) diff --git a/src/tests/nativeaot/SmokeTests/SharedLibrary/SharedLibrary.cpp b/src/tests/nativeaot/SmokeTests/SharedLibrary/SharedLibrary.cpp index cc03865016a4f9..e6d3e682b8f1b7 100644 --- a/src/tests/nativeaot/SmokeTests/SharedLibrary/SharedLibrary.cpp +++ b/src/tests/nativeaot/SmokeTests/SharedLibrary/SharedLibrary.cpp @@ -77,8 +77,7 @@ int main(int argc, char* argv[]) #ifdef TARGET_WINDOWS FreeLibrary(handle); #else - // TODO: How to pin the library in memory on Unix? - // dlclose(handle); + dlclose(handle); #endif return 100; From 429d65a63f7f756411226a39444bd95393a66360 Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Thu, 6 Aug 2026 14:59:35 -0700 Subject: [PATCH 2/8] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index 5e9987fc74bca7..9ef228778da75f 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -854,18 +854,15 @@ HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer) if (st != 0) { #if defined(RTLD_NODELETE) - if (info.dli_fname == nullptr) + if (info.dli_fname != nullptr) { - return NULL; - } - - // NativeAOT runtime state cannot be safely unloaded. - void* module = dlopen(info.dli_fname, RTLD_LAZY | RTLD_NODELETE); - if (module == nullptr) - { - return NULL; + // NativeAOT runtime state cannot be safely unloaded. + void* module = dlopen(info.dli_fname, RTLD_LAZY | RTLD_NODELETE); + if (module != nullptr) + { + dlclose(module); + } } - dlclose(module); #endif moduleHandle = info.dli_fbase; From 2793c2d54fca5cfcdff19387aadcdca132bc7047 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Thu, 6 Aug 2026 15:00:49 -0700 Subject: [PATCH 3/8] code review --- src/coreclr/nativeaot/Bootstrap/main.cpp | 2 +- src/coreclr/nativeaot/Runtime/Pal.h | 2 +- src/coreclr/nativeaot/Runtime/startup.cpp | 2 +- src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 10 ++++++++-- src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp | 9 +++++---- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/coreclr/nativeaot/Bootstrap/main.cpp b/src/coreclr/nativeaot/Bootstrap/main.cpp index fe349e7ba4e03b..eb3d7c1d525d3f 100644 --- a/src/coreclr/nativeaot/Bootstrap/main.cpp +++ b/src/coreclr/nativeaot/Bootstrap/main.cpp @@ -108,7 +108,7 @@ extern "C" bool RhRegisterOSModule(void * pModule, void * pvUnboxingStubsStartRange, uint32_t cbUnboxingStubsRange, void ** pClasslibFunctions, uint32_t nClasslibFunctions); -void* PalGetModuleHandleFromPointer(void* pointer); +void* PalGetModuleHandleFromPointer(void* pointer, bool pinModule = false); #if defined(HOST_X86) && defined(HOST_WINDOWS) #define STRINGIFY(s) #s diff --git a/src/coreclr/nativeaot/Runtime/Pal.h b/src/coreclr/nativeaot/Runtime/Pal.h index 74976bce4c9915..534b69675410e1 100644 --- a/src/coreclr/nativeaot/Runtime/Pal.h +++ b/src/coreclr/nativeaot/Runtime/Pal.h @@ -235,7 +235,7 @@ void PalSleep(uint32_t milliseconds); UInt32_BOOL PalSwitchToThread(); UInt32_BOOL PalAreShadowStacksEnabled(); HANDLE PalCreateEventW(_In_opt_ LPSECURITY_ATTRIBUTES pEventAttributes, UInt32_BOOL manualReset, UInt32_BOOL initialState, _In_opt_z_ LPCWSTR pName); -HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer); +HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer, bool pinModule = false); #ifdef TARGET_UNIX typedef int32_t (*PHARDWARE_EXCEPTION_HANDLER)(uintptr_t faultCode, uintptr_t faultAddress, PAL_LIMITED_CONTEXT* palContext, uintptr_t* arg0Reg, uintptr_t* arg1Reg); diff --git a/src/coreclr/nativeaot/Runtime/startup.cpp b/src/coreclr/nativeaot/Runtime/startup.cpp index 945b19c7eba7e2..ef91e894dc9152 100644 --- a/src/coreclr/nativeaot/Runtime/startup.cpp +++ b/src/coreclr/nativeaot/Runtime/startup.cpp @@ -367,7 +367,7 @@ extern "C" bool RhInitialize(bool isDll) g_safeToShutdownTracing = !isDll; #endif - if (!InitDLL(PalGetModuleHandleFromPointer((void*)&RhInitialize))) + if (!InitDLL(PalGetModuleHandleFromPointer((void*)&RhInitialize, isDll))) return false; return true; diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index 9ef228778da75f..f168fb41807253 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -842,10 +842,14 @@ bool PalStartEventPipeHelperThread(_In_ BackgroundCallback callback, _In_opt_ vo return PalStartBackgroundWork(callback, pCallbackContext, UInt32_FALSE); } -HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer) +HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer, bool pinModule) { HANDLE moduleHandle = NULL; +#if defined(HOST_WASM) + (void)pinModule; +#endif + // Emscripten's implementation of dladdr corrupts memory, // but always returns 0 for the module handle, so just skip the call #if !defined(HOST_WASM) @@ -854,7 +858,7 @@ HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer) if (st != 0) { #if defined(RTLD_NODELETE) - if (info.dli_fname != nullptr) + if (pinModule && info.dli_fname != nullptr) { // NativeAOT runtime state cannot be safely unloaded. void* module = dlopen(info.dli_fname, RTLD_LAZY | RTLD_NODELETE); @@ -863,6 +867,8 @@ HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer) dlclose(module); } } +#else + (void)pinModule; #endif moduleHandle = info.dli_fbase; diff --git a/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp b/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp index 49952d726db39d..789f9269755d84 100644 --- a/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp +++ b/src/coreclr/nativeaot/Runtime/windows/PalMinWin.cpp @@ -921,14 +921,15 @@ bool PalStartEventPipeHelperThread(_In_ BackgroundCallback callback, _In_opt_ vo return PalStartBackgroundWork(callback, pCallbackContext, FALSE); } -HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer) +HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer, bool pinModule) { - // The runtime is not designed to be unloadable today. Use GET_MODULE_HANDLE_EX_FLAG_PIN to prevent - // the module from ever unloading. + // The runtime is not designed to be unloadable today. + DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + (pinModule ? GET_MODULE_HANDLE_EX_FLAG_PIN : GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT); HMODULE module; if (!GetModuleHandleExW( - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, + flags, (LPCWSTR)pointer, &module)) { From eb9716c4a8596664fb4544250c0fe74dedb9966a Mon Sep 17 00:00:00 2001 From: rcj1 Date: Thu, 6 Aug 2026 22:11:53 -0700 Subject: [PATCH 4/8] code review --- .../Microsoft.NETCore.Native.Unix.targets | 2 ++ src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 9 +++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index 905f039becd434..05968938101ebd 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -290,6 +290,8 @@ The .NET Foundation licenses this file to you under the MIT license. + + diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index f168fb41807253..eed825eed35387 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -857,15 +857,12 @@ HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer, bool pinModule) int st = dladdr(pointer, &info); if (st != 0) { -#if defined(RTLD_NODELETE) +#if defined(HOST_OSX) if (pinModule && info.dli_fname != nullptr) { // NativeAOT runtime state cannot be safely unloaded. - void* module = dlopen(info.dli_fname, RTLD_LAZY | RTLD_NODELETE); - if (module != nullptr) - { - dlclose(module); - } + // Keep the extra reference for the lifetime of the process. + (void)dlopen(info.dli_fname, RTLD_LAZY | RTLD_NOLOAD); } #else (void)pinModule; From e62aaf8f48a0b85b184654c26547a455ac453244 Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Thu, 6 Aug 2026 22:14:59 -0700 Subject: [PATCH 5/8] Update PalUnix.cpp --- src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index eed825eed35387..e2c872e3b973dc 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -862,7 +862,7 @@ HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer, bool pinModule) { // NativeAOT runtime state cannot be safely unloaded. // Keep the extra reference for the lifetime of the process. - (void)dlopen(info.dli_fname, RTLD_LAZY | RTLD_NOLOAD); + dlopen(info.dli_fname, RTLD_LAZY | RTLD_NOLOAD); } #else (void)pinModule; From f0412ce6ac1c202f5103e8ebb6d8add854121976 Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Fri, 7 Aug 2026 15:11:59 -0700 Subject: [PATCH 6/8] Update src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets Co-authored-by: Jan Kotas --- .../BuildIntegration/Microsoft.NETCore.Native.Unix.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets index 05968938101ebd..d777c401ff16d9 100644 --- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets +++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.Unix.targets @@ -291,7 +291,7 @@ The .NET Foundation licenses this file to you under the MIT license. - + From 0c19248818500a7cf2c4a6f12da728fa0bb33cad Mon Sep 17 00:00:00 2001 From: Rachel Jarvi Date: Fri, 7 Aug 2026 15:12:16 -0700 Subject: [PATCH 7/8] Update src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp Co-authored-by: Jan Kotas --- src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index e2c872e3b973dc..fd0979539c6aa4 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -862,6 +862,7 @@ HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer, bool pinModule) { // NativeAOT runtime state cannot be safely unloaded. // Keep the extra reference for the lifetime of the process. + // Unloading is disabled via `-z,nodelete` linker option on ELF platforms. dlopen(info.dli_fname, RTLD_LAZY | RTLD_NOLOAD); } #else From 818d5620540c55691499684a67e708649beba5b9 Mon Sep 17 00:00:00 2001 From: rcj1 Date: Fri, 7 Aug 2026 15:15:12 -0700 Subject: [PATCH 8/8] code review --- src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp index fd0979539c6aa4..5bf9f6b1fa8a0a 100644 --- a/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp +++ b/src/coreclr/nativeaot/Runtime/unix/PalUnix.cpp @@ -846,10 +846,6 @@ HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer, bool pinModule) { HANDLE moduleHandle = NULL; -#if defined(HOST_WASM) - (void)pinModule; -#endif - // Emscripten's implementation of dladdr corrupts memory, // but always returns 0 for the module handle, so just skip the call #if !defined(HOST_WASM) @@ -865,8 +861,6 @@ HANDLE PalGetModuleHandleFromPointer(_In_ void* pointer, bool pinModule) // Unloading is disabled via `-z,nodelete` linker option on ELF platforms. dlopen(info.dli_fname, RTLD_LAZY | RTLD_NOLOAD); } -#else - (void)pinModule; #endif moduleHandle = info.dli_fbase;