aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorinna Vinschen <corinna@vinschen.de>2025-03-30 20:42:07 +0200
committerCorinna Vinschen <corinna@vinschen.de>2025-03-30 20:52:34 +0200
commit1c4fc31c48a0c7cc6de122c50e25e618c169c343 (patch)
tree5ecda04e4d337a5c072673353dbed635a53d6bbc
parent82b31085f6ad23c3aaf5a928bd56fe06cbe63df5 (diff)
downloadnewlib-1c4fc31c48a0c7cc6de122c50e25e618c169c343.zip
newlib-1c4fc31c48a0c7cc6de122c50e25e618c169c343.tar.gz
newlib-1c4fc31c48a0c7cc6de122c50e25e618c169c343.tar.bz2
Cygwin: dlfcn: fix dlopen refcounting for DLLs loaded with RTLD_NODELETE
Refcounting is performed even if the DLL is loaded at least once with RTLD_NODELETE. In that case we must not decrement the count on dlclose, even if FreeLibrary will never unload the DLL. Use INT_MAX as dll count if RTLD_NODELETE has been used at least once. Skip any further refcounting and make sure the DLL is still pinned in memory after fork. Fixes: 33297d810d90 ("Cygwin: dlfcn: Fix reference counting") Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
-rw-r--r--winsup/cygwin/dlfcn.cc15
-rw-r--r--winsup/cygwin/dll_init.cc16
-rw-r--r--winsup/cygwin/release/3.6.12
3 files changed, 26 insertions, 7 deletions
diff --git a/winsup/cygwin/dlfcn.cc b/winsup/cygwin/dlfcn.cc
index f98c7db..a1e121f 100644
--- a/winsup/cygwin/dlfcn.cc
+++ b/winsup/cygwin/dlfcn.cc
@@ -278,7 +278,13 @@ dlopen (const char *name, int flags)
{
dll *d = dlls.find (ret, true);
if (d)
- ++d->count;
+ {
+ /* count == INT_MIN is used to specify RTLD_NODELETE */
+ if (d->count == INT_MIN || gmheflags)
+ d->count = INT_MIN;
+ else
+ ++d->count;
+ }
}
if (ret && gmheflags)
@@ -348,14 +354,15 @@ dlclose (void *handle)
int ret = 0;
if (handle != GetModuleHandle (NULL))
{
- /* reference counting */
+ /* Reference counting.
+ count == INT_MIN is used to specify RTLD_NODELETE */
dll *d = dlls.find (handle, true);
- if (!d || d->count <= 0)
+ if (!d || (d->count <= 0 && d->count != INT_MIN))
{
errno = ENOENT;
ret = -1;
}
- else
+ else if (d->count != INT_MIN)
{
--d->count;
if (!FreeLibrary ((HMODULE) handle))
diff --git a/winsup/cygwin/dll_init.cc b/winsup/cygwin/dll_init.cc
index c6fb94a..aab11d1 100644
--- a/winsup/cygwin/dll_init.cc
+++ b/winsup/cygwin/dll_init.cc
@@ -782,9 +782,19 @@ void dll_list::load_after_fork_impl (HANDLE parent, dll* d, int retries)
if (h != d->handle)
fabort ("unable to map %W (using %W) to same address as parent: %p != %p",
d->ntname, buffered_shortname (d->forkedntname ()), d->handle, h);
- /* Fix OS reference count. */
- for (int cnt = 1; cnt < d->count; ++cnt)
- LoadLibraryW (buffered_shortname (d->forkedntname ()));
+ /* Fix OS reference count.
+ count == INT_MIN is used to specify RTLD_NODELETE. If so, we don't
+ have to call LoadLibraryW count times, just mark the DLL as pinned. */
+ if (d->count == INT_MIN) /* RTLD_NODELETE */
+ {
+ HMODULE hm;
+ GetModuleHandleExW (GET_MODULE_HANDLE_EX_FLAG_PIN,
+ buffered_shortname (d->forkedntname ()),
+ &hm);
+ }
+ else
+ for (int cnt = 1; cnt < d->count; ++cnt)
+ LoadLibraryW (buffered_shortname (d->forkedntname ()));
}
}
diff --git a/winsup/cygwin/release/3.6.1 b/winsup/cygwin/release/3.6.1
index 85c3f6c..c1dbbfb 100644
--- a/winsup/cygwin/release/3.6.1
+++ b/winsup/cygwin/release/3.6.1
@@ -18,3 +18,5 @@ Fixes:
- Fix reference counting when dlopen/dlclose is called on the Cygwin DLL.
Addresses: https://cygwin.com/pipermail/cygwin/2025-March/257783.html
+
+- Fix reference counting when dlopen/dlclose a DLL with RTLD_NODELETE.