@@ -412,6 +412,8 @@ tests += \
tst-dlsym-error \
tst-filterobj \
tst-filterobj-dlopen \
+ tst-finiorder1 \
+ tst-finiorder2 \
tst-glibc-hwcaps \
tst-glibc-hwcaps-mask \
tst-glibc-hwcaps-prepend \
@@ -834,6 +836,11 @@ modules-names += \
tst-filterobj-filtee \
tst-filterobj-flt \
tst-finilazyfailmod \
+ tst-finiorder1mod1 \
+ tst-finiorder1mod2 \
+ tst-finiorder2mod1 \
+ tst-finiorder2mod2 \
+ tst-finiorder2mod3 \
tst-globalmod2 \
tst-initlazyfailmod \
tst-initorder2a \
@@ -3018,3 +3025,17 @@ LDFLAGS-tst-dlclose-lazy-mod1.so = -Wl,-z,lazy,--no-as-needed
$(objpfx)tst-dlclose-lazy-mod1.so: $(objpfx)tst-dlclose-lazy-mod2.so
$(objpfx)tst-dlclose-lazy.out: \
$(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so
+
+LDFLAGS-tst-finiorder1 = -Wl,-export-dynamic
+tst-finiorder1mod1.so-no-z-defs = yes
+tst-finiorder1mod2.so-no-z-defs = yes
+$(objpfx)tst-finiorder1.out: \
+ $(objpfx)tst-finiorder1mod1.so $(objpfx)tst-finiorder1mod2.so
+
+LDFLAGS-tst-finiorder2 = -Wl,-export-dynamic
+tst-finiorder2mod1.so-no-z-defs = yes
+tst-finiorder2mod2.so-no-z-defs = yes
+tst-finiorder2mod3.so-no-z-defs = yes
+$(objpfx)tst-finiorder2: $(objpfx)tst-finiorder2mod1.so
+$(objpfx)tst-finiorder2.out: \
+ $(objpfx)tst-finiorder2mod2.so $(objpfx)tst-finiorder2mod3.so
@@ -24,15 +24,42 @@
void
_dl_fini (void)
{
- /* Call destructors strictly in the reverse order of constructors.
- This causes fewer surprises than some arbitrary reordering based
- on new (relocation) dependencies. None of the objects are
- unmapped, so applications can deal with this if their DSOs remain
- in a consistent state after destructors have run. */
-
/* Protect against concurrent loads and unloads. */
__rtld_lock_lock_recursive (GL(dl_load_lock));
+ /* Always call the destructor of the main program first. This may
+ trigger dlclose calls, which runs other destructors early. (The
+ main program is never added to _dl_init_called_list.) Together
+ with the first loop below, the effect is similar to calling
+ dlclose on the main program (except that nothing is unloaded). */
+#ifdef SHARED
+ Lmid_t last_ns = LM_ID_BASE;
+ _dl_audit_activity_nsid (LM_ID_BASE, LA_ACT_DELETE);
+#endif
+ /* There is no need to re-enable exceptions because _dl_fini
+ is not called from a context where exceptions are caught. */
+ _dl_call_fini (GL(dl_ns)[LM_ID_BASE]._ns_loaded);
+ /* Auditing checkpoint: another object closed. */
+ _dl_audit_objclose (GL(dl_ns)[LM_ID_BASE]._ns_loaded);
+
+ /* Give the initially loaded objects a chance to unload dlopen'ed
+ objects using dlclose. */
+ for (struct link_map *l = _dl_init_called_library_list; l != NULL;
+ l = l->l_init_called_next)
+ {
+ _dl_call_fini (l);
+ _dl_audit_objclose (l);
+ }
+
+ /* At this point _dl_init_called_list contains objects that have
+ been dlopen'ed but not dlclose'd, and destructors for other
+ objects have been invoked. Call destructors in the reverse order
+ of constructors (with an exception for audit modules). This
+ causes fewer surprises than some arbitrary reordering based on
+ new (relocation) dependencies. None of the objects are unmapped,
+ so applications can rely on DSOs remaining in a consistent state
+ after destructors have run. */
+
/* Ignore objects which are opened during shutdown. */
struct link_map *local_init_called_list = _dl_init_called_list;
@@ -52,7 +79,6 @@ _dl_fini (void)
still run. */
#ifdef SHARED
int last_pass = GLRO(dl_naudit) > 0;
- Lmid_t last_ns = -1;
for (int do_audit = 0; do_audit <= last_pass; ++do_audit)
#endif
for (struct link_map *l = local_init_called_list; l != NULL;
@@ -73,19 +99,12 @@ _dl_fini (void)
}
#endif
- /* There is no need to re-enable exceptions because _dl_fini
- is not called from a context where exceptions are caught. */
_dl_call_fini (l);
-
-#ifdef SHARED
- /* Auditing checkpoint: another object closed. */
_dl_audit_objclose (l);
-#endif
}
#ifdef SHARED
- if (last_ns >= 0)
- _dl_audit_activity_nsid (last_ns, LA_ACT_CONSISTENT);
+ _dl_audit_activity_nsid (last_ns, LA_ACT_CONSISTENT);
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS))
_dl_debug_printf ("\nruntime linker statistics:\n"
@@ -22,6 +22,7 @@
#include <elf-initfini.h>
struct link_map *_dl_init_called_list;
+struct link_map *_dl_init_called_library_list;
static void
call_init (struct link_map *l, int argc, char **argv, char **env)
@@ -48,16 +49,6 @@ call_init (struct link_map *l, int argc, char **argv, char **env)
progress.) */
l->l_map_used = 1;
- /* Record execution before starting any initializers. This way, if
- the initializers themselves call dlopen, their ELF destructors
- will eventually be run before this object is destructed, matching
- that their ELF constructors have run before this object was
- constructed. _dl_fini uses this list for audit callbacks, so
- register objects on the list even if they do not have a
- constructor. */
- l->l_init_called_next = _dl_init_called_list;
- _dl_init_called_list = l;
-
/* Check for object which constructors we do not run here. */
if (__builtin_expect (l->l_name[0], 'a') == '\0'
&& l->l_type == lt_executable)
@@ -89,6 +80,33 @@ call_init (struct link_map *l, int argc, char **argv, char **env)
for (j = 0; j < jm; ++j)
((dl_init_t) addrs[j]) (argc, argv, env);
}
+
+ /* Schedule invocation of the ELF destructors for this object. Use
+ separate lists for initially loaded objects (lt_library) and
+ dlopened objects (lt_loaded), so that _dl_fini can give a chance
+ to lt_library objects to unload objects using dlclose.
+
+ Do this after completion of the constructors, so that the
+ initializers themselves call dlopen, this object has a chance to
+ influence destructor order with explicit dlclose calls if this
+ object is destructed via _dl_fini. (If an object is destructed
+ earlier, using dlclose, objects it has dlopened are not eligible
+ for destruction, and the destructor of the explicitly dlclose'd
+ object runs first, but _dl_fini is different.)
+
+ Objects without constructors may still have destructors, and
+ _dl_fini uses this list for audit callbacks, so register objects
+ on the list even if they do not have a constructor. */
+ if (l->l_type == lt_loaded)
+ {
+ l->l_init_called_next = _dl_init_called_list;
+ _dl_init_called_list = l;
+ }
+ else
+ {
+ l->l_init_called_next = _dl_init_called_library_list;
+ _dl_init_called_library_list = l;
+ }
}
@@ -63,4 +63,4 @@ output: .>{+a[d>c>b>a>];+e[e>];+f[f>];+g[g>];+d[];%d(b(e(a()))a()g(c(a()f(b(e(a(
# object, that object is initialized last (and not unloaded prematurely).
# Final destructor order is the opposite of constructor order.
tst-bz28937: {+a;+b;-b;+c;%c};a->a1;a->a2;a2->a;b->b1;c->a1;c=>a1
-output: .>{+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<c<a<a1<a2<.
+output: .>{+a[a2>a1>a>];+b[b1>b>];-b[<b<b1];+c[c>];%c(a1());}<.<c<a<a1<a2
new file mode 100644
@@ -0,0 +1,52 @@
+/* _dl_fini ordering test.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* This test opens mod2 from the ELF constructor of mod1, and expects
+ the main program to be destructed first, followed by mod1, followed
+ by mod2: .>{mod1>mod2>}<.<mod1<mod2 in scripts/dso-ordering-test.py
+ terms. */
+
+#include <stdio.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <unistd.h>
+
+/* Used to check destructor ordering. */
+int fini_counter;
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ printf ("info: main program destructor invoked (PID %d)\n", (int) getpid ());
+
+ /* Destructor for the main program runs first. */
+ TEST_COMPARE (fini_counter, 0);
+ fini_counter = 1;
+}
+
+static int
+do_test (void)
+{
+ /* Leak the handle, so that the destructor is invoked by _dl_fini. */
+ (void) xdlopen ("tst-finiorder1mod1.so", RTLD_NOW);
+ return 0;
+}
+
+/* Marker value from the tst-finiorder1mod2.so destructor. */
+#define EXPECTED_STATUS 17
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,43 @@
+/* _dl_fini ordering test. Helper module.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <support/xdlfcn.h>
+#include <unistd.h>
+
+extern int fini_counter;
+
+static void __attribute__ ((constructor))
+init (void)
+{
+ /* Leak the handle, rely on _dl_fini constructor invocation. */
+ (void) xdlopen ("tst-finiorder1mod2.so", RTLD_NOW);
+}
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ printf ("info: tst-finiorder1mod1.so destructor invoked (%d)\n",
+ fini_counter);
+ if (fini_counter != 1)
+ {
+ puts ("error: unexpected fini_counter");
+ _exit (1);
+ }
+ fini_counter = 2;
+}
new file mode 100644
@@ -0,0 +1,36 @@
+/* _dl_fini ordering test. Helper module producing exit status 17.
+ Copyright (C) 2021-2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <unistd.h>
+
+extern int fini_counter;
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ printf ("info: tst-finiorder1mod2.so destructor invoked (%d)\n",
+ fini_counter);
+ if (fini_counter != 2)
+ {
+ puts ("error: unexpected fini_counter");
+ _exit (1);
+ }
+ /* Marker value expected by the test driver. */
+ _exit (17);
+}
new file mode 100644
@@ -0,0 +1,61 @@
+/* _dl_fini ordering test.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* This test dlopens mod3, followed by mod2 (indirectly via the
+ directly linked mod1). The main program to be destructed first,
+ followed by mod1, mod2, mod3: mod1>.>{mod2>mod3>}<.<mod1<mod2<mod3
+ in scripts/dso-ordering-test.py terms. */
+
+#include <stdio.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+#include <unistd.h>
+
+/* Used to check destructor ordering. */
+int fini_counter;
+
+extern void init_mod1 (void);
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ printf ("info: main program destructor invoked (PID %d)\n", (int) getpid ());
+
+ /* Destructor for the main program runs first. */
+ TEST_COMPARE (fini_counter, 0);
+ fini_counter = 1;
+}
+
+static int
+do_test (void)
+{
+ /* Leak the handle, so that the destructor is invoked by _dl_fini.
+ The other dlopen'ed object (tst-finiorder1mod2.so) is dlclose'd
+ explicitly. */
+ (void) xdlopen ("tst-finiorder2mod3.so", RTLD_NOW);
+
+ /* Trigger a dlopen call from tst-finiorder2mod1.so, to load
+ tst-finiorder2mod2.so. */
+ init_mod1 ();
+
+ return 0;
+}
+
+/* Marker value from the tst-finiorder2mod3.so destructor. */
+#define EXPECTED_STATUS 17
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,49 @@
+/* _dl_fini ordering test. Helper module.
+ Copyright (C) 2021-2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <support/xdlfcn.h>
+#include <unistd.h>
+
+extern int fini_counter;
+
+static void *mod2_handle;
+
+void
+init_mod1 (void)
+{
+ mod2_handle = xdlopen ("tst-finiorder2mod2.so", RTLD_NOW);
+}
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ if (mod2_handle == NULL)
+ /* Do nothing in the test wrapper. */
+ return;
+
+ printf ("info: tst-finiorder1mod1.so destructor invoked (%d)\n",
+ fini_counter);
+ if (fini_counter != 1)
+ {
+ puts ("error: unexpected fini_counter");
+ _exit (1);
+ }
+ fini_counter = 2;
+ xdlclose (mod2_handle);
+}
new file mode 100644
@@ -0,0 +1,35 @@
+/* _dl_fini ordering test. Helper module.
+ Copyright (C) 2021-2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <unistd.h>
+
+extern int fini_counter;
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ printf ("info: tst-finiorder2mod2.so destructor invoked (%d)\n",
+ fini_counter);
+ if (fini_counter != 2)
+ {
+ puts ("error: unexpected fini_counter");
+ _exit (1);
+ }
+ fini_counter = 3;
+}
new file mode 100644
@@ -0,0 +1,36 @@
+/* _dl_fini ordering test. Helper module producing exit status 17.
+ Copyright (C) 2021-2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <unistd.h>
+
+extern int fini_counter;
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+ printf ("info: tst-finiorder2mod3.so destructor invoked (%d)\n",
+ fini_counter);
+ if (fini_counter != 3)
+ {
+ puts ("error: unexpected fini_counter");
+ _exit (1);
+ }
+ /* Marker value expected by the test driver. */
+ _exit (17);
+}
@@ -1041,6 +1041,9 @@ extern void _dl_init (struct link_map *main_map, int argc, char **argv,
invocation. */
extern struct link_map *_dl_init_called_list attribute_hidden;
+/* The same, but for initially loaded libraries. */
+extern struct link_map *_dl_init_called_library_list attribute_hidden;
+
/* Call the finalizer functions of all shared objects whose
initializer functions have completed. */
extern void _dl_fini (void) attribute_hidden;