diff --git a/glibc-dlopen-nodelete-fixes-1.patch b/glibc-dlopen-nodelete-fixes-1.patch new file mode 100644 index 0000000..36136ad --- /dev/null +++ b/glibc-dlopen-nodelete-fixes-1.patch @@ -0,0 +1,1226 @@ +Author: Florian Weimer +Date: Tue Dec 3 18:13:51 2019 +0100 + + dlopen: Fix issues related to NODELETE handling and relocations + + The assumption behind the assert in activate_nodelete was wrong: + + Inconsistency detected by ld.so: dl-open.c: 459: activate_nodelete: + Assertion `!imap->l_init_called || imap->l_type != lt_loaded' failed! (edit) + + It can happen that an already-loaded object that is in the local + scope is promoted to NODELETE status, via binding to a unique + symbol. + + Similarly, it is possible that such NODELETE promotion occurs to + an already-loaded object from the global scope. This is why the + loop in activate_nodelete has to cover all objects in the namespace + of the new object. + + In do_lookup_unique, it could happen that the NODELETE status of + an already-loaded object was overwritten with a pending NODELETE + status. As a result, if dlopen fails, this could cause a loss of + the NODELETE status of the affected object, eventually resulting + in an incorrect unload. + + Fixes commit f63b73814f74032c0e5d0a83300e3d864ef905e5 ("Remove all + loaded objects if dlopen fails, ignoring NODELETE [BZ #20839]"). + +diff --git a/elf/Makefile b/elf/Makefile +index b2b3be203fbaf1ca..72a5aa88b1025e76 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -191,7 +191,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ + tst-audit1 tst-audit2 tst-audit8 tst-audit9 \ + tst-addr1 tst-thrlock \ + tst-unique1 tst-unique2 $(if $(CXX),tst-unique3 tst-unique4 \ +- tst-nodelete) \ ++ tst-nodelete tst-dlopen-nodelete-reloc) \ + tst-initorder tst-initorder2 tst-relsort1 tst-null-argv \ + tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \ + tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \ +@@ -271,7 +271,24 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ + tst-auditmod9a tst-auditmod9b \ + $(if $(CXX),tst-unique3lib tst-unique3lib2 tst-unique4lib \ + tst-nodelete-uniquemod tst-nodelete-rtldmod \ +- tst-nodelete-zmod) \ ++ tst-nodelete-zmod \ ++ tst-dlopen-nodelete-reloc-mod1 \ ++ tst-dlopen-nodelete-reloc-mod2 \ ++ tst-dlopen-nodelete-reloc-mod3 \ ++ tst-dlopen-nodelete-reloc-mod4 \ ++ tst-dlopen-nodelete-reloc-mod5 \ ++ tst-dlopen-nodelete-reloc-mod6 \ ++ tst-dlopen-nodelete-reloc-mod7 \ ++ tst-dlopen-nodelete-reloc-mod8 \ ++ tst-dlopen-nodelete-reloc-mod9 \ ++ tst-dlopen-nodelete-reloc-mod10 \ ++ tst-dlopen-nodelete-reloc-mod11 \ ++ tst-dlopen-nodelete-reloc-mod12 \ ++ tst-dlopen-nodelete-reloc-mod13 \ ++ tst-dlopen-nodelete-reloc-mod14 \ ++ tst-dlopen-nodelete-reloc-mod15 \ ++ tst-dlopen-nodelete-reloc-mod16 \ ++ tst-dlopen-nodelete-reloc-mod17) \ + tst-initordera1 tst-initorderb1 \ + tst-initordera2 tst-initorderb2 \ + tst-initordera3 tst-initordera4 \ +@@ -1627,3 +1644,48 @@ $(objpfx)tst-dlopenfailmod1.so: \ + $(shared-thread-library) $(objpfx)tst-dlopenfaillinkmod.so + LDFLAGS-tst-dlopenfaillinkmod.so = -Wl,-soname,tst-dlopenfail-missingmod.so + $(objpfx)tst-dlopenfailmod2.so: $(shared-thread-library) ++ ++$(objpfx)tst-dlopen-nodelete-reloc: $(libdl) ++$(objpfx)tst-dlopen-nodelete-reloc.out: \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod1.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod2.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod3.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod4.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod5.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod6.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod7.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod8.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod9.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod10.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod11.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod12.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod13.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod14.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod15.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod16.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod17.so ++tst-dlopen-nodelete-reloc-mod2.so-no-z-defs = yes ++LDFLAGS-tst-dlopen-nodelete-reloc-mod2.so = -Wl,-z,nodelete ++$(objpfx)tst-dlopen-nodelete-reloc-mod4.so: \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod3.so ++LDFLAGS-tst-dlopen-nodelete-reloc-mod4.so = -Wl,--no-as-needed ++$(objpfx)tst-dlopen-nodelete-reloc-mod5.so: \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod4.so ++LDFLAGS-tst-dlopen-nodelete-reloc-mod5.so = -Wl,-z,nodelete,--no-as-needed ++tst-dlopen-nodelete-reloc-mod5.so-no-z-defs = yes ++tst-dlopen-nodelete-reloc-mod7.so-no-z-defs = yes ++$(objpfx)tst-dlopen-nodelete-reloc-mod8.so: $(libdl) ++$(objpfx)tst-dlopen-nodelete-reloc-mod10.so: $(libdl) ++tst-dlopen-nodelete-reloc-mod11.so-no-z-defs = yes ++$(objpfx)tst-dlopen-nodelete-reloc-mod13.so: \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod12.so ++$(objpfx)tst-dlopen-nodelete-reloc-mod15.so: \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod14.so ++tst-dlopen-nodelete-reloc-mod16.so-no-z-defs = yes ++$(objpfx)tst-dlopen-nodelete-reloc-mod16.so: \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod15.so ++LDFLAGS-tst-dlopen-nodelete-reloc-mod16.so = -Wl,--no-as-needed ++$(objpfx)tst-dlopen-nodelete-reloc-mod17.so: \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod15.so \ ++ $(objpfx)tst-dlopen-nodelete-reloc-mod16.so ++LDFLAGS-tst-dlopen-nodelete-reloc-mod17.so = -Wl,--no-as-needed +diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c +index a2e85a55686086c9..99846918c3453694 100644 +--- a/elf/dl-lookup.c ++++ b/elf/dl-lookup.c +@@ -311,12 +311,12 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, + enter_unique_sym (entries, size, + new_hash, strtab + sym->st_name, sym, map); + +- if (map->l_type == lt_loaded) ++ if (map->l_type == lt_loaded ++ && map->l_nodelete == link_map_nodelete_inactive) + { + /* Make sure we don't unload this object by + setting the appropriate flag. */ +- if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS) +- && map->l_nodelete == link_map_nodelete_inactive) ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)) + _dl_debug_printf ("\ + marking %s [%lu] as NODELETE due to unique symbol\n", + map->l_name, map->l_ns); +diff --git a/elf/dl-open.c b/elf/dl-open.c +index df9f29a5e5683bf2..56f213323ce5cf65 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -433,34 +433,21 @@ TLS generation counter wrapped! Please report this.")); + after dlopen failure is not possible, so that _dl_close can clean + up objects if necessary. */ + static void +-activate_nodelete (struct link_map *new, int mode) ++activate_nodelete (struct link_map *new) + { +- if (mode & RTLD_NODELETE || new->l_nodelete == link_map_nodelete_pending) +- { +- if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)) +- _dl_debug_printf ("activating NODELETE for %s [%lu]\n", +- new->l_name, new->l_ns); +- new->l_nodelete = link_map_nodelete_active; +- } +- +- for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) +- { +- struct link_map *imap = new->l_searchlist.r_list[i]; +- if (imap->l_nodelete == link_map_nodelete_pending) +- { +- if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)) +- _dl_debug_printf ("activating NODELETE for %s [%lu]\n", +- imap->l_name, imap->l_ns); +- +- /* Only new objects should have set +- link_map_nodelete_pending. Existing objects should not +- have gained any new dependencies and therefore cannot +- reach NODELETE status. */ +- assert (!imap->l_init_called || imap->l_type != lt_loaded); ++ /* It is necessary to traverse the entire namespace. References to ++ objects in the global scope and unique symbol bindings can force ++ NODELETE status for objects outside the local scope. */ ++ for (struct link_map *l = GL (dl_ns)[new->l_ns]._ns_loaded; l != NULL; ++ l = l->l_next) ++ if (l->l_nodelete == link_map_nodelete_pending) ++ { ++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)) ++ _dl_debug_printf ("activating NODELETE for %s [%lu]\n", ++ l->l_name, l->l_ns); + +- imap->l_nodelete = link_map_nodelete_active; +- } +- } ++ l->l_nodelete = link_map_nodelete_active; ++ } + } + + /* struct dl_init_args and call_dl_init are used to call _dl_init with +@@ -721,7 +708,7 @@ dl_open_worker (void *a) + All memory allocations for new objects must have happened + before. */ + +- activate_nodelete (new, mode); ++ activate_nodelete (new); + + /* Second stage after resize_scopes: Actually perform the scope + update. After this, dlsym and lazy binding can bind to new +diff --git a/elf/tst-dlopen-nodelete-reloc-mod1.c b/elf/tst-dlopen-nodelete-reloc-mod1.c +new file mode 100644 +index 0000000000000000..8927a9f851410268 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod1.c +@@ -0,0 +1,39 @@ ++/* Test propagation of NODELETE to an already-loaded object via relocation. ++ Non-NODELETE helper module. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include ++#include ++#include ++ ++/* Globally exported. Set by the main program to true before ++ termination, and used by tst-dlopen-nodelete-reloc-mod2.so to ++ trigger marking his module as NODELETE (and also for its destructor ++ check). */ ++bool may_finalize_mod1 = false; ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod1) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod1.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod10.c b/elf/tst-dlopen-nodelete-reloc-mod10.c +new file mode 100644 +index 0000000000000000..30748b73ec7daed3 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod10.c +@@ -0,0 +1,41 @@ ++/* Helper module to load tst-dlopen-nodelete-reloc-mod11.so. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++static void *handle; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ handle = dlopen ("tst-dlopen-nodelete-reloc-mod11.so", RTLD_NOW); ++ if (handle == NULL) ++ { ++ printf ("error: dlopen in module 10: %s\n", dlerror ()); ++ _exit (1); ++ } ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ dlclose (handle); ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod11.cc b/elf/tst-dlopen-nodelete-reloc-mod11.cc +new file mode 100644 +index 0000000000000000..48c910403e782c83 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod11.cc +@@ -0,0 +1,49 @@ ++/* Second module defining a unique symbol (loaded indirectly). ++ Copyright (C) 2019 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 ++ . */ ++ ++#include "tst-dlopen-nodelete-reloc.h" ++ ++#include ++#include ++#include ++ ++/* Just a flag here, not used for NODELETE processing. */ ++bool may_finalize_mod11 = false; ++ ++/* Trigger the creation of a unique symbol reference. This should ++ cause tst-dlopen-nodelete-reloc-mod9.so to be marked as ++ NODELETE. */ ++ ++extern template struct unique_symbol<9>; ++ ++int ++global_function_mod11 (void) ++{ ++ return unique_symbol<9>::value; ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod11) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod11.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod12.cc b/elf/tst-dlopen-nodelete-reloc-mod12.cc +new file mode 100644 +index 0000000000000000..5c093fd02d1fd0c7 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod12.cc +@@ -0,0 +1,42 @@ ++/* First module for NODELETE test defining a unique symbol (with DT_NEEDED). ++ Copyright (C) 2019 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 ++ . */ ++ ++#include "tst-dlopen-nodelete-reloc.h" ++ ++#include ++#include ++#include ++ ++/* Just a flag here, not used for NODELETE processing. */ ++bool may_finalize_mod12 = false; ++ ++/* Explicit instantiation. This produces a unique symbol definition ++ which is not referenced by the library itself, so the library is ++ not marked NODELETE. */ ++template struct unique_symbol<12>; ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod12) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod12.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod13.cc b/elf/tst-dlopen-nodelete-reloc-mod13.cc +new file mode 100644 +index 0000000000000000..caf4fd1cc9e1c1e1 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod13.cc +@@ -0,0 +1,48 @@ ++/* Second module for NODELETE test defining a unique symbol (with DT_NEEDED). ++ Copyright (C) 2019 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 ++ . */ ++ ++#include "tst-dlopen-nodelete-reloc.h" ++ ++#include ++#include ++#include ++ ++/* Just a flag here, not used for NODELETE processing. */ ++bool may_finalize_mod13 = false; ++ ++extern template struct unique_symbol<12>; ++ ++/* Trigger the creation of a unique symbol reference. This should ++ cause tst-dlopen-nodelete-reloc-mod12.so to be marked as ++ NODELETE. */ ++int ++global_function_mod13 (void) ++{ ++ return unique_symbol<12>::value; ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod13) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod13.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod13.h b/elf/tst-dlopen-nodelete-reloc-mod13.h +new file mode 100644 +index 0000000000000000..5d338481a34a5714 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod13.h +@@ -0,0 +1,24 @@ ++/* Inline function which produces a unique symbol. ++ Copyright (C) 2019 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 ++ . */ ++ ++inline char * ++third_function_with_local_static (void) ++{ ++ static char local; ++ return &local; ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod14.cc b/elf/tst-dlopen-nodelete-reloc-mod14.cc +new file mode 100644 +index 0000000000000000..e67621a2a2f8509a +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod14.cc +@@ -0,0 +1,42 @@ ++/* This object must retain NODELETE status after a dlopen failure. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include "tst-dlopen-nodelete-reloc.h" ++ ++#include ++#include ++#include ++ ++/* Just a flag here, not used for NODELETE processing. */ ++bool may_finalize_mod14 = false; ++ ++/* Explicit instantiation. This produces a unique symbol definition ++ which is not referenced by the library itself, so the library is ++ not marked NODELETE. */ ++template struct unique_symbol<14>; ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod14) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod14.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod15.cc b/elf/tst-dlopen-nodelete-reloc-mod15.cc +new file mode 100644 +index 0000000000000000..ff7a64edd1813119 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod15.cc +@@ -0,0 +1,41 @@ ++/* Helper object to mark tst-dlopen-nodelete-reloc-mod14.so as NODELETE. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include "tst-dlopen-nodelete-reloc.h" ++ ++#include ++#include ++#include ++ ++extern template struct unique_symbol<14>; ++ ++/* Trigger the creation of a unique symbol reference. This should ++ cause tst-dlopen-nodelete-reloc-mod14.so to be marked as ++ NODELETE. */ ++int ++global_function_mod15 (void) ++{ ++ return unique_symbol<14>::value; ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ /* This object is never loaded completely. */ ++ puts ("error: tst-dlopen-nodelete-reloc-mod15.so destructor invoked"); ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod16.c b/elf/tst-dlopen-nodelete-reloc-mod16.c +new file mode 100644 +index 0000000000000000..f836f04fb5420286 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod16.c +@@ -0,0 +1,27 @@ ++/* Object with an undefined symbol to trigger a relocation failure. ++ Copyright (C) 2019 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 ++ . */ ++ ++/* The reference to undefined_mod16 triggers a relocation failure. */ ++ ++extern int undefined_mod16; ++ ++int ++global_function_mod15 (void) ++{ ++ return undefined_mod16; ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod17.c b/elf/tst-dlopen-nodelete-reloc-mod17.c +new file mode 100644 +index 0000000000000000..426562edd9a3ffee +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod17.c +@@ -0,0 +1,19 @@ ++/* Top-level object with dependency on an object that fails relocation. ++ Copyright (C) 2019 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 ++ . */ ++ ++/* The dependencies do all the work. */ +diff --git a/elf/tst-dlopen-nodelete-reloc-mod2.c b/elf/tst-dlopen-nodelete-reloc-mod2.c +new file mode 100644 +index 0000000000000000..81ea8e5af2d00b93 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod2.c +@@ -0,0 +1,38 @@ ++/* Test propagation of NODELETE to an already-loaded object via relocation. ++ NODELETE helper module. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include ++#include ++#include ++ ++/* Defined in tst-dlopen-nodelete-reloc-mod1.so. This dependency is ++ not expressed via DT_NEEDED, so this reference marks the other ++ object as NODELETE dynamically, during initially relocation. */ ++extern bool may_finalize_mod1; ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod1) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod2.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod3.c b/elf/tst-dlopen-nodelete-reloc-mod3.c +new file mode 100644 +index 0000000000000000..d33f4ec7630c6a1e +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod3.c +@@ -0,0 +1,38 @@ ++/* Test propagation of NODELETE to an already-loaded object via relocation. ++ Non-NODELETE helper module. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include ++#include ++#include ++ ++/* Globally exported. Set by the main program to true before ++ termination, and used by tst-dlopen-nodelete-reloc-mod4.so, ++ tst-dlopen-nodelete-reloc-mod5.so. */ ++bool may_finalize_mod3 = false; ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod3) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod3.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod4.c b/elf/tst-dlopen-nodelete-reloc-mod4.c +new file mode 100644 +index 0000000000000000..7e6633aebb1e2f00 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod4.c +@@ -0,0 +1,37 @@ ++/* Test propagation of NODELETE to an already-loaded object via relocation. ++ Intermediate helper module. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include ++#include ++#include ++ ++/* Defined in tst-dlopen-nodelete-reloc-mod3.so. The dependency is ++ expressed via DT_NEEDED. */ ++extern bool may_finalize_mod3; ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod3) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod4.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod5.c b/elf/tst-dlopen-nodelete-reloc-mod5.c +new file mode 100644 +index 0000000000000000..f876fa0f97c48b5c +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod5.c +@@ -0,0 +1,38 @@ ++/* Test propagation of NODELETE to an already-loaded object via relocation. ++ NODELETE helper module. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include ++#include ++#include ++ ++/* Defined in tst-dlopen-nodelete-reloc-mod3.so. The dependency is ++ expressed via DT_NEEDED on the intermedia DSO ++ tst-dlopen-nodelete-reloc-mod3.so. */ ++extern bool may_finalize_mod3; ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod3) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod5.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod6.cc b/elf/tst-dlopen-nodelete-reloc-mod6.cc +new file mode 100644 +index 0000000000000000..180f5b5842f1c2b0 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod6.cc +@@ -0,0 +1,42 @@ ++/* First module for NODELETE test defining a unique symbol. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include "tst-dlopen-nodelete-reloc.h" ++ ++#include ++#include ++#include ++ ++/* Just a flag here, not used for NODELETE processing. */ ++bool may_finalize_mod6 = false; ++ ++/* Explicit instantiation. This produces a unique symbol definition ++ which is not referenced by the library itself, so the library is ++ not marked NODELETE. */ ++template struct unique_symbol<6>; ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod6) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod6.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod7.cc b/elf/tst-dlopen-nodelete-reloc-mod7.cc +new file mode 100644 +index 0000000000000000..c85e7c991b098bf5 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod7.cc +@@ -0,0 +1,48 @@ ++/* Second module for NODELETE test defining a unique symbol. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include "tst-dlopen-nodelete-reloc.h" ++ ++#include ++#include ++#include ++ ++/* Just a flag here, not used for NODELETE processing. */ ++bool may_finalize_mod7 = false; ++ ++extern template struct unique_symbol<6>; ++ ++/* Trigger the creation of a unique symbol reference. This should ++ cause tst-dlopen-nodelete-reloc-mod6.so to be marked as ++ NODELETE. */ ++int ++global_function_mod7 (void) ++{ ++ return unique_symbol<6>::value; ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod7) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod7.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod8.c b/elf/tst-dlopen-nodelete-reloc-mod8.c +new file mode 100644 +index 0000000000000000..ebb1c35fab57e319 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod8.c +@@ -0,0 +1,41 @@ ++/* Helper module to load tst-dlopen-nodelete-reloc-mod9.so. ++ Copyright (C) 2019 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++static void *handle; ++ ++static void __attribute__ ((constructor)) ++init (void) ++{ ++ handle = dlopen ("tst-dlopen-nodelete-reloc-mod9.so", RTLD_NOW); ++ if (handle == NULL) ++ { ++ printf ("error: dlopen in module 8: %s\n", dlerror ()); ++ _exit (1); ++ } ++} ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ dlclose (handle); ++} +diff --git a/elf/tst-dlopen-nodelete-reloc-mod9.cc b/elf/tst-dlopen-nodelete-reloc-mod9.cc +new file mode 100644 +index 0000000000000000..06fb49cdf753cb41 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc-mod9.cc +@@ -0,0 +1,42 @@ ++/* First module defining a unique symbol (loaded indirectly). ++ Copyright (C) 2019 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 ++ . */ ++ ++#include "tst-dlopen-nodelete-reloc.h" ++ ++#include ++#include ++#include ++ ++/* Just a flag here, not used for NODELETE processing. */ ++bool may_finalize_mod9 = false; ++ ++/* Explicit instantiation. This produces a unique symbol definition ++ which is not referenced by the library itself, so the library is ++ not marked NODELETE. */ ++template struct unique_symbol<9>; ++ ++static void __attribute__ ((destructor)) ++fini (void) ++{ ++ if (!may_finalize_mod9) ++ { ++ puts ("error: tst-dlopen-nodelete-reloc-mod9.so destructor" ++ " called too early"); ++ _exit (1); ++ } ++} +diff --git a/elf/tst-dlopen-nodelete-reloc.c b/elf/tst-dlopen-nodelete-reloc.c +new file mode 100644 +index 0000000000000000..d3de90a9f5d3cca8 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc.c +@@ -0,0 +1,178 @@ ++/* Test interactions of dlopen, NODELETE, and relocations. ++ Copyright (C) 2019 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 ++ . */ ++ ++/* This test exercises NODELETE propagation due to data relocations ++ and unique symbols, and the interaction with already-loaded ++ objects. Some test objects are written in C++, to produce unique ++ symbol definitions. ++ ++ First test: Global scope variant, data relocation as the NODELETE ++ trigger. mod1 is loaded first with a separate dlopen call. ++ ++ mod2 ---(may_finalize_mod1 relocation dependency)---> mod1 ++ (NODELETE) (marked as NODELETE) ++ ++ Second test: Local scope variant, data relocation. mod3 is loaded ++ first, then mod5. ++ ++ mod5 ---(DT_NEEDED)---> mod4 ---(DT_NEEDED)---> mod3 ++ (NODELETE) (not NODELETE) ^ ++ \ / (marked as ++ `--(may_finalize_mod3 relocation dependency)--/ NODELETE) ++ ++ Third test: Shared local scope with unique symbol. mod6 is loaded ++ first, then mod7. No explicit dependencies between the two ++ objects, so first object has to be opened with RTLD_GLOBAL. ++ ++ mod7 ---(unique symbol)---> mod6 ++ (marked as NODELETE) ++ ++ Forth test: Non-shared scopes with unique symbol. mod8 and mod10 ++ are loaded from the main program. mod8 loads mod9 from an ELF ++ constructor, mod10 loads mod11. There are no DT_NEEDED ++ dependencies. mod9 is promoted to the global scope form the main ++ program. The unique symbol dependency is: ++ ++ mod9 ---(unique symbol)---> mod11 ++ (marked as NODELETE) ++ ++ Fifth test: Shared local scope with unique symbol, like test 3, but ++ this time, there is also a DT_NEEDED dependency (so no RTLD_GLOBAL ++ needed): ++ ++ DT_NEEDED ++ mod13 ---(unique symbol)---> mod12 ++ (marked as NODELETE) ++ ++ Sixth test: NODELETE status is retained after relocation failure ++ with unique symbol dependency. The object graph ensures that the ++ unique symbol binding is processed before the dlopen failure. ++ ++ DT_NEEDED ++ mod17 --(DT_NEEDED)--> mod15 --(unique symbol)--> mod14 ++ \ ^ (RTLD_NODELETE) ++ \ (DT_NEEDED) ++ \ | ++ `---(DT_NEEDED)--> mod16 ++ (fails to relocate) ++ ++ mod14 must remain NODELETE after opening mod17 failed. */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++static int ++do_test (void) ++{ ++ /* First case: global scope, regular data symbol. Open the object ++ which is not NODELETE initially. */ ++ void *mod1 = xdlopen ("tst-dlopen-nodelete-reloc-mod1.so", ++ RTLD_NOW | RTLD_GLOBAL); ++ /* This is used to indicate that the ELF destructor may be ++ called. */ ++ bool *may_finalize_mod1 = xdlsym (mod1, "may_finalize_mod1"); ++ /* Open the NODELETE object. */ ++ void *mod2 = xdlopen ("tst-dlopen-nodelete-reloc-mod2.so", RTLD_NOW); ++ /* This has no effect because the DSO is directly marked as ++ NODELETE. */ ++ xdlclose (mod2); ++ /* This has no effect because the DSO has been indirectly marked as ++ NODELETE due to a relocation dependency. */ ++ xdlclose (mod1); ++ ++ /* Second case: local scope, regular data symbol. Open the object ++ which is not NODELETE initially. */ ++ void *mod3 = xdlopen ("tst-dlopen-nodelete-reloc-mod3.so", RTLD_NOW); ++ bool *may_finalize_mod3 = xdlsym (mod3, "may_finalize_mod3"); ++ /* Open the NODELETE object. */ ++ void *mod5 = xdlopen ("tst-dlopen-nodelete-reloc-mod5.so", RTLD_NOW); ++ /* Again those have no effect because of NODELETE. */ ++ xdlclose (mod5); ++ xdlclose (mod3); ++ ++ /* Third case: Unique symbol. */ ++ void *mod6 = xdlopen ("tst-dlopen-nodelete-reloc-mod6.so", ++ RTLD_NOW | RTLD_GLOBAL); ++ bool *may_finalize_mod6 = xdlsym (mod6, "may_finalize_mod6"); ++ void *mod7 = xdlopen ("tst-dlopen-nodelete-reloc-mod7.so", RTLD_NOW); ++ bool *may_finalize_mod7 = xdlsym (mod7, "may_finalize_mod7"); ++ /* This should not have any effect because of the unique symbol and ++ the resulting NODELETE status. */ ++ xdlclose (mod6); ++ /* mod7 is not NODELETE and can be closed. */ ++ *may_finalize_mod7 = true; ++ xdlclose (mod7); ++ ++ /* Fourth case: Unique symbol, indirect loading. */ ++ void *mod8 = xdlopen ("tst-dlopen-nodelete-reloc-mod8.so", RTLD_NOW); ++ /* Also promote to global scope. */ ++ void *mod9 = xdlopen ("tst-dlopen-nodelete-reloc-mod9.so", ++ RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL); ++ bool *may_finalize_mod9 = xdlsym (mod9, "may_finalize_mod9"); ++ xdlclose (mod9); /* Drop mod9 reference. */ ++ void *mod10 = xdlopen ("tst-dlopen-nodelete-reloc-mod10.so", RTLD_NOW); ++ void *mod11 = xdlopen ("tst-dlopen-nodelete-reloc-mod11.so", ++ RTLD_NOW | RTLD_NOLOAD); ++ bool *may_finalize_mod11 = xdlsym (mod11, "may_finalize_mod11"); ++ xdlclose (mod11); /* Drop mod11 reference. */ ++ /* mod11 is not NODELETE and can be closed. */ ++ *may_finalize_mod11 = true; ++ /* Trigger closing of mod11, too. */ ++ xdlclose (mod10); ++ /* Does not trigger closing of mod9. */ ++ xdlclose (mod8); ++ ++ /* Fifth case: Unique symbol, with DT_NEEDED dependency. */ ++ void *mod12 = xdlopen ("tst-dlopen-nodelete-reloc-mod12.so", RTLD_NOW); ++ bool *may_finalize_mod12 = xdlsym (mod12, "may_finalize_mod12"); ++ void *mod13 = xdlopen ("tst-dlopen-nodelete-reloc-mod13.so", RTLD_NOW); ++ bool *may_finalize_mod13 = xdlsym (mod13, "may_finalize_mod13"); ++ /* This should not have any effect because of the unique symbol. */ ++ xdlclose (mod12); ++ /* mod13 is not NODELETE and can be closed. */ ++ *may_finalize_mod13 = true; ++ xdlclose (mod13); ++ ++ /* Sixth case: Unique symbol binding must not cause loss of NODELETE ++ status. */ ++ void *mod14 = xdlopen ("tst-dlopen-nodelete-reloc-mod14.so", ++ RTLD_NOW | RTLD_NODELETE); ++ bool *may_finalize_mod14 = xdlsym (mod14, "may_finalize_mod14"); ++ TEST_VERIFY (dlopen ("tst-dlopen-nodelete-reloc-mod17.so", RTLD_NOW) ++ == NULL); ++ const char *message = dlerror (); ++ printf ("info: test 6 message: %s\n", message); ++ /* This must not close the object, it must still be NODELETE. */ ++ xdlclose (mod14); ++ xdlopen ("tst-dlopen-nodelete-reloc-mod14.so", RTLD_NOW | RTLD_NOLOAD); ++ ++ /* Prepare for process exit. Destructors for NODELETE objects will ++ be invoked. */ ++ *may_finalize_mod1 = true; ++ *may_finalize_mod3 = true; ++ *may_finalize_mod6 = true; ++ *may_finalize_mod9 = true; ++ *may_finalize_mod12 = true; ++ *may_finalize_mod14 = true; ++ return 0; ++} ++ ++#include +diff --git a/elf/tst-dlopen-nodelete-reloc.h b/elf/tst-dlopen-nodelete-reloc.h +new file mode 100644 +index 0000000000000000..8844de622631f575 +--- /dev/null ++++ b/elf/tst-dlopen-nodelete-reloc.h +@@ -0,0 +1,35 @@ ++/* Template to produce unique symbols. ++ Copyright (C) 2019 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 ++ . */ ++ ++/* This template produces a unique symbol definition for an explicit ++ template instantiation (without also incorporating a reference), ++ and an extern template declaration can be used to reference that ++ symbol from another object. The modid parameter is just a ++ placeholder to create different symbols (because it affects the ++ name mangling of the static value member). By convention, it ++ should match the number of the module that contains the ++ definition. */ ++ ++template ++struct unique_symbol ++{ ++ static int value; ++}; ++ ++template ++int unique_symbol::value; diff --git a/glibc-dlopen-nodelete-fixes-2.patch b/glibc-dlopen-nodelete-fixes-2.patch new file mode 100644 index 0000000..54fdafa --- /dev/null +++ b/glibc-dlopen-nodelete-fixes-2.patch @@ -0,0 +1,296 @@ +Author: Florian Weimer +Date: Thu Dec 5 13:32:42 2019 +0100 + + dlopen: Rework handling of pending NODELETE status + + To avoid a read-modify-write cycle on the l_nodelete field, this + commit introduces two flags for active NODELETE status (irrevocable) + and pending NODELETE status (revocable until activate_nodelete) is + invoked. As a result, NODELETE processing in dlopen does not + introduce further reasons why lazy binding from signal handlers + is unsafe during dlopen, and a subsequent commit can remove signal + blocking from dlopen. + +diff --git a/elf/dl-close.c b/elf/dl-close.c +index e35a62daf6fa98a1..df1df6fb298b2319 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -197,7 +197,7 @@ _dl_close_worker (struct link_map *map, bool force) + /* Check whether this object is still used. */ + if (l->l_type == lt_loaded + && l->l_direct_opencount == 0 +- && l->l_nodelete != link_map_nodelete_active ++ && !l->l_nodelete_active + /* See CONCURRENCY NOTES in cxa_thread_atexit_impl.c to know why + acquire is sufficient and correct. */ + && atomic_load_acquire (&l->l_tls_dtor_count) == 0 +@@ -279,8 +279,7 @@ _dl_close_worker (struct link_map *map, bool force) + + if (!used[i]) + { +- assert (imap->l_type == lt_loaded +- && imap->l_nodelete != link_map_nodelete_active); ++ assert (imap->l_type == lt_loaded && !imap->l_nodelete_active); + + /* Call its termination function. Do not do it for + half-cooked objects. Temporarily disable exception +@@ -830,7 +829,7 @@ _dl_close (void *_map) + before we took the lock. There is no way to detect this (see below) + so we proceed assuming this isn't the case. First see whether we + can remove the object at all. */ +- if (__glibc_unlikely (map->l_nodelete == link_map_nodelete_active)) ++ if (__glibc_unlikely (map->l_nodelete_active)) + { + /* Nope. Do nothing. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); +diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c +index 99846918c3453694..759b45a2c977175a 100644 +--- a/elf/dl-lookup.c ++++ b/elf/dl-lookup.c +@@ -187,6 +187,28 @@ enter_unique_sym (struct unique_sym *table, size_t size, + table[idx].map = map; + } + ++/* Mark MAP as NODELETE according to the lookup mode in FLAGS. During ++ initial relocation, NODELETE state is pending only. */ ++static void ++mark_nodelete (struct link_map *map, int flags) ++{ ++ if (flags & DL_LOOKUP_FOR_RELOCATE) ++ map->l_nodelete_pending = true; ++ else ++ map->l_nodelete_active = true; ++} ++ ++/* Return true if MAP is marked as NODELETE according to the lookup ++ mode in FLAGS> */ ++static bool ++is_nodelete (struct link_map *map, int flags) ++{ ++ /* Non-pending NODELETE always counts. Pending NODELETE only counts ++ during initial relocation processing. */ ++ return map->l_nodelete_active ++ || ((flags & DL_LOOKUP_FOR_RELOCATE) && map->l_nodelete_pending); ++} ++ + /* Utility function for do_lookup_x. Lookup an STB_GNU_UNIQUE symbol + in the unique symbol table, creating a new entry if necessary. + Return the matching symbol in RESULT. */ +@@ -311,8 +333,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, + enter_unique_sym (entries, size, + new_hash, strtab + sym->st_name, sym, map); + +- if (map->l_type == lt_loaded +- && map->l_nodelete == link_map_nodelete_inactive) ++ if (map->l_type == lt_loaded && !is_nodelete (map, flags)) + { + /* Make sure we don't unload this object by + setting the appropriate flag. */ +@@ -320,10 +341,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, + _dl_debug_printf ("\ + marking %s [%lu] as NODELETE due to unique symbol\n", + map->l_name, map->l_ns); +- if (flags & DL_LOOKUP_FOR_RELOCATE) +- map->l_nodelete = link_map_nodelete_pending; +- else +- map->l_nodelete = link_map_nodelete_active; ++ mark_nodelete (map, flags); + } + } + ++tab->n_elements; +@@ -586,7 +604,7 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags) + dependencies may pick an dependency which can be dlclose'd, but + such IFUNC resolvers are undefined anyway. */ + assert (map->l_type == lt_loaded); +- if (map->l_nodelete != link_map_nodelete_inactive) ++ if (is_nodelete (map, flags)) + return 0; + + struct link_map_reldeps *l_reldeps +@@ -694,17 +712,16 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags) + + /* Redo the NODELETE check, as when dl_load_lock wasn't held + yet this could have changed. */ +- if (map->l_nodelete != link_map_nodelete_inactive) ++ if (is_nodelete (map, flags)) + goto out; + + /* If the object with the undefined reference cannot be removed ever + just make sure the same is true for the object which contains the + definition. */ +- if (undef_map->l_type != lt_loaded +- || (undef_map->l_nodelete != link_map_nodelete_inactive)) ++ if (undef_map->l_type != lt_loaded || is_nodelete (map, flags)) + { + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS) +- && map->l_nodelete == link_map_nodelete_inactive) ++ && !is_nodelete (map, flags)) + { + if (undef_map->l_name[0] == '\0') + _dl_debug_printf ("\ +@@ -716,11 +733,7 @@ marking %s [%lu] as NODELETE due to reference to %s [%lu]\n", + map->l_name, map->l_ns, + undef_map->l_name, undef_map->l_ns); + } +- +- if (flags & DL_LOOKUP_FOR_RELOCATE) +- map->l_nodelete = link_map_nodelete_pending; +- else +- map->l_nodelete = link_map_nodelete_active; ++ mark_nodelete (map, flags); + goto out; + } + +@@ -746,17 +759,14 @@ marking %s [%lu] as NODELETE due to reference to %s [%lu]\n", + cannot be unloaded. This is semantically the correct + behavior. */ + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS) +- && map->l_nodelete == link_map_nodelete_inactive) ++ && !is_nodelete (map, flags)) + _dl_debug_printf ("\ + marking %s [%lu] as NODELETE due to memory allocation failure\n", + map->l_name, map->l_ns); +- if (flags & DL_LOOKUP_FOR_RELOCATE) +- /* In case of non-lazy binding, we could actually +- report the memory allocation error, but for now, we +- use the conservative approximation as well. */ +- map->l_nodelete = link_map_nodelete_pending; +- else +- map->l_nodelete = link_map_nodelete_active; ++ /* In case of non-lazy binding, we could actually report ++ the memory allocation error, but for now, we use the ++ conservative approximation as well. */ ++ mark_nodelete (map, flags); + goto out; + } + else +diff --git a/elf/dl-open.c b/elf/dl-open.c +index 56f213323ce5cf65..c23341be5892fb12 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -440,13 +440,17 @@ activate_nodelete (struct link_map *new) + NODELETE status for objects outside the local scope. */ + for (struct link_map *l = GL (dl_ns)[new->l_ns]._ns_loaded; l != NULL; + l = l->l_next) +- if (l->l_nodelete == link_map_nodelete_pending) ++ if (l->l_nodelete_pending) + { + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("activating NODELETE for %s [%lu]\n", + l->l_name, l->l_ns); + +- l->l_nodelete = link_map_nodelete_active; ++ l->l_nodelete_active = true; ++ ++ /* This is just a debugging aid, to indicate that ++ activate_nodelete has run for this map. */ ++ l->l_nodelete_pending = false; + } + } + +@@ -549,10 +553,10 @@ dl_open_worker (void *a) + if (__glibc_unlikely (mode & RTLD_NODELETE)) + { + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES) +- && new->l_nodelete == link_map_nodelete_inactive) ++ && !new->l_nodelete_active) + _dl_debug_printf ("marking %s [%lu] as NODELETE\n", + new->l_name, new->l_ns); +- new->l_nodelete = link_map_nodelete_active; ++ new->l_nodelete_active = true; + } + + /* Finalize the addition to the global scope. */ +@@ -568,7 +572,7 @@ dl_open_worker (void *a) + /* Schedule NODELETE marking for the directly loaded object if + requested. */ + if (__glibc_unlikely (mode & RTLD_NODELETE)) +- new->l_nodelete = link_map_nodelete_pending; ++ new->l_nodelete_pending = true; + + /* Load that object's dependencies. */ + _dl_map_object_deps (new, NULL, 0, 0, +@@ -683,7 +687,7 @@ dl_open_worker (void *a) + _dl_start_profile (); + + /* Prevent unloading the object. */ +- GL(dl_profile_map)->l_nodelete = link_map_nodelete_active; ++ GL(dl_profile_map)->l_nodelete_active = true; + } + } + else +@@ -882,9 +886,9 @@ no more namespaces available for dlmopen()")); + happens inside dl_open_worker. */ + __libc_signal_restore_set (&args.original_signal_mask); + +- /* All link_map_nodelete_pending objects should have been +- deleted at this point, which is why it is not necessary +- to reset the flag here. */ ++ /* All l_nodelete_pending objects should have been deleted ++ at this point, which is why it is not necessary to reset ++ the flag here. */ + } + else + __libc_signal_restore_set (&args.original_signal_mask); +diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h +index 075683d729447a40..ea4e304bef282a6f 100644 +--- a/elf/get-dynamic-info.h ++++ b/elf/get-dynamic-info.h +@@ -164,7 +164,7 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp) + { + l->l_flags_1 = info[VERSYMIDX (DT_FLAGS_1)]->d_un.d_val; + if (l->l_flags_1 & DF_1_NODELETE) +- l->l_nodelete = link_map_nodelete_pending; ++ l->l_nodelete_pending = true; + + /* Only DT_1_SUPPORTED_MASK bits are supported, and we would like + to assert this, but we can't. Users have been setting +diff --git a/include/link.h b/include/link.h +index 2e771e433a36e6cd..2acc8363802d6d64 100644 +--- a/include/link.h ++++ b/include/link.h +@@ -79,22 +79,6 @@ struct r_search_path_struct + int malloced; + }; + +-/* Type used by the l_nodelete member. */ +-enum link_map_nodelete +-{ +- /* This link map can be deallocated. */ +- link_map_nodelete_inactive = 0, /* Zero-initialized in _dl_new_object. */ +- +- /* This link map cannot be deallocated. */ +- link_map_nodelete_active, +- +- /* This link map cannot be deallocated after dlopen has succeded. +- dlopen turns this into link_map_nodelete_active. dlclose treats +- this intermediate state as link_map_nodelete_active. */ +- link_map_nodelete_pending, +-}; +- +- + /* Structure describing a loaded shared object. The `l_next' and `l_prev' + members form a chain of all the shared objects loaded at startup. + +@@ -218,10 +202,17 @@ struct link_map + freed, ie. not allocated with + the dummy malloc in ld.so. */ + +- /* Actually of type enum link_map_nodelete. Separate byte due to +- a read in add_dependency in elf/dl-lookup.c outside the loader +- lock. Only valid for l_type == lt_loaded. */ +- unsigned char l_nodelete; ++ /* NODELETE status of the map. Only valid for maps of type ++ lt_loaded. Lazy binding sets l_nodelete_active directly, ++ potentially from signal handlers. Initial loading of an ++ DF_1_NODELETE object set l_nodelete_pending. Relocation may ++ set l_nodelete_pending as well. l_nodelete_pending maps are ++ promoted to l_nodelete_active status in the final stages of ++ dlopen, prior to calling ELF constructors. dlclose only ++ refuses to unload l_nodelete_active maps, the pending status is ++ ignored. */ ++ bool l_nodelete_active; ++ bool l_nodelete_pending; + + #include + diff --git a/glibc-dlopen-nodelete-fixes-3.patch b/glibc-dlopen-nodelete-fixes-3.patch new file mode 100644 index 0000000..56252dd --- /dev/null +++ b/glibc-dlopen-nodelete-fixes-3.patch @@ -0,0 +1,130 @@ +Author: Florian Weimer +Date: Thu Dec 5 16:20:30 2019 +0100 + + dlopen: Do not block signals + + Blocking signals causes issues with certain anti-malware solutions + which rely on an unblocked SIGSYS signal for system calls they + intercept. + + This reverts commit a2e8aa0d9ea648068d8be52dd7b15f1b6a008e23 + ("Block signals during the initial part of dlopen") and adds + comments related to async signal safety to active_nodelete and + its caller. + + Note that this does not make lazy binding async-signal-safe with regards + to dlopen. It merely avoids introducing new async-signal-safety hazards + as part of the NODELETE changes. + +diff --git a/elf/dl-open.c b/elf/dl-open.c +index c23341be5892fb12..5a1c5b53269f1236 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -34,7 +34,6 @@ + #include + #include + #include +-#include + + #include + #include +@@ -53,10 +52,6 @@ struct dl_open_args + /* Namespace ID. */ + Lmid_t nsid; + +- /* Original signal mask. Used for unblocking signal handlers before +- running ELF constructors. */ +- sigset_t original_signal_mask; +- + /* Original value of _ns_global_scope_pending_adds. Set by + dl_open_worker. Only valid if nsid is a real namespace + (non-negative). */ +@@ -446,6 +441,9 @@ activate_nodelete (struct link_map *new) + _dl_debug_printf ("activating NODELETE for %s [%lu]\n", + l->l_name, l->l_ns); + ++ /* The flag can already be true at this point, e.g. a signal ++ handler may have triggered lazy binding and set NODELETE ++ status immediately. */ + l->l_nodelete_active = true; + + /* This is just a debugging aid, to indicate that +@@ -520,16 +518,12 @@ dl_open_worker (void *a) + if (new == NULL) + { + assert (mode & RTLD_NOLOAD); +- __libc_signal_restore_set (&args->original_signal_mask); + return; + } + + if (__glibc_unlikely (mode & __RTLD_SPROF)) +- { +- /* This happens only if we load a DSO for 'sprof'. */ +- __libc_signal_restore_set (&args->original_signal_mask); +- return; +- } ++ /* This happens only if we load a DSO for 'sprof'. */ ++ return; + + /* This object is directly loaded. */ + ++new->l_direct_opencount; +@@ -565,7 +559,6 @@ dl_open_worker (void *a) + + assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); + +- __libc_signal_restore_set (&args->original_signal_mask); + return; + } + +@@ -712,6 +705,12 @@ dl_open_worker (void *a) + All memory allocations for new objects must have happened + before. */ + ++ /* Finalize the NODELETE status first. This comes before ++ update_scopes, so that lazy binding will not see pending NODELETE ++ state for newly loaded objects. There is a compiler barrier in ++ update_scopes which ensures that the changes from ++ activate_nodelete are visible before new objects show up in the ++ local scope. */ + activate_nodelete (new); + + /* Second stage after resize_scopes: Actually perform the scope +@@ -745,10 +744,6 @@ dl_open_worker (void *a) + if (mode & RTLD_GLOBAL) + add_to_global_resize (new); + +- /* Unblock signals. Data structures are now consistent, and +- application code may run. */ +- __libc_signal_restore_set (&args->original_signal_mask); +- + /* Run the initializer functions of new objects. Temporarily + disable the exception handler, so that lazy binding failures are + fatal. */ +@@ -838,10 +833,6 @@ no more namespaces available for dlmopen()")); + args.argv = argv; + args.env = env; + +- /* Recursive lazy binding during manipulation of the dynamic loader +- structures may result in incorrect behavior. */ +- __libc_signal_block_all (&args.original_signal_mask); +- + struct dl_exception exception; + int errcode = _dl_catch_exception (&exception, dl_open_worker, &args); + +@@ -882,16 +873,10 @@ no more namespaces available for dlmopen()")); + + _dl_close_worker (args.map, true); + +- /* Restore the signal mask. In the success case, this +- happens inside dl_open_worker. */ +- __libc_signal_restore_set (&args.original_signal_mask); +- + /* All l_nodelete_pending objects should have been deleted + at this point, which is why it is not necessary to reset + the flag here. */ + } +- else +- __libc_signal_restore_set (&args.original_signal_mask); + + assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); + diff --git a/glibc.spec b/glibc.spec index 3edcf58..dcc2b86 100644 --- a/glibc.spec +++ b/glibc.spec @@ -87,7 +87,7 @@ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: 23%{?dist} +Release: 24%{?dist} # In general, GPLv2+ is used by programs, LGPLv2+ is used for # libraries. @@ -158,7 +158,9 @@ Patch17: glibc-cs-path.patch Patch18: glibc-c-utf8-locale.patch Patch23: glibc-python3.patch Patch29: glibc-fedora-nsswitch.patch -Patch30: glibc-rh1778344.patch +Patch30: glibc-dlopen-nodelete-fixes-1.patch +Patch31: glibc-dlopen-nodelete-fixes-2.patch +Patch32: glibc-dlopen-nodelete-fixes-3.patch ############################################################################## # Continued list of core "glibc" package information: @@ -2042,6 +2044,9 @@ fi %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared %changelog +* Thu Dec 5 2019 Florian Weimer - 2.30.9000-24 +- Upstream patches for fallout from dlopen NODELETE changes (#1778344, #1778366) + * Wed Dec 04 2019 Patsy Franklin - 2.30.9000-23 - Auto-sync with upstream branch master, commit ec138c67cbda8b5826a0a2a7ba456408117996dc.