Skip to content

Commit 5810de8

Browse files
committed
Cygwin: dlfcn: add native DLLs to dll list
If native DLLs are dlopen'ed, we have to keep track of the load count, so dlclose doesn't misbehave. We also have to keep track for the sake of a forked child so the handle returned by dlopen is still valid in the child. To accomplish that, add a new type DLL_NATIVE. Fixes: 82b3108 ("Cygwin: dlfcn: avoid ENOENT on dlcose after dlopen(cygwin1.dll)") Signed-off-by: Corinna Vinschen <[email protected]> (cherry picked from commit 9fb7f28)
1 parent d750786 commit 5810de8

File tree

4 files changed

+55
-19
lines changed

4 files changed

+55
-19
lines changed

winsup/cygwin/dlfcn.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,16 @@ dlopen (const char *name, int flags)
284284
else
285285
++d->count;
286286
}
287+
else
288+
{
289+
/* All Cygwin DLLs loaded or linked into this process get a dll
290+
record when they call dll_dllcrt0 on init. So if we don't
291+
find the dll it's a native DLL. Add it as DLL_NATIVE.
292+
Simply restore the LoadLibrary count after fork. Don't care
293+
where they are loaded to, don't try to fix up their data and
294+
bss segments after fork, and don't run dtors. */
295+
dlls.alloc ((HMODULE) ret, user_data, DLL_NATIVE);
296+
}
287297
}
288298

289299
if (ret && nodelete)
@@ -364,6 +374,10 @@ dlclose (void *handle)
364374
else if (d->count != INT_MIN)
365375
{
366376
--d->count;
377+
/* Native DLLs don't call cygwin_detach_dll so they have to be
378+
detached explicitely. */
379+
if (d->type == DLL_NATIVE && d->count <= 0)
380+
dlls.detach (handle);
367381
if (!FreeLibrary ((HMODULE) handle))
368382
{
369383
__seterrno ();

winsup/cygwin/dll_init.cc

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,8 @@ dll_list::alloc (HINSTANCE h, per_process *p, dll_type type)
314314
/* Already loaded? For linked DLLs, only compare the basenames. Linked
315315
DLLs are loaded using just the basename and the default DLL search path.
316316
The Windows loader picks up the first one it finds.
317-
This also applies to cygwin1.dll and the main-executable (DLL_SELF).
317+
This also applies to cygwin1.dll and the main-executable (DLL_SELF)
318+
and native DLLs (DLL_NATIVE).
318319
When in_load_after_fork, dynamically loaded dll's are reloaded
319320
using their parent's forkable_ntname, if available. */
320321
dll *d = (type != DLL_LOAD) ? dlls.find_by_modname (modname) :
@@ -346,7 +347,6 @@ dll_list::alloc (HINSTANCE h, per_process *p, dll_type type)
346347
{
347348
size_t forkntsize = forkable_ntnamesize (type, ntname, modname);
348349

349-
/* FIXME: Change this to new at some point. */
350350
d = (dll *) cmalloc (HEAP_2_DLL, sizeof (*d)
351351
+ ((ntnamelen + forkntsize) * sizeof (*ntname)));
352352

@@ -356,14 +356,20 @@ dll_list::alloc (HINSTANCE h, per_process *p, dll_type type)
356356
d->modname = d->ntname + (modname - ntname);
357357
d->handle = h;
358358
d->count = 0; /* Reference counting performed in dlopen/dlclose. */
359-
/* DLL_SELF dtors (main-executable, cygwin1.dll) are run elsewhere */
360-
d->has_dtors = type != DLL_SELF;
359+
/* DLL_SELF dtors (main-executable, cygwin1.dll) are run elsewhere,
360+
DLL_NATIVE dtors whereever native DLLs do it. */
361+
d->has_dtors = (type == DLL_LINK || type == DLL_LOAD);
361362
d->p = p;
362363
d->ndeps = 0;
363364
d->deps = NULL;
364365
d->image_size = ((pefile*)h)->optional_hdr ()->SizeOfImage;
365366
d->preferred_base = (void*) ((pefile*)h)->optional_hdr()->ImageBase;
366367
d->type = type;
368+
if (type == DLL_NATIVE)
369+
{
370+
d->count = 1;
371+
reload_on_fork = 1;
372+
}
367373
d->fii.IndexNumber.QuadPart = -1LL;
368374
if (!forkntsize)
369375
d->forkable_ntname = NULL;
@@ -541,8 +547,10 @@ dll_list::topsort_visit (dll* d, bool seek_tail)
541547
}
542548

543549

550+
/* If called from dlopen/dlclose, from_dlfcn is true and we return any dll if
551+
the address matches. Otherwise we only want DLL_LINK and DLL_LOAD dlls. */
544552
dll *
545-
dll_list::find (void *retaddr, bool find_self)
553+
dll_list::find (void *retaddr, bool from_dlfcn)
546554
{
547555
MEMORY_BASIC_INFORMATION m;
548556
if (!VirtualQuery (retaddr, &m, sizeof m))
@@ -551,7 +559,8 @@ dll_list::find (void *retaddr, bool find_self)
551559

552560
dll *d = &start;
553561
while ((d = d->next))
554-
if ((d->type != DLL_SELF || find_self) && d->handle == h)
562+
if ((from_dlfcn || d->type == DLL_LINK || d->type == DLL_LOAD)
563+
&& d->handle == h)
555564
break;
556565
return d;
557566
}
@@ -569,12 +578,16 @@ dll_list::detach (void *retaddr)
569578
guard (true);
570579
if ((d = find (retaddr)))
571580
{
572-
/* Ensure our exception handler is enabled for destructors */
573-
exception protect;
574-
/* Call finalize function if we are not already exiting */
575-
if (!exit_state)
576-
__cxa_finalize (d->handle);
577-
d->run_dtors ();
581+
/* Only run dtors for Cygwin DLLs. */
582+
if (d->type == DLL_LINK || d->type == DLL_LOAD)
583+
{
584+
/* Ensure our exception handler is enabled for destructors */
585+
exception protect;
586+
/* Call finalize function if we are not already exiting */
587+
if (!exit_state)
588+
__cxa_finalize (d->handle);
589+
d->run_dtors ();
590+
}
578591
d->prev->next = d->next;
579592
if (d->next)
580593
d->next->prev = d->prev;
@@ -596,8 +609,9 @@ dll_list::init ()
596609
/* Walk the dll chain, initializing each dll */
597610
dll *d = &start;
598611
dll_global_dtors_recorded = d->next != NULL;
612+
/* Init linked and early loaded Cygwin DLLs. */
599613
while ((d = d->next))
600-
if (d->type != DLL_SELF) /* linked and early loaded dlls */
614+
if (d->type == DLL_LINK || d->type == DLL_LOAD)
601615
d->init ();
602616
}
603617

@@ -684,7 +698,10 @@ dll_list::load_after_fork (HANDLE parent)
684698

685699
in_load_after_fork = true;
686700
if (reload_on_fork)
687-
load_after_fork_impl (parent, dlls.istart (DLL_LOAD), 0);
701+
{
702+
load_after_fork_impl (parent, dlls.istart (DLL_NATIVE), 0);
703+
load_after_fork_impl (parent, dlls.istart (DLL_LOAD), 0);
704+
}
688705
track_self ();
689706
in_load_after_fork = false;
690707
}
@@ -709,7 +726,7 @@ void dll_list::load_after_fork_impl (HANDLE parent, dll* d, int retries)
709726
the LoadLibraryExW call unconditional.
710727
*/
711728
for ( ; d; d = dlls.inext ())
712-
if (d->handle != d->preferred_base)
729+
if (hold_type == DLL_LOAD && d->handle != d->preferred_base)
713730
{
714731
/* See if the DLL will load in proper place. If not, unload it,
715732
reserve the memory around it, and try again.
@@ -757,9 +774,11 @@ void dll_list::load_after_fork_impl (HANDLE parent, dll* d, int retries)
757774
interim mapping (for rebased dlls) . The dll list is sorted in
758775
dependency order, so we shouldn't pull in any additional dlls
759776
outside our control. */
760-
for (dll *d = dlls.istart (DLL_LOAD); d; d = dlls.inext ())
777+
for (dll *d = dlls.istart (hold_type); d; d = dlls.inext ())
761778
{
762-
if (d->handle == d->preferred_base)
779+
if (hold_type == DLL_NATIVE)
780+
/* just LoadLibrary... */;
781+
else if (d->handle == d->preferred_base)
763782
{
764783
if (!VirtualFree (d->handle, 0, MEM_RELEASE))
765784
fabort ("unable to release protective reservation for %W (%p), %E",

winsup/cygwin/local_includes/dll_init.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ struct per_module
3434
typedef enum
3535
{
3636
DLL_NONE,
37-
DLL_SELF, /* main-program.exe, cygwin1.dll */
37+
DLL_SELF, /* main-program.exe, cygwin1.dll */
38+
DLL_NATIVE, /* dlopen'ed native DLLs. reload after fork, but otherwise
39+
do nothing, just as with DLL_SELF. */
3840
DLL_LINK,
3941
DLL_LOAD,
4042
DLL_ANY

winsup/cygwin/release/3.6.1

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ Fixes:
1616
stack area is not accessible from the signal handler.
1717
Addresses: https://cygwin.com/pipermail/cygwin/2025-March/257714.html
1818

19-
- Fix reference counting when dlopen/dlclose is called on the Cygwin DLL.
19+
- Fix reference counting when dlopen/dlclose is called on the Cygwin DLL
20+
or on non-Cygwin DLLs.
2021
Addresses: https://cygwin.com/pipermail/cygwin/2025-March/257783.html
2122

2223
- Fix reference counting when dlopen/dlclose a DLL with RTLD_NODELETE.

0 commit comments

Comments
 (0)