Login
Log in using an SSO provider:
Fedora Account System
Red Hat Associate
Red Hat Customer
Login using a Red Hat Bugzilla account
Forgot Password
Create an Account
Red Hat Bugzilla – Attachment 1643297 Details for
Bug 1410154
glibc: Incomplete rollback of dynamic linker state on dlopen failure (NODELETE bug)
Home
New
Search
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh90 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
[?]
This site requires JavaScript to be enabled to function correctly, please enable it.
Proposed patch
bz1410154.2.patch (text/plain), 151.68 KB, created by
Florian Weimer
on 2019-12-09 13:21:02 UTC
(
hide
)
Description:
Proposed patch
Filename:
MIME Type:
Creator:
Florian Weimer
Created:
2019-12-09 13:21:02 UTC
Size:
151.68 KB
patch
obsolete
>commit aea37fba5dc4c14cdb2e8933e54b8525f8e931b5 >Author: Florian Weimer <fweimer@redhat.com> >Date: Mon Dec 9 14:18:54 2019 +0100 > > Roll back dynamic linker state on dlopen failure (#1410154) > > Resolves: #1410154 > >diff --git a/glibc-rh1410154-1.patch b/glibc-rh1410154-1.patch >new file mode 100644 >index 00000000..6acd1674 >--- /dev/null >+++ b/glibc-rh1410154-1.patch >@@ -0,0 +1,185 @@ >+commit 96cd0558bcd69481ccc42e1b392f0c0b36fce2b0 >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Wed Nov 28 19:59:45 2018 +0100 >+ >+ support: Add signal support to support_capture_subprocess_check >+ >+ Signal zero does not terminate a process, so it is safe to use negative >+ values for signal numbers. >+ >+ Adjust libio/tst-vtables-common.c to use this new functionality, >+ instead of determining the termination status for a signal indirectly. >+ >+diff --git a/libio/tst-vtables-common.c b/libio/tst-vtables-common.c >+index 5e3101206919fa1b..85e246cd1131f8e8 100644 >+--- a/libio/tst-vtables-common.c >++++ b/libio/tst-vtables-common.c >+@@ -380,21 +380,6 @@ without_compatibility_fflush (void *closure) >+ _exit (1); >+ } >+ >+-/* Exit status after abnormal termination. */ >+-static int termination_status; >+- >+-static void >+-init_termination_status (void) >+-{ >+- pid_t pid = xfork (); >+- if (pid == 0) >+- abort (); >+- xwaitpid (pid, &termination_status, 0); >+- >+- TEST_VERIFY (WIFSIGNALED (termination_status)); >+- TEST_COMPARE (WTERMSIG (termination_status), SIGABRT); >+-} >+- >+ static void >+ check_for_termination (const char *name, void (*callback) (void *)) >+ { >+@@ -404,7 +389,7 @@ check_for_termination (const char *name, void (*callback) (void *)) >+ shared->calls = 0; >+ struct support_capture_subprocess proc >+ = support_capture_subprocess (callback, NULL); >+- support_capture_subprocess_check (&proc, name, termination_status, >++ support_capture_subprocess_check (&proc, name, -SIGABRT, >+ sc_allow_stderr); >+ const char *message >+ = "Fatal error: glibc detected an invalid stdio handle\n"; >+@@ -491,7 +476,6 @@ run_tests (bool initially_disabled) >+ >+ shared = support_shared_allocate (sizeof (*shared)); >+ shared->initially_disabled = initially_disabled; >+- init_termination_status (); >+ >+ if (initially_disabled) >+ { >+diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h >+index d5eac84d09ae325f..2d2384e73df0d2d0 100644 >+--- a/support/capture_subprocess.h >++++ b/support/capture_subprocess.h >+@@ -55,13 +55,16 @@ enum support_capture_allow >+ sc_allow_stderr = 0x04, >+ }; >+ >+-/* Check that the subprocess exited with STATUS and that only the >+- allowed outputs happened. ALLOWED is a combination of >+- support_capture_allow flags. Report errors under the CONTEXT >+- message. */ >++/* Check that the subprocess exited and that only the allowed outputs >++ happened. If STATUS_OR_SIGNAL is nonnegative, it is the expected >++ (decoded) exit status of the process, as returned by WEXITSTATUS. >++ If STATUS_OR_SIGNAL is negative, -STATUS_OR_SIGNAL is the expected >++ termination signal, as returned by WTERMSIG. ALLOWED is a >++ combination of support_capture_allow flags. Report errors under >++ the CONTEXT message. */ >+ void support_capture_subprocess_check (struct support_capture_subprocess *, >+- const char *context, int status, >+- int allowed) >++ const char *context, >++ int status_or_signal, int allowed) >+ __attribute__ ((nonnull (1, 2))); >+ >+ #endif /* SUPPORT_CAPTURE_SUBPROCESS_H */ >+diff --git a/support/support_capture_subprocess_check.c b/support/support_capture_subprocess_check.c >+index ff5ee89fb02599ae..8b4c352c96227b78 100644 >+--- a/support/support_capture_subprocess_check.c >++++ b/support/support_capture_subprocess_check.c >+@@ -20,6 +20,7 @@ >+ #include <stdio.h> >+ #include <support/capture_subprocess.h> >+ #include <support/check.h> >++#include <sys/wait.h> >+ >+ static void >+ print_context (const char *context, bool *failed) >+@@ -31,9 +32,22 @@ print_context (const char *context, bool *failed) >+ printf ("error: subprocess failed: %s\n", context); >+ } >+ >++static void >++print_actual_status (struct support_capture_subprocess *proc) >++{ >++ if (WIFEXITED (proc->status)) >++ printf ("error: actual exit status: %d [0x%x]\n", >++ WEXITSTATUS (proc->status), proc->status); >++ else if (WIFSIGNALED (proc->status)) >++ printf ("error: actual termination signal: %d [0x%x]\n", >++ WTERMSIG (proc->status), proc->status); >++ else >++ printf ("error: actual undecoded exit status: [0x%x]\n", proc->status); >++} >++ >+ void >+ support_capture_subprocess_check (struct support_capture_subprocess *proc, >+- const char *context, int status, >++ const char *context, int status_or_signal, >+ int allowed) >+ { >+ TEST_VERIFY ((allowed & sc_allow_none) >+@@ -44,11 +58,28 @@ support_capture_subprocess_check (struct support_capture_subprocess *proc, >+ || (allowed & sc_allow_stderr)))); >+ >+ bool failed = false; >+- if (proc->status != status) >++ if (status_or_signal >= 0) >+ { >+- print_context (context, &failed); >+- printf ("error: expected exit status: %d\n", status); >+- printf ("error: actual exit status: %d\n", proc->status); >++ /* Expect regular termination. */ >++ if (!(WIFEXITED (proc->status) >++ && WEXITSTATUS (proc->status) == status_or_signal)) >++ { >++ print_context (context, &failed); >++ printf ("error: expected exit status: %d\n", status_or_signal); >++ print_actual_status (proc); >++ } >++ } >++ else >++ { >++ /* status_or_signal < 0. Expect termination by signal. */ >++ if (!(WIFSIGNALED (proc->status) >++ && WTERMSIG (proc->status) == -status_or_signal)) >++ { >++ print_context (context, &failed); >++ printf ("error: expected termination signal: %d\n", >++ -status_or_signal); >++ print_actual_status (proc); >++ } >+ } >+ if (!(allowed & sc_allow_stdout) && proc->out.length != 0) >+ { >+diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c >+index 63b6699622f97fcc..99570879eedd65b1 100644 >+--- a/support/tst-support_capture_subprocess.c >++++ b/support/tst-support_capture_subprocess.c >+@@ -285,15 +285,29 @@ do_multiple_tests (enum test_type type) >+ >+ check_stream ("stdout", &result.out, test.out); >+ check_stream ("stderr", &result.err, test.err); >++ >++ /* Allowed output for support_capture_subprocess_check. */ >++ int check_allow = 0; >++ if (lengths[length_idx_stdout] > 0) >++ check_allow |= sc_allow_stdout; >++ if (lengths[length_idx_stderr] > 0) >++ check_allow |= sc_allow_stderr; >++ if (check_allow == 0) >++ check_allow = sc_allow_none; >++ >+ if (test.signal != 0) >+ { >+ TEST_VERIFY (WIFSIGNALED (result.status)); >+ TEST_VERIFY (WTERMSIG (result.status) == test.signal); >++ support_capture_subprocess_check (&result, "signal", >++ -SIGTERM, check_allow); >+ } >+ else >+ { >+ TEST_VERIFY (WIFEXITED (result.status)); >+ TEST_VERIFY (WEXITSTATUS (result.status) == test.status); >++ support_capture_subprocess_check (&result, "exit", >++ test.status, check_allow); >+ } >+ support_capture_subprocess_free (&result); >+ free (test.out); >diff --git a/glibc-rh1410154-10.patch b/glibc-rh1410154-10.patch >new file mode 100644 >index 00000000..9025c9e5 >--- /dev/null >+++ b/glibc-rh1410154-10.patch >@@ -0,0 +1,42 @@ >+commit e37c2cf299b61ce18f62852f6c5624c27829b610 >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Thu Oct 31 18:48:43 2019 +0100 >+ >+ Move _dl_open_check to its original place in dl_open_worker >+ >+ This reverts the non-test change from commit d0093c5cefb7f7a4143f >+ ("Call _dl_open_check after relocation [BZ #24259]"), given that >+ the underlying bug has been fixed properly in commit 61b74477fa7f63 >+ ("Remove all loaded objects if dlopen fails, ignoring NODELETE >+ [BZ #20839]"). >+ >+ Tested on x86-64-linux-gnu, with and without --enable-cet. >+ >+ Change-Id: I995a6cfb89f25d2b0cf5e606428c2a93eb48fc33 >+ >+diff --git a/elf/dl-open.c b/elf/dl-open.c >+index 25838b073ac1edaf..e13968d4d7c4c83f 100644 >+--- a/elf/dl-open.c >++++ b/elf/dl-open.c >+@@ -619,6 +619,8 @@ dl_open_worker (void *a) >+ _dl_debug_state (); >+ LIBC_PROBE (map_complete, 3, args->nsid, r, new); >+ >++ _dl_open_check (new); >++ >+ /* Print scope information. */ >+ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES)) >+ _dl_show_scope (new, 0); >+@@ -699,12 +701,6 @@ dl_open_worker (void *a) >+ _dl_relocate_object (l, l->l_scope, reloc_mode, 0); >+ } >+ >+- /* NB: Workaround for [BZ #20839] which doesn't remove the NODELETE >+- object when _dl_open_check throws an exception. Move it after >+- relocation to avoid leaving the NODELETE object mapped without >+- relocation. */ >+- _dl_open_check (new); >+- >+ /* This only performs the memory allocations. The actual update of >+ the scopes happens below, after failure is impossible. */ >+ resize_scopes (new); >diff --git a/glibc-rh1410154-11.patch b/glibc-rh1410154-11.patch >new file mode 100644 >index 00000000..c3a16f3d >--- /dev/null >+++ b/glibc-rh1410154-11.patch >@@ -0,0 +1,27 @@ >+commit 61a7c9df71ee4e6f94b56c20f0d37c6e17d5f284 >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Mon Dec 2 14:53:16 2019 +0100 >+ >+ elf/tst-dlopenfail: Disable --no-as-needed for tst-dlopenfailmod1.so >+ >+ Otherwise, the shared object dependency which triggers the load >+ failure is dropped, invalidating the test. >+ >+diff --git a/elf/Makefile b/elf/Makefile >+index bf7c41f38be42184..467e810e784bb96d 100644 >+--- a/elf/Makefile >++++ b/elf/Makefile >+@@ -1543,8 +1543,11 @@ LDFLAGS-tst-finilazyfailmod.so = \ >+ $(objpfx)tst-dlopenfail: $(libdl) >+ $(objpfx)tst-dlopenfail.out: \ >+ $(objpfx)tst-dlopenfailmod1.so $(objpfx)tst-dlopenfailmod2.so >+-# Order matters here. tst-dlopenfaillinkmod.so's soname ensures >+-# a run-time loader failure. >++# Order matters here. tst-dlopenfaillinkmod.so's soname ensures a >++# run-time loader failure. --as-needed breaks this test because >++# nothing actually references tst-dlopenfailmod2.so (with its soname >++# tst-dlopenfail-missingmod.so). >++LDFLAGS-tst-dlopenfailmod1.so = -Wl,--no-as-needed >+ $(objpfx)tst-dlopenfailmod1.so: \ >+ $(shared-thread-library) $(objpfx)tst-dlopenfaillinkmod.so >+ LDFLAGS-tst-dlopenfaillinkmod.so = -Wl,-soname,tst-dlopenfail-missingmod.so >diff --git a/glibc-rh1410154-12.patch b/glibc-rh1410154-12.patch >new file mode 100644 >index 00000000..8f9dcb82 >--- /dev/null >+++ b/glibc-rh1410154-12.patch >@@ -0,0 +1,1226 @@ >+Author: Florian Weimer <fweimer@redhat.com> >+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 467e810e784bb96d..16a3e8dcda19b4ba 100644 >+--- a/elf/Makefile >++++ b/elf/Makefile >+@@ -185,7 +185,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 \ >+@@ -266,7 +266,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 \ >+@@ -1552,3 +1569,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 c5e5857fb1fe2808..35a3f96a6296294a 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 e13968d4d7c4c83f..c7ed85b7ee99a296 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 >+@@ -718,7 +705,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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <dlfcn.h> >++#include <stddef.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include "tst-dlopen-nodelete-reloc.h" >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include "tst-dlopen-nodelete-reloc.h" >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include "tst-dlopen-nodelete-reloc.h" >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include "tst-dlopen-nodelete-reloc.h" >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include "tst-dlopen-nodelete-reloc.h" >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++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 >++ <https://www.gnu.org/licenses/>. */ >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include "tst-dlopen-nodelete-reloc.h" >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include "tst-dlopen-nodelete-reloc.h" >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <dlfcn.h> >++#include <stddef.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include "tst-dlopen-nodelete-reloc.h" >++ >++#include <stdbool.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++/* 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 <stdio.h> >++#include <string.h> >++#include <stdbool.h> >++#include <support/check.h> >++#include <support/xdlfcn.h> >++ >++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 <support/test-driver.c> >+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 >++ <https://www.gnu.org/licenses/>. */ >++ >++/* 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 <int modid> >++struct unique_symbol >++{ >++ static int value; >++}; >++ >++template <int modid> >++int unique_symbol<modid>::value; >diff --git a/glibc-rh1410154-13.patch b/glibc-rh1410154-13.patch >new file mode 100644 >index 00000000..f45fbea8 >--- /dev/null >+++ b/glibc-rh1410154-13.patch >@@ -0,0 +1,296 @@ >+Author: Florian Weimer <fweimer@redhat.com> >+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 243a028c443173c1..fa7f3e8174576e46 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 >+@@ -820,7 +819,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 35a3f96a6296294a..01724a54f8840f9f 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 c7ed85b7ee99a296..a382bfae8aa3a2f8 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, >+@@ -680,7 +684,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 >+@@ -879,9 +883,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 ea286abaea0128d1..78ba7e76db9706cc 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 a277b77cad6b52b1..e90fa79a0b332087 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 <link_map.h> >+ >diff --git a/glibc-rh1410154-14.patch b/glibc-rh1410154-14.patch >new file mode 100644 >index 00000000..70c36955 >--- /dev/null >+++ b/glibc-rh1410154-14.patch >@@ -0,0 +1,130 @@ >+Author: Florian Weimer <fweimer@redhat.com> >+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 a382bfae8aa3a2f8..d834b89754d2b073 100644 >+--- a/elf/dl-open.c >++++ b/elf/dl-open.c >+@@ -34,7 +34,6 @@ >+ #include <atomic.h> >+ #include <libc-internal.h> >+ #include <array_length.h> >+-#include <internal-signals.h> >+ >+ #include <dl-dst.h> >+ #include <dl-prop.h> >+@@ -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; >+ } >+ >+@@ -709,6 +702,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 >+@@ -742,10 +741,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. */ >+@@ -835,10 +830,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); >+ >+@@ -879,16 +870,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-rh1410154-2.patch b/glibc-rh1410154-2.patch >new file mode 100644 >index 00000000..84269e72 >--- /dev/null >+++ b/glibc-rh1410154-2.patch >@@ -0,0 +1,33 @@ >+commit ca136bb0a36d0a7056c926bfe5126873566efe40 >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Thu Oct 31 13:28:26 2019 +0100 >+ >+ Clarify purpose of assert in _dl_lookup_symbol_x >+ >+ Only one of the currently defined flags is incompatible with versioned >+ symbol lookups, so it makes sense to check for that flag and not its >+ complement. >+ >+ Reviewed-by: Carlos O'Donell <carlos@redhat.com> >+ Reviewed-by: Gabriel F. T. Gomes <gabrielftg@linux.ibm.com> >+ Change-Id: I3384349cef90cfd91862ebc34a4053f0c0a99404 >+ >+diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c >+index 1d046caf017b582b..efbdb8deb3c0a9d4 100644 >+--- a/elf/dl-lookup.c >++++ b/elf/dl-lookup.c >+@@ -792,11 +792,9 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map, >+ >+ bump_num_relocations (); >+ >+- /* No other flag than DL_LOOKUP_ADD_DEPENDENCY or DL_LOOKUP_GSCOPE_LOCK >+- is allowed if we look up a versioned symbol. */ >+- assert (version == NULL >+- || (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK)) >+- == 0); >++ /* DL_LOOKUP_RETURN_NEWEST does not make sense for versioned >++ lookups. */ >++ assert (version == NULL || !(flags & DL_LOOKUP_RETURN_NEWEST)); >+ >+ size_t i = 0; >+ if (__glibc_unlikely (skip_map != NULL)) >diff --git a/glibc-rh1410154-3.patch b/glibc-rh1410154-3.patch >new file mode 100644 >index 00000000..34afb62f >--- /dev/null >+++ b/glibc-rh1410154-3.patch >@@ -0,0 +1,54 @@ >+commit 2a764c6ee848dfe92cb2921ed3b14085f15d9e79 >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Thu Oct 31 13:23:06 2019 +0100 >+ >+ Enhance _dl_catch_exception to allow disabling exception handling >+ >+ In some cases, it is necessary to introduce noexcept regions >+ where raised dynamic loader exceptions (e.g., from lazy binding) >+ are fatal, despite being nested in a code region with an active >+ exception handler. This change enhances _dl_catch_exception with >+ to provide such a capability. The existing function is reused, >+ so that it is not necessary to introduce yet another function with >+ a similar purpose. >+ >+ Change-Id: Iec1bf642ff95a349fdde8040e9baf851ac7b8904 >+ >+diff --git a/elf/dl-error-skeleton.c b/elf/dl-error-skeleton.c >+index d5f418ab1848f0c4..9cb002ccfed2c7b4 100644 >+--- a/elf/dl-error-skeleton.c >++++ b/elf/dl-error-skeleton.c >+@@ -173,6 +173,18 @@ int >+ _dl_catch_exception (struct dl_exception *exception, >+ void (*operate) (void *), void *args) >+ { >++ /* If exception is NULL, temporarily disable exception handling. >++ Exceptions during operate (args) are fatal. */ >++ if (exception == NULL) >++ { >++ struct catch *const old = catch_hook; >++ catch_hook = NULL; >++ operate (args); >++ /* If we get here, the operation was successful. */ >++ catch_hook = old; >++ return 0; >++ } >++ >+ /* We need not handle `receiver' since setting a `catch' is handled >+ before it. */ >+ >+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h >+index 95dc87519b80e0ec..cc2484033fe0d902 100644 >+--- a/sysdeps/generic/ldsodefs.h >++++ b/sysdeps/generic/ldsodefs.h >+@@ -852,7 +852,9 @@ libc_hidden_proto (_dl_catch_error) >+ >+ /* Call OPERATE (ARGS). If no error occurs, set *EXCEPTION to zero. >+ Otherwise, store a copy of the raised exception in *EXCEPTION, >+- which has to be freed by _dl_exception_free. */ >++ which has to be freed by _dl_exception_free. As a special case, if >++ EXCEPTION is null, call OPERATE (ARGS) with exception handling >++ disabled (so that exceptions are fatal). */ >+ int _dl_catch_exception (struct dl_exception *exception, >+ void (*operate) (void *), void *args); >+ libc_hidden_proto (_dl_catch_exception) >diff --git a/glibc-rh1410154-4.patch b/glibc-rh1410154-4.patch >new file mode 100644 >index 00000000..fa3cc530 >--- /dev/null >+++ b/glibc-rh1410154-4.patch >@@ -0,0 +1,42 @@ >+commit fcb04b9aed26a737159ef7be9c5a6ad0994437dc >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Thu Oct 31 13:28:49 2019 +0100 >+ >+ Introduce DL_LOOKUP_FOR_RELOCATE flag for _dl_lookup_symbol_x >+ >+ This will allow changes in dependency processing during non-lazy >+ binding, for more precise processing of NODELETE objects: During >+ initial relocation in dlopen, the fate of NODELETE objects is still >+ unclear, so objects which are depended upon by NODELETE objects >+ cannot immediately be marked as NODELETE. >+ >+ Change-Id: Ic7b94a3f7c4719a00ca8e6018088567824da0658 >+ >+diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c >+index 053916eeae50467c..afeace4d3e49180c 100644 >+--- a/elf/dl-reloc.c >++++ b/elf/dl-reloc.c >+@@ -248,7 +248,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[], >+ v = (version); \ >+ _lr = _dl_lookup_symbol_x (strtab + (*ref)->st_name, l, (ref), \ >+ scope, v, _tc, \ >+- DL_LOOKUP_ADD_DEPENDENCY, NULL); \ >++ DL_LOOKUP_ADD_DEPENDENCY \ >++ | DL_LOOKUP_FOR_RELOCATE, NULL); \ >+ l->l_lookup_cache.ret = (*ref); \ >+ l->l_lookup_cache.value = _lr; })) \ >+ : l) >+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h >+index cc2484033fe0d902..6c5298a80bff8e96 100644 >+--- a/sysdeps/generic/ldsodefs.h >++++ b/sysdeps/generic/ldsodefs.h >+@@ -908,6 +908,9 @@ enum >+ DL_LOOKUP_RETURN_NEWEST = 2, >+ /* Set if dl_lookup* called with GSCOPE lock held. */ >+ DL_LOOKUP_GSCOPE_LOCK = 4, >++ /* Set if dl_lookup is called for non-lazy relocation processing >++ from _dl_relocate_object in elf/dl-reloc.c. */ >++ DL_LOOKUP_FOR_RELOCATE = 8, >+ }; >+ >+ /* Lookup versioned symbol. */ >diff --git a/glibc-rh1410154-5.patch b/glibc-rh1410154-5.patch >new file mode 100644 >index 00000000..6879fb36 >--- /dev/null >+++ b/glibc-rh1410154-5.patch >@@ -0,0 +1,343 @@ >+commit 79e0cd7b3c997e211fad44a81fd839dc5b2546e8 >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Wed Nov 27 16:20:47 2019 +0100 >+ >+ Lazy binding failures during dlopen/dlclose must be fatal [BZ #24304] >+ >+ If a lazy binding failure happens during the execution of an ELF >+ constructor or destructor, the dynamic loader catches the error >+ and reports it using the dlerror mechanism. This is undesirable >+ because there could be other constructors and destructors that >+ need processing (which are skipped), and the process is in an >+ inconsistent state at this point. Therefore, we have to issue >+ a fatal dynamic loader error error and terminate the process. >+ >+ Note that the _dl_catch_exception in _dl_open is just an inner catch, >+ to roll back some state locally. If called from dlopen, there is >+ still an outer catch, which is why calling _dl_init via call_dl_init >+ and a no-exception is required and cannot be avoiding by moving the >+ _dl_init call directly into _dl_open. >+ >+ _dl_fini does not need changes because it does not install an error >+ handler, so errors are already fatal there. >+ >+ Change-Id: I6b1addfe2e30f50a1781595f046f44173db9491a >+ >+Conflicts: >+ elf/Makefile >+ (Usual conflicts due to test backport differences.) >+ >+diff --git a/elf/Makefile b/elf/Makefile >+index 74a240b3a68ff5e2..b752f6366400d221 100644 >+--- a/elf/Makefile >++++ b/elf/Makefile >+@@ -191,7 +191,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ >+ tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \ >+ tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \ >+ tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \ >+- tst-sonamemove-link tst-sonamemove-dlopen >++ tst-sonamemove-link tst-sonamemove-dlopen tst-initfinilazyfail >+ # reldep9 >+ tests-internal += loadtest unload unload2 circleload1 \ >+ neededtest neededtest2 neededtest3 neededtest4 \ >+@@ -281,7 +281,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ >+ tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \ >+ tst-absolute-zero-lib tst-big-note-lib \ >+ tst-sonamemove-linkmod1 \ >+- tst-sonamemove-runmod1 tst-sonamemove-runmod2 >++ tst-sonamemove-runmod1 tst-sonamemove-runmod2 \ >++ tst-initlazyfailmod tst-finilazyfailmod >+ >+ ifeq (yes,$(have-mtls-dialect-gnu2)) >+ tests += tst-gnu2-tls1 >+@@ -1526,3 +1527,13 @@ tst-libc_dlvsym-static-ENV = \ >+ $(objpfx)tst-libc_dlvsym-static.out: $(objpfx)tst-libc_dlvsym-dso.so >+ >+ $(objpfx)tst-big-note: $(objpfx)tst-big-note-lib.so >++ >++$(objpfx)tst-initfinilazyfail: $(libdl) >++$(objpfx)tst-initfinilazyfail.out: \ >++ $(objpfx)tst-initlazyfailmod.so $(objpfx)tst-finilazyfailmod.so >++# Override -z defs, so that we can reference an undefined symbol. >++# Force lazy binding for the same reason. >++LDFLAGS-tst-initlazyfailmod.so = \ >++ -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all >++LDFLAGS-tst-finilazyfailmod.so = \ >++ -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all >+diff --git a/elf/dl-close.c b/elf/dl-close.c >+index ecd6729704ea3294..88aeea25839a34e0 100644 >+--- a/elf/dl-close.c >++++ b/elf/dl-close.c >+@@ -106,6 +106,30 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp, >+ return false; >+ } >+ >++/* Invoke dstructors for CLOSURE (a struct link_map *). Called with >++ exception handling temporarily disabled, to make errors fatal. */ >++static void >++call_destructors (void *closure) >++{ >++ struct link_map *map = closure; >++ >++ if (map->l_info[DT_FINI_ARRAY] != NULL) >++ { >++ ElfW(Addr) *array = >++ (ElfW(Addr) *) (map->l_addr >++ + map->l_info[DT_FINI_ARRAY]->d_un.d_ptr); >++ unsigned int sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val >++ / sizeof (ElfW(Addr))); >++ >++ while (sz-- > 0) >++ ((fini_t) array[sz]) (); >++ } >++ >++ /* Next try the old-style destructor. */ >++ if (map->l_info[DT_FINI] != NULL) >++ DL_CALL_DT_FINI (map, ((void *) map->l_addr >++ + map->l_info[DT_FINI]->d_un.d_ptr)); >++} >+ >+ void >+ _dl_close_worker (struct link_map *map, bool force) >+@@ -267,7 +291,8 @@ _dl_close_worker (struct link_map *map, bool force) >+ && (imap->l_flags_1 & DF_1_NODELETE) == 0); >+ >+ /* Call its termination function. Do not do it for >+- half-cooked objects. */ >++ half-cooked objects. Temporarily disable exception >++ handling, so that errors are fatal. */ >+ if (imap->l_init_called) >+ { >+ /* When debugging print a message first. */ >+@@ -276,22 +301,9 @@ _dl_close_worker (struct link_map *map, bool force) >+ _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", >+ imap->l_name, nsid); >+ >+- if (imap->l_info[DT_FINI_ARRAY] != NULL) >+- { >+- ElfW(Addr) *array = >+- (ElfW(Addr) *) (imap->l_addr >+- + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr); >+- unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val >+- / sizeof (ElfW(Addr))); >+- >+- while (sz-- > 0) >+- ((fini_t) array[sz]) (); >+- } >+- >+- /* Next try the old-style destructor. */ >+- if (imap->l_info[DT_FINI] != NULL) >+- DL_CALL_DT_FINI (imap, ((void *) imap->l_addr >+- + imap->l_info[DT_FINI]->d_un.d_ptr)); >++ if (imap->l_info[DT_FINI_ARRAY] != NULL >++ || imap->l_info[DT_FINI] != NULL) >++ _dl_catch_exception (NULL, call_destructors, imap); >+ } >+ >+ #ifdef SHARED >+diff --git a/elf/dl-open.c b/elf/dl-open.c >+index 518a6cad699ec6d0..c9c0254ee74c4f4b 100644 >+--- a/elf/dl-open.c >++++ b/elf/dl-open.c >+@@ -177,6 +177,23 @@ _dl_find_dso_for_object (const ElfW(Addr) addr) >+ } >+ rtld_hidden_def (_dl_find_dso_for_object); >+ >++/* struct dl_init_args and call_dl_init are used to call _dl_init with >++ exception handling disabled. */ >++struct dl_init_args >++{ >++ struct link_map *new; >++ int argc; >++ char **argv; >++ char **env; >++}; >++ >++static void >++call_dl_init (void *closure) >++{ >++ struct dl_init_args *args = closure; >++ _dl_init (args->new, args->argc, args->argv, args->env); >++} >++ >+ static void >+ dl_open_worker (void *a) >+ { >+@@ -506,8 +523,19 @@ TLS generation counter wrapped! Please report this.")); >+ DL_STATIC_INIT (new); >+ #endif >+ >+- /* Run the initializer functions of new objects. */ >+- _dl_init (new, args->argc, args->argv, args->env); >++ /* Run the initializer functions of new objects. Temporarily >++ disable the exception handler, so that lazy binding failures are >++ fatal. */ >++ { >++ struct dl_init_args init_args = >++ { >++ .new = new, >++ .argc = args->argc, >++ .argv = args->argv, >++ .env = args->env >++ }; >++ _dl_catch_exception (NULL, call_dl_init, &init_args); >++ } >+ >+ /* Now we can make the new map available in the global scope. */ >+ if (mode & RTLD_GLOBAL) >+diff --git a/elf/tst-finilazyfailmod.c b/elf/tst-finilazyfailmod.c >+new file mode 100644 >+index 0000000000000000..2670bd1a9400d0ef >+--- /dev/null >++++ b/elf/tst-finilazyfailmod.c >+@@ -0,0 +1,27 @@ >++/* Helper module for tst-initfinilazyfail: lazy binding failure in destructor. >++ 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++/* An undefined function. Calling it will cause a lazy binding >++ failure. */ >++void undefined_function (void); >++ >++static void __attribute__ ((destructor)) >++fini (void) >++{ >++ undefined_function (); >++} >+diff --git a/elf/tst-initfinilazyfail.c b/elf/tst-initfinilazyfail.c >+new file mode 100644 >+index 0000000000000000..9b4a3d0c0ffbb7c6 >+--- /dev/null >++++ b/elf/tst-initfinilazyfail.c >+@@ -0,0 +1,84 @@ >++/* Test that lazy binding failures in constructors and destructors are fatal. >++ 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <dlfcn.h> >++#include <string.h> >++#include <support/capture_subprocess.h> >++#include <support/check.h> >++#include <support/xdlfcn.h> >++ >++static void >++test_constructor (void *closure) >++{ >++ void *handle = dlopen ("tst-initlazyfailmod.so", RTLD_LAZY); >++ if (handle == NULL) >++ FAIL_EXIT (2, "dlopen did not terminate the process: %s", dlerror ()); >++ else >++ FAIL_EXIT (2, "dlopen did not terminate the process (%p)", handle); >++} >++ >++static void >++test_destructor (void *closure) >++{ >++ void *handle = xdlopen ("tst-finilazyfailmod.so", RTLD_LAZY); >++ int ret = dlclose (handle); >++ const char *message = dlerror (); >++ if (message != NULL) >++ FAIL_EXIT (2, "dlclose did not terminate the process: %d, %s", >++ ret, message); >++ else >++ FAIL_EXIT (2, "dlopen did not terminate the process: %d", ret); >++} >++ >++static int >++do_test (void) >++{ >++ { >++ struct support_capture_subprocess proc >++ = support_capture_subprocess (test_constructor, NULL); >++ support_capture_subprocess_check (&proc, "constructor", 127, >++ sc_allow_stderr); >++ printf ("info: constructor failure output: [[%s]]\n", proc.err.buffer); >++ TEST_VERIFY (strstr (proc.err.buffer, >++ "tst-initfinilazyfail: symbol lookup error: ") >++ != NULL); >++ TEST_VERIFY (strstr (proc.err.buffer, >++ "tst-initlazyfailmod.so: undefined symbol:" >++ " undefined_function\n") != NULL); >++ support_capture_subprocess_free (&proc); >++ } >++ >++ { >++ struct support_capture_subprocess proc >++ = support_capture_subprocess (test_destructor, NULL); >++ support_capture_subprocess_check (&proc, "destructor", 127, >++ sc_allow_stderr); >++ printf ("info: destructor failure output: [[%s]]\n", proc.err.buffer); >++ TEST_VERIFY (strstr (proc.err.buffer, >++ "tst-initfinilazyfail: symbol lookup error: ") >++ != NULL); >++ TEST_VERIFY (strstr (proc.err.buffer, >++ "tst-finilazyfailmod.so: undefined symbol:" >++ " undefined_function\n") != NULL); >++ support_capture_subprocess_free (&proc); >++ } >++ >++ return 0; >++} >++ >++#include <support/test-driver.c> >+diff --git a/elf/tst-initlazyfailmod.c b/elf/tst-initlazyfailmod.c >+new file mode 100644 >+index 0000000000000000..36348b58d634d2bb >+--- /dev/null >++++ b/elf/tst-initlazyfailmod.c >+@@ -0,0 +1,27 @@ >++/* Helper module for tst-initfinilazyfail: lazy binding failure in constructor. >++ 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++/* An undefined function. Calling it will cause a lazy binding >++ failure. */ >++void undefined_function (void); >++ >++static void __attribute__ ((constructor)) >++init (void) >++{ >++ undefined_function (); >++} >diff --git a/glibc-rh1410154-6.patch b/glibc-rh1410154-6.patch >new file mode 100644 >index 00000000..3b05076f >--- /dev/null >+++ b/glibc-rh1410154-6.patch >@@ -0,0 +1,308 @@ >+commit 440b7f8653e4ed8f6e1425145208050b795e9a6c >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Thu Oct 31 18:25:39 2019 +0100 >+ >+ Avoid late failure in dlopen in global scope update [BZ #25112] >+ >+ The call to add_to_global in dl_open_worker happens after running ELF >+ constructors for new objects. At this point, proper recovery from >+ malloc failure would be quite complicated: We would have to run the >+ ELF destructors and close all opened objects, something that we >+ currently do not do. >+ >+ Instead, this change splits add_to_global into two phases, >+ add_to_global_resize (which can raise an exception, called before ELF >+ constructors run), and add_to_global_update (which cannot, called >+ after ELF constructors). A complication arises due to recursive >+ dlopen: After the inner dlopen consumes some space, the pre-allocation >+ in the outer dlopen may no longer be sufficient. A new member in the >+ namespace structure, _ns_global_scope_pending_adds keeps track of the >+ maximum number of objects that need to be added to the global scope. >+ This enables the inner add_to_global_resize call to take into account >+ the needs of an outer dlopen. >+ >+ Most code in the dynamic linker assumes that the number of global >+ scope entries fits into an unsigned int (matching the r_nlist member >+ of struct r_scop_elem). Therefore, change the type of >+ _ns_global_scope_alloc to unsigned int (from size_t), and add overflow >+ checks. >+ >+ Change-Id: Ie08e2f318510d5a6a4bcb1c315f46791b5b77524 >+ >+diff --git a/elf/dl-open.c b/elf/dl-open.c >+index c9c0254ee74c4f4b..85db4f0ecb5f29ce 100644 >+--- a/elf/dl-open.c >++++ b/elf/dl-open.c >+@@ -50,22 +50,38 @@ struct dl_open_args >+ struct link_map *map; >+ /* Namespace ID. */ >+ Lmid_t nsid; >++ >++ /* Original value of _ns_global_scope_pending_adds. Set by >++ dl_open_worker. Only valid if nsid is a real namespace >++ (non-negative). */ >++ unsigned int original_global_scope_pending_adds; >++ >+ /* Original parameters to the program and the current environment. */ >+ int argc; >+ char **argv; >+ char **env; >+ }; >+ >++/* Called in case the global scope cannot be extended. */ >++static void __attribute__ ((noreturn)) >++add_to_global_resize_failure (struct link_map *new) >++{ >++ _dl_signal_error (ENOMEM, new->l_libname->name, NULL, >++ N_ ("cannot extend global scope")); >++} >+ >+-static int >+-add_to_global (struct link_map *new) >++/* Grow the global scope array for the namespace, so that all the new >++ global objects can be added later in add_to_global_update, without >++ risk of memory allocation failure. add_to_global_resize raises >++ exceptions for memory allocation errors. */ >++static void >++add_to_global_resize (struct link_map *new) >+ { >+- struct link_map **new_global; >+- unsigned int to_add = 0; >+- unsigned int cnt; >++ struct link_namespaces *ns = &GL (dl_ns)[new->l_ns]; >+ >+ /* Count the objects we have to put in the global scope. */ >+- for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt) >++ unsigned int to_add = 0; >++ for (unsigned int cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt) >+ if (new->l_searchlist.r_list[cnt]->l_global == 0) >+ ++to_add; >+ >+@@ -83,47 +99,51 @@ add_to_global (struct link_map *new) >+ in an realloc() call. Therefore we allocate a completely new >+ array the first time we have to add something to the locale scope. */ >+ >+- struct link_namespaces *ns = &GL(dl_ns)[new->l_ns]; >++ if (__builtin_add_overflow (ns->_ns_global_scope_pending_adds, to_add, >++ &ns->_ns_global_scope_pending_adds)) >++ add_to_global_resize_failure (new); >++ >++ unsigned int new_size = 0; /* 0 means no new allocation. */ >++ void *old_global = NULL; /* Old allocation if free-able. */ >++ >++ /* Minimum required element count for resizing. Adjusted below for >++ an exponential resizing policy. */ >++ size_t required_new_size; >++ if (__builtin_add_overflow (ns->_ns_main_searchlist->r_nlist, >++ ns->_ns_global_scope_pending_adds, >++ &required_new_size)) >++ add_to_global_resize_failure (new); >++ >+ if (ns->_ns_global_scope_alloc == 0) >+ { >+- /* This is the first dynamic object given global scope. */ >+- ns->_ns_global_scope_alloc >+- = ns->_ns_main_searchlist->r_nlist + to_add + 8; >+- new_global = (struct link_map **) >+- malloc (ns->_ns_global_scope_alloc * sizeof (struct link_map *)); >+- if (new_global == NULL) >+- { >+- ns->_ns_global_scope_alloc = 0; >+- nomem: >+- _dl_signal_error (ENOMEM, new->l_libname->name, NULL, >+- N_("cannot extend global scope")); >+- return 1; >+- } >++ if (__builtin_add_overflow (required_new_size, 8, &new_size)) >++ add_to_global_resize_failure (new); >++ } >++ else if (required_new_size > ns->_ns_global_scope_alloc) >++ { >++ if (__builtin_mul_overflow (required_new_size, 2, &new_size)) >++ add_to_global_resize_failure (new); >+ >+- /* Copy over the old entries. */ >+- ns->_ns_main_searchlist->r_list >+- = memcpy (new_global, ns->_ns_main_searchlist->r_list, >+- (ns->_ns_main_searchlist->r_nlist >+- * sizeof (struct link_map *))); >++ /* The old array was allocated with our malloc, not the minimal >++ malloc. */ >++ old_global = ns->_ns_main_searchlist->r_list; >+ } >+- else if (ns->_ns_main_searchlist->r_nlist + to_add >+- > ns->_ns_global_scope_alloc) >++ >++ if (new_size > 0) >+ { >+- /* We have to extend the existing array of link maps in the >+- main map. */ >+- struct link_map **old_global >+- = GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list; >+- size_t new_nalloc = ((ns->_ns_global_scope_alloc + to_add) * 2); >+- >+- new_global = (struct link_map **) >+- malloc (new_nalloc * sizeof (struct link_map *)); >++ size_t allocation_size; >++ if (__builtin_mul_overflow (new_size, sizeof (struct link_map *), >++ &allocation_size)) >++ add_to_global_resize_failure (new); >++ struct link_map **new_global = malloc (allocation_size); >+ if (new_global == NULL) >+- goto nomem; >++ add_to_global_resize_failure (new); >+ >+- memcpy (new_global, old_global, >+- ns->_ns_global_scope_alloc * sizeof (struct link_map *)); >++ /* Copy over the old entries. */ >++ memcpy (new_global, ns->_ns_main_searchlist->r_list, >++ ns->_ns_main_searchlist->r_nlist * sizeof (struct link_map *)); >+ >+- ns->_ns_global_scope_alloc = new_nalloc; >++ ns->_ns_global_scope_alloc = new_size; >+ ns->_ns_main_searchlist->r_list = new_global; >+ >+ if (!RTLD_SINGLE_THREAD_P) >+@@ -131,16 +151,28 @@ add_to_global (struct link_map *new) >+ >+ free (old_global); >+ } >++} >++ >++/* Actually add the new global objects to the global scope. Must be >++ called after add_to_global_resize. This function cannot fail. */ >++static void >++add_to_global_update (struct link_map *new) >++{ >++ struct link_namespaces *ns = &GL (dl_ns)[new->l_ns]; >+ >+ /* Now add the new entries. */ >+ unsigned int new_nlist = ns->_ns_main_searchlist->r_nlist; >+- for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt) >++ for (unsigned int cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt) >+ { >+ struct link_map *map = new->l_searchlist.r_list[cnt]; >+ >+ if (map->l_global == 0) >+ { >+ map->l_global = 1; >++ >++ /* The array has been resized by add_to_global_resize. */ >++ assert (new_nlist < ns->_ns_global_scope_alloc); >++ >+ ns->_ns_main_searchlist->r_list[new_nlist++] = map; >+ >+ /* We modify the global scope. Report this. */ >+@@ -149,10 +181,15 @@ add_to_global (struct link_map *new) >+ map->l_name, map->l_ns); >+ } >+ } >++ >++ /* Some of the pending adds have been performed by the loop above. >++ Adjust the counter accordingly. */ >++ unsigned int added = new_nlist - ns->_ns_main_searchlist->r_nlist; >++ assert (added <= ns->_ns_global_scope_pending_adds); >++ ns->_ns_global_scope_pending_adds -= added; >++ >+ atomic_write_barrier (); >+ ns->_ns_main_searchlist->r_nlist = new_nlist; >+- >+- return 0; >+ } >+ >+ /* Search link maps in all namespaces for the DSO that contains the object at >+@@ -225,6 +262,10 @@ dl_open_worker (void *a) >+ args->nsid = call_map->l_ns; >+ } >+ >++ /* Retain the old value, so that it can be restored. */ >++ args->original_global_scope_pending_adds >++ = GL (dl_ns)[args->nsid]._ns_global_scope_pending_adds; >++ >+ /* One might be tempted to assert that we are RT_CONSISTENT at this point, but that >+ may not be true if this is a recursive call to dlopen. */ >+ _dl_debug_initialize (0, args->nsid); >+@@ -266,7 +307,10 @@ dl_open_worker (void *a) >+ /* If the user requested the object to be in the global namespace >+ but it is not so far, add it now. */ >+ if ((mode & RTLD_GLOBAL) && new->l_global == 0) >+- (void) add_to_global (new); >++ { >++ add_to_global_resize (new); >++ add_to_global_update (new); >++ } >+ >+ assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); >+ >+@@ -523,6 +567,11 @@ TLS generation counter wrapped! Please report this.")); >+ DL_STATIC_INIT (new); >+ #endif >+ >++ /* Perform the necessary allocations for adding new global objects >++ to the global scope below, via add_to_global_update. */ >++ if (mode & RTLD_GLOBAL) >++ add_to_global_resize (new); >++ >+ /* Run the initializer functions of new objects. Temporarily >+ disable the exception handler, so that lazy binding failures are >+ fatal. */ >+@@ -539,10 +588,7 @@ TLS generation counter wrapped! Please report this.")); >+ >+ /* Now we can make the new map available in the global scope. */ >+ if (mode & RTLD_GLOBAL) >+- /* Move the object in the global namespace. */ >+- if (add_to_global (new) != 0) >+- /* It failed. */ >+- return; >++ add_to_global_update (new); >+ >+ #ifndef SHARED >+ /* We must be the static _dl_open in libc.a. A static program that >+@@ -556,7 +602,6 @@ TLS generation counter wrapped! Please report this.")); >+ new->l_name, new->l_ns, new->l_direct_opencount); >+ } >+ >+- >+ void * >+ _dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid, >+ int argc, char *argv[], char *env[]) >+@@ -624,6 +669,19 @@ no more namespaces available for dlmopen()")); >+ _dl_unload_cache (); >+ #endif >+ >++ /* Do this for both the error and success cases. The old value has >++ only been determined if the namespace ID was assigned (i.e., it >++ is not __LM_ID_CALLER). In the success case, we actually may >++ have consumed more pending adds than planned (because the local >++ scopes overlap in case of a recursive dlopen, the inner dlopen >++ doing some of the globalization work of the outer dlopen), so the >++ old pending adds value is larger than absolutely necessary. >++ Since it is just a conservative upper bound, this is harmless. >++ The top-level dlopen call will restore the field to zero. */ >++ if (args.nsid >= 0) >++ GL (dl_ns)[args.nsid]._ns_global_scope_pending_adds >++ = args.original_global_scope_pending_adds; >++ >+ /* See if an error occurred during loading. */ >+ if (__glibc_unlikely (exception.errstring != NULL)) >+ { >+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h >+index 6c5298a80bff8e96..57fbefea3cb841e9 100644 >+--- a/sysdeps/generic/ldsodefs.h >++++ b/sysdeps/generic/ldsodefs.h >+@@ -311,7 +311,14 @@ struct rtld_global >+ /* This is zero at program start to signal that the global scope map is >+ allocated by rtld. Later it keeps the size of the map. It might be >+ reset if in _dl_close if the last global object is removed. */ >+- size_t _ns_global_scope_alloc; >++ unsigned int _ns_global_scope_alloc; >++ >++ /* During dlopen, this is the number of objects that still need to >++ be added to the global scope map. It has to be taken into >++ account when resizing the map, for future map additions after >++ recursive dlopen calls from ELF constructors. */ >++ unsigned int _ns_global_scope_pending_adds; >++ >+ /* Search table for unique objects. */ >+ struct unique_sym_table >+ { >diff --git a/glibc-rh1410154-7.patch b/glibc-rh1410154-7.patch >new file mode 100644 >index 00000000..977a0112 >--- /dev/null >+++ b/glibc-rh1410154-7.patch >@@ -0,0 +1,490 @@ >+commit a509eb117fac1d764b15eba64993f4bdb63d7f3c >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Wed Nov 27 16:37:17 2019 +0100 >+ >+ Avoid late dlopen failure due to scope, TLS slotinfo updates [BZ #25112] >+ >+ This change splits the scope and TLS slotinfo updates in dlopen into >+ two parts: one to resize the data structures, and one to actually apply >+ the update. The call to add_to_global_resize in dl_open_worker is moved >+ before the demarcation point at which no further memory allocations are >+ allowed. >+ >+ _dl_add_to_slotinfo is adjusted to make the list update optional. There >+ is some optimization possibility here because we could grow the slotinfo >+ list of arrays in a single call, one the largest TLS modid is known. >+ >+ This commit does not fix the fatal meory allocation failure in >+ _dl_update_slotinfo. Ideally, this error during dlopen should be >+ recoverable. >+ >+ The update order of scopes and TLS data structures is retained, although >+ it appears to be more correct to fully initialize TLS first, and then >+ expose symbols in the newly loaded objects via the scope update. >+ >+ Tested on x86_64-linux-gnu. >+ >+ Change-Id: I240c58387dabda3ca1bcab48b02115175fa83d6c >+ >+diff --git a/elf/dl-open.c b/elf/dl-open.c >+index 85db4f0ecb5f29ce..b330cff7d349224a 100644 >+--- a/elf/dl-open.c >++++ b/elf/dl-open.c >+@@ -33,6 +33,7 @@ >+ #include <stap-probe.h> >+ #include <atomic.h> >+ #include <libc-internal.h> >++#include <array_length.h> >+ >+ #include <dl-dst.h> >+ #include <dl-prop.h> >+@@ -214,6 +215,215 @@ _dl_find_dso_for_object (const ElfW(Addr) addr) >+ } >+ rtld_hidden_def (_dl_find_dso_for_object); >+ >++/* Return true if NEW is found in the scope for MAP. */ >++static size_t >++scope_has_map (struct link_map *map, struct link_map *new) >++{ >++ size_t cnt; >++ for (cnt = 0; map->l_scope[cnt] != NULL; ++cnt) >++ if (map->l_scope[cnt] == &new->l_searchlist) >++ return true; >++ return false; >++} >++ >++/* Return the length of the scope for MAP. */ >++static size_t >++scope_size (struct link_map *map) >++{ >++ size_t cnt; >++ for (cnt = 0; map->l_scope[cnt] != NULL; ) >++ ++cnt; >++ return cnt; >++} >++ >++/* Resize the scopes of depended-upon objects, so that the new object >++ can be added later without further allocation of memory. This >++ function can raise an exceptions due to malloc failure. */ >++static void >++resize_scopes (struct link_map *new) >++{ >++ /* If the file is not loaded now as a dependency, add the search >++ list of the newly loaded object to the scope. */ >++ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) >++ { >++ struct link_map *imap = new->l_searchlist.r_list[i]; >++ >++ /* If the initializer has been called already, the object has >++ not been loaded here and now. */ >++ if (imap->l_init_called && imap->l_type == lt_loaded) >++ { >++ if (scope_has_map (imap, new)) >++ /* Avoid duplicates. */ >++ continue; >++ >++ size_t cnt = scope_size (imap); >++ if (__glibc_unlikely (cnt + 1 >= imap->l_scope_max)) >++ { >++ /* The l_scope array is too small. Allocate a new one >++ dynamically. */ >++ size_t new_size; >++ struct r_scope_elem **newp; >++ >++ if (imap->l_scope != imap->l_scope_mem >++ && imap->l_scope_max < array_length (imap->l_scope_mem)) >++ { >++ /* If the current l_scope memory is not pointing to >++ the static memory in the structure, but the >++ static memory in the structure is large enough to >++ use for cnt + 1 scope entries, then switch to >++ using the static memory. */ >++ new_size = array_length (imap->l_scope_mem); >++ newp = imap->l_scope_mem; >++ } >++ else >++ { >++ new_size = imap->l_scope_max * 2; >++ newp = (struct r_scope_elem **) >++ malloc (new_size * sizeof (struct r_scope_elem *)); >++ if (newp == NULL) >++ _dl_signal_error (ENOMEM, "dlopen", NULL, >++ N_("cannot create scope list")); >++ } >++ >++ /* Copy the array and the terminating NULL. */ >++ memcpy (newp, imap->l_scope, >++ (cnt + 1) * sizeof (imap->l_scope[0])); >++ struct r_scope_elem **old = imap->l_scope; >++ >++ imap->l_scope = newp; >++ >++ if (old != imap->l_scope_mem) >++ _dl_scope_free (old); >++ >++ imap->l_scope_max = new_size; >++ } >++ } >++ } >++} >++ >++/* Second stage of resize_scopes: Add NEW to the scopes. Also print >++ debugging information about scopes if requested. >++ >++ This function cannot raise an exception because all required memory >++ has been allocated by a previous call to resize_scopes. */ >++static void >++update_scopes (struct link_map *new) >++{ >++ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) >++ { >++ struct link_map *imap = new->l_searchlist.r_list[i]; >++ int from_scope = 0; >++ >++ if (imap->l_init_called && imap->l_type == lt_loaded) >++ { >++ if (scope_has_map (imap, new)) >++ /* Avoid duplicates. */ >++ continue; >++ >++ size_t cnt = scope_size (imap); >++ /* Assert that resize_scopes has sufficiently enlarged the >++ array. */ >++ assert (cnt + 1 < imap->l_scope_max); >++ >++ /* First terminate the extended list. Otherwise a thread >++ might use the new last element and then use the garbage >++ at offset IDX+1. */ >++ imap->l_scope[cnt + 1] = NULL; >++ atomic_write_barrier (); >++ imap->l_scope[cnt] = &new->l_searchlist; >++ >++ from_scope = cnt; >++ } >++ >++ /* Print scope information. */ >++ if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES)) >++ _dl_show_scope (imap, from_scope); >++ } >++} >++ >++/* Call _dl_add_to_slotinfo with DO_ADD set to false, to allocate >++ space in GL (dl_tls_dtv_slotinfo_list). This can raise an >++ exception. The return value is true if any of the new objects use >++ TLS. */ >++static bool >++resize_tls_slotinfo (struct link_map *new) >++{ >++ bool any_tls = false; >++ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) >++ { >++ struct link_map *imap = new->l_searchlist.r_list[i]; >++ >++ /* Only add TLS memory if this object is loaded now and >++ therefore is not yet initialized. */ >++ if (! imap->l_init_called && imap->l_tls_blocksize > 0) >++ { >++ _dl_add_to_slotinfo (imap, false); >++ any_tls = true; >++ } >++ } >++ return any_tls; >++} >++ >++/* Second stage of TLS update, after resize_tls_slotinfo. This >++ function does not raise any exception. It should only be called if >++ resize_tls_slotinfo returned true. */ >++static void >++update_tls_slotinfo (struct link_map *new) >++{ >++ unsigned int first_static_tls = new->l_searchlist.r_nlist; >++ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) >++ { >++ struct link_map *imap = new->l_searchlist.r_list[i]; >++ >++ /* Only add TLS memory if this object is loaded now and >++ therefore is not yet initialized. */ >++ if (! imap->l_init_called && imap->l_tls_blocksize > 0) >++ { >++ _dl_add_to_slotinfo (imap, true); >++ >++ if (imap->l_need_tls_init >++ && first_static_tls == new->l_searchlist.r_nlist) >++ first_static_tls = i; >++ } >++ } >++ >++ if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) >++ _dl_fatal_printf (N_("\ >++TLS generation counter wrapped! Please report this.")); >++ >++ /* We need a second pass for static tls data, because >++ _dl_update_slotinfo must not be run while calls to >++ _dl_add_to_slotinfo are still pending. */ >++ for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i) >++ { >++ struct link_map *imap = new->l_searchlist.r_list[i]; >++ >++ if (imap->l_need_tls_init >++ && ! imap->l_init_called >++ && imap->l_tls_blocksize > 0) >++ { >++ /* For static TLS we have to allocate the memory here and >++ now, but we can delay updating the DTV. */ >++ imap->l_need_tls_init = 0; >++#ifdef SHARED >++ /* Update the slot information data for at least the >++ generation of the DSO we are allocating data for. */ >++ >++ /* FIXME: This can terminate the process on memory >++ allocation failure. It is not possible to raise >++ exceptions from this context; to fix this bug, >++ _dl_update_slotinfo would have to be split into two >++ operations, similar to resize_scopes and update_scopes >++ above. This is related to bug 16134. */ >++ _dl_update_slotinfo (imap->l_tls_modid); >++#endif >++ >++ GL(dl_init_static_tls) (imap); >++ assert (imap->l_need_tls_init == 0); >++ } >++ } >++} >++ >+ /* struct dl_init_args and call_dl_init are used to call _dl_init with >+ exception handling disabled. */ >+ struct dl_init_args >+@@ -431,133 +641,40 @@ dl_open_worker (void *a) >+ relocation. */ >+ _dl_open_check (new); >+ >+- /* If the file is not loaded now as a dependency, add the search >+- list of the newly loaded object to the scope. */ >+- bool any_tls = false; >+- unsigned int first_static_tls = new->l_searchlist.r_nlist; >+- for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) >+- { >+- struct link_map *imap = new->l_searchlist.r_list[i]; >+- int from_scope = 0; >++ /* This only performs the memory allocations. The actual update of >++ the scopes happens below, after failure is impossible. */ >++ resize_scopes (new); >+ >+- /* If the initializer has been called already, the object has >+- not been loaded here and now. */ >+- if (imap->l_init_called && imap->l_type == lt_loaded) >+- { >+- struct r_scope_elem **runp = imap->l_scope; >+- size_t cnt = 0; >+- >+- while (*runp != NULL) >+- { >+- if (*runp == &new->l_searchlist) >+- break; >+- ++cnt; >+- ++runp; >+- } >+- >+- if (*runp != NULL) >+- /* Avoid duplicates. */ >+- continue; >+- >+- if (__glibc_unlikely (cnt + 1 >= imap->l_scope_max)) >+- { >+- /* The 'r_scope' array is too small. Allocate a new one >+- dynamically. */ >+- size_t new_size; >+- struct r_scope_elem **newp; >+- >+-#define SCOPE_ELEMS(imap) \ >+- (sizeof (imap->l_scope_mem) / sizeof (imap->l_scope_mem[0])) >++ /* Increase the size of the GL (dl_tls_dtv_slotinfo_list) data >++ structure. */ >++ bool any_tls = resize_tls_slotinfo (new); >+ >+- if (imap->l_scope != imap->l_scope_mem >+- && imap->l_scope_max < SCOPE_ELEMS (imap)) >+- { >+- new_size = SCOPE_ELEMS (imap); >+- newp = imap->l_scope_mem; >+- } >+- else >+- { >+- new_size = imap->l_scope_max * 2; >+- newp = (struct r_scope_elem **) >+- malloc (new_size * sizeof (struct r_scope_elem *)); >+- if (newp == NULL) >+- _dl_signal_error (ENOMEM, "dlopen", NULL, >+- N_("cannot create scope list")); >+- } >+- >+- memcpy (newp, imap->l_scope, cnt * sizeof (imap->l_scope[0])); >+- struct r_scope_elem **old = imap->l_scope; >+- >+- imap->l_scope = newp; >+- >+- if (old != imap->l_scope_mem) >+- _dl_scope_free (old); >+- >+- imap->l_scope_max = new_size; >+- } >+- >+- /* First terminate the extended list. Otherwise a thread >+- might use the new last element and then use the garbage >+- at offset IDX+1. */ >+- imap->l_scope[cnt + 1] = NULL; >+- atomic_write_barrier (); >+- imap->l_scope[cnt] = &new->l_searchlist; >+- >+- /* Print only new scope information. */ >+- from_scope = cnt; >+- } >+- /* Only add TLS memory if this object is loaded now and >+- therefore is not yet initialized. */ >+- else if (! imap->l_init_called >+- /* Only if the module defines thread local data. */ >+- && __builtin_expect (imap->l_tls_blocksize > 0, 0)) >+- { >+- /* Now that we know the object is loaded successfully add >+- modules containing TLS data to the slot info table. We >+- might have to increase its size. */ >+- _dl_add_to_slotinfo (imap); >+- >+- if (imap->l_need_tls_init >+- && first_static_tls == new->l_searchlist.r_nlist) >+- first_static_tls = i; >+- >+- /* We have to bump the generation counter. */ >+- any_tls = true; >+- } >+- >+- /* Print scope information. */ >+- if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SCOPES)) >+- _dl_show_scope (imap, from_scope); >+- } >+- >+- /* Bump the generation number if necessary. */ >+- if (any_tls && __builtin_expect (++GL(dl_tls_generation) == 0, 0)) >+- _dl_fatal_printf (N_("\ >+-TLS generation counter wrapped! Please report this.")); >+- >+- /* We need a second pass for static tls data, because _dl_update_slotinfo >+- must not be run while calls to _dl_add_to_slotinfo are still pending. */ >+- for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i) >+- { >+- struct link_map *imap = new->l_searchlist.r_list[i]; >+- >+- if (imap->l_need_tls_init >+- && ! imap->l_init_called >+- && imap->l_tls_blocksize > 0) >+- { >+- /* For static TLS we have to allocate the memory here and >+- now, but we can delay updating the DTV. */ >+- imap->l_need_tls_init = 0; >+-#ifdef SHARED >+- /* Update the slot information data for at least the >+- generation of the DSO we are allocating data for. */ >+- _dl_update_slotinfo (imap->l_tls_modid); >+-#endif >++ /* Perform the necessary allocations for adding new global objects >++ to the global scope below. */ >++ if (mode & RTLD_GLOBAL) >++ add_to_global_resize (new); >+ >+- GL(dl_init_static_tls) (imap); >+- assert (imap->l_need_tls_init == 0); >+- } >+- } >++ /* Demarcation point: After this, no recoverable errors are allowed. >++ All memory allocations for new objects must have happened >++ before. */ >++ >++ /* Second stage after resize_scopes: Actually perform the scope >++ update. After this, dlsym and lazy binding can bind to new >++ objects. */ >++ update_scopes (new); >++ >++ /* FIXME: It is unclear whether the order here is correct. >++ Shouldn't new objects be made available for binding (and thus >++ execution) only after there TLS data has been set up fully? >++ Fixing bug 16134 will likely make this distinction less >++ important. */ >++ >++ /* Second stage after resize_tls_slotinfo: Update the slotinfo data >++ structures. */ >++ if (any_tls) >++ /* FIXME: This calls _dl_update_slotinfo, which aborts the process >++ on memory allocation failure. See bug 16134. */ >++ update_tls_slotinfo (new); >+ >+ /* Notify the debugger all new objects have been relocated. */ >+ if (relocation_in_progress) >+diff --git a/elf/dl-tls.c b/elf/dl-tls.c >+index c87caf13d6a97ba4..a2def280b7096960 100644 >+--- a/elf/dl-tls.c >++++ b/elf/dl-tls.c >+@@ -883,7 +883,7 @@ _dl_tls_get_addr_soft (struct link_map *l) >+ >+ >+ void >+-_dl_add_to_slotinfo (struct link_map *l) >++_dl_add_to_slotinfo (struct link_map *l, bool do_add) >+ { >+ /* Now that we know the object is loaded successfully add >+ modules containing TLS data to the dtv info table. We >+@@ -939,6 +939,9 @@ cannot create TLS data structures")); >+ } >+ >+ /* Add the information into the slotinfo data structure. */ >+- listp->slotinfo[idx].map = l; >+- listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1; >++ if (do_add) >++ { >++ listp->slotinfo[idx].map = l; >++ listp->slotinfo[idx].gen = GL(dl_tls_generation) + 1; >++ } >+ } >+diff --git a/elf/rtld.c b/elf/rtld.c >+index 4ec26a79cbb0aa4f..0aa1a2a19f649e16 100644 >+--- a/elf/rtld.c >++++ b/elf/rtld.c >+@@ -2167,7 +2167,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", >+ >+ /* Add object to slot information data if necessasy. */ >+ if (l->l_tls_blocksize != 0 && tls_init_tp_called) >+- _dl_add_to_slotinfo (l); >++ _dl_add_to_slotinfo (l, true); >+ } >+ } >+ else >+@@ -2215,7 +2215,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", >+ >+ /* Add object to slot information data if necessasy. */ >+ if (l->l_tls_blocksize != 0 && tls_init_tp_called) >+- _dl_add_to_slotinfo (l); >++ _dl_add_to_slotinfo (l, true); >+ } >+ HP_TIMING_NOW (stop); >+ >+diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h >+index 57fbefea3cb841e9..c6b7e61badbfd513 100644 >+--- a/sysdeps/generic/ldsodefs.h >++++ b/sysdeps/generic/ldsodefs.h >+@@ -1135,8 +1135,15 @@ extern void *_dl_open (const char *name, int mode, const void *caller, >+ old scope, OLD can't be freed until no thread is using it. */ >+ extern int _dl_scope_free (void *) attribute_hidden; >+ >+-/* Add module to slot information data. */ >+-extern void _dl_add_to_slotinfo (struct link_map *l) attribute_hidden; >++ >++/* Add module to slot information data. If DO_ADD is false, only the >++ required memory is allocated. Must be called with GL >++ (dl_load_lock) acquired. If the function has already been called >++ for the link map L with !do_add, then this function will not raise >++ an exception, otherwise it is possible that it encounters a memory >++ allocation failure. */ >++extern void _dl_add_to_slotinfo (struct link_map *l, bool do_add) >++ attribute_hidden; >+ >+ /* Update slot information data for at least the generation of the >+ module with the given index. */ >diff --git a/glibc-rh1410154-8.patch b/glibc-rh1410154-8.patch >new file mode 100644 >index 00000000..c22f32af >--- /dev/null >+++ b/glibc-rh1410154-8.patch >@@ -0,0 +1,619 @@ >+commit f63b73814f74032c0e5d0a83300e3d864ef905e5 >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Wed Nov 13 15:44:56 2019 +0100 >+ >+ Remove all loaded objects if dlopen fails, ignoring NODELETE [BZ #20839] >+ >+ This introduces a âpending NODELETEâ state in the link map, which is >+ flipped to the persistent NODELETE state late in dlopen, via >+ activate_nodelete. During initial relocation, symbol binding >+ records pending NODELETE state only. dlclose ignores pending NODELETE >+ state. Taken together, this results that a partially completed dlopen >+ is rolled back completely because new NODELETE mappings are unloaded. >+ >+ Tested on x86_64-linux-gnu and i386-linux-gnu. >+ >+ Change-Id: Ib2a3d86af6f92d75baca65431d74783ee0dbc292 >+ >+Conflicts: >+ elf/Makefile >+ (Usual conflicts due to test backport differences.) >+ >+diff --git a/elf/Makefile b/elf/Makefile >+index b752f6366400d221..bf7c41f38be42184 100644 >+--- a/elf/Makefile >++++ b/elf/Makefile >+@@ -191,7 +191,8 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ >+ tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \ >+ tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \ >+ tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \ >+- tst-sonamemove-link tst-sonamemove-dlopen tst-initfinilazyfail >++ tst-sonamemove-link tst-sonamemove-dlopen tst-initfinilazyfail \ >++ tst-dlopenfail >+ # reldep9 >+ tests-internal += loadtest unload unload2 circleload1 \ >+ neededtest neededtest2 neededtest3 neededtest4 \ >+@@ -282,7 +283,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ >+ tst-absolute-zero-lib tst-big-note-lib \ >+ tst-sonamemove-linkmod1 \ >+ tst-sonamemove-runmod1 tst-sonamemove-runmod2 \ >+- tst-initlazyfailmod tst-finilazyfailmod >++ tst-initlazyfailmod tst-finilazyfailmod \ >++ tst-dlopenfailmod1 tst-dlopenfaillinkmod tst-dlopenfailmod2 >+ >+ ifeq (yes,$(have-mtls-dialect-gnu2)) >+ tests += tst-gnu2-tls1 >+@@ -1537,3 +1539,13 @@ LDFLAGS-tst-initlazyfailmod.so = \ >+ -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all >+ LDFLAGS-tst-finilazyfailmod.so = \ >+ -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all >++ >++$(objpfx)tst-dlopenfail: $(libdl) >++$(objpfx)tst-dlopenfail.out: \ >++ $(objpfx)tst-dlopenfailmod1.so $(objpfx)tst-dlopenfailmod2.so >++# Order matters here. tst-dlopenfaillinkmod.so's soname ensures >++# a run-time loader failure. >++$(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) >+diff --git a/elf/dl-close.c b/elf/dl-close.c >+index 88aeea25839a34e0..243a028c443173c1 100644 >+--- a/elf/dl-close.c >++++ b/elf/dl-close.c >+@@ -168,14 +168,6 @@ _dl_close_worker (struct link_map *map, bool force) >+ char done[nloaded]; >+ struct link_map *maps[nloaded]; >+ >+- /* Clear DF_1_NODELETE to force object deletion. We don't need to touch >+- l_tls_dtor_count because forced object deletion only happens when an >+- error occurs during object load. Destructor registration for TLS >+- non-POD objects should not have happened till then for this >+- object. */ >+- if (force) >+- map->l_flags_1 &= ~DF_1_NODELETE; >+- >+ /* Run over the list and assign indexes to the link maps and enter >+ them into the MAPS array. */ >+ int idx = 0; >+@@ -205,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_flags_1 & DF_1_NODELETE) == 0 >++ && l->l_nodelete != link_map_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 >+@@ -288,7 +280,7 @@ _dl_close_worker (struct link_map *map, bool force) >+ if (!used[i]) >+ { >+ assert (imap->l_type == lt_loaded >+- && (imap->l_flags_1 & DF_1_NODELETE) == 0); >++ && imap->l_nodelete != link_map_nodelete_active); >+ >+ /* Call its termination function. Do not do it for >+ half-cooked objects. Temporarily disable exception >+@@ -828,7 +820,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_flags_1 & DF_1_NODELETE)) >++ if (__glibc_unlikely (map->l_nodelete == link_map_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 efbdb8deb3c0a9d4..c5e5857fb1fe2808 100644 >+--- a/elf/dl-lookup.c >++++ b/elf/dl-lookup.c >+@@ -192,9 +192,10 @@ enter_unique_sym (struct unique_sym *table, size_t size, >+ Return the matching symbol in RESULT. */ >+ static void >+ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, >+- const struct link_map *map, struct sym_val *result, >++ struct link_map *map, struct sym_val *result, >+ int type_class, const ElfW(Sym) *sym, const char *strtab, >+- const ElfW(Sym) *ref, const struct link_map *undef_map) >++ const ElfW(Sym) *ref, const struct link_map *undef_map, >++ int flags) >+ { >+ /* We have to determine whether we already found a symbol with this >+ name before. If not then we have to add it to the search table. >+@@ -222,7 +223,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, >+ copy from the copy addressed through the >+ relocation. */ >+ result->s = sym; >+- result->m = (struct link_map *) map; >++ result->m = map; >+ } >+ else >+ { >+@@ -311,9 +312,19 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, >+ new_hash, strtab + sym->st_name, sym, map); >+ >+ if (map->l_type == lt_loaded) >+- /* Make sure we don't unload this object by >+- setting the appropriate flag. */ >+- ((struct link_map *) map)->l_flags_1 |= DF_1_NODELETE; >++ { >++ /* 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) >++ _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; >++ } >+ } >+ ++tab->n_elements; >+ >+@@ -525,8 +536,9 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash, >+ return 1; >+ >+ case STB_GNU_UNIQUE:; >+- do_lookup_unique (undef_name, new_hash, map, result, type_class, >+- sym, strtab, ref, undef_map); >++ do_lookup_unique (undef_name, new_hash, (struct link_map *) map, >++ result, type_class, sym, strtab, ref, >++ undef_map, flags); >+ return 1; >+ >+ default: >+@@ -568,9 +580,13 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags) >+ if (undef_map == map) >+ return 0; >+ >+- /* Avoid references to objects which cannot be unloaded anyway. */ >++ /* Avoid references to objects which cannot be unloaded anyway. We >++ do not need to record dependencies if this object goes away >++ during dlopen failure, either. IFUNC resolvers with relocation >++ 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_flags_1 & DF_1_NODELETE) != 0) >++ if (map->l_nodelete != link_map_nodelete_inactive) >+ return 0; >+ >+ struct link_map_reldeps *l_reldeps >+@@ -678,16 +694,33 @@ 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_flags_1 & DF_1_NODELETE) != 0) >++ if (map->l_nodelete != link_map_nodelete_inactive) >+ 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_flags_1 & DF_1_NODELETE) != 0) >++ || (undef_map->l_nodelete != link_map_nodelete_inactive)) >+ { >+- map->l_flags_1 |= DF_1_NODELETE; >++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS) >++ && map->l_nodelete == link_map_nodelete_inactive) >++ { >++ if (undef_map->l_name[0] == '\0') >++ _dl_debug_printf ("\ >++marking %s [%lu] as NODELETE due to reference to main program\n", >++ map->l_name, map->l_ns); >++ else >++ _dl_debug_printf ("\ >++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; >+ goto out; >+ } >+ >+@@ -712,7 +745,18 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags) >+ no fatal problem. We simply make sure the referenced object >+ cannot be unloaded. This is semantically the correct >+ behavior. */ >+- map->l_flags_1 |= DF_1_NODELETE; >++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS) >++ && map->l_nodelete == link_map_nodelete_inactive) >++ _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; >+ goto out; >+ } >+ else >+diff --git a/elf/dl-open.c b/elf/dl-open.c >+index b330cff7d349224a..79c6e4c8ed1c9dfa 100644 >+--- a/elf/dl-open.c >++++ b/elf/dl-open.c >+@@ -424,6 +424,40 @@ TLS generation counter wrapped! Please report this.")); >+ } >+ } >+ >++/* Mark the objects as NODELETE if required. This is delayed until >++ 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) >++{ >++ 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); >++ >++ imap->l_nodelete = link_map_nodelete_active; >++ } >++ } >++} >++ >+ /* struct dl_init_args and call_dl_init are used to call _dl_init with >+ exception handling disabled. */ >+ struct dl_init_args >+@@ -493,12 +527,6 @@ dl_open_worker (void *a) >+ return; >+ } >+ >+- /* Mark the object as not deletable if the RTLD_NODELETE flags was passed. >+- Do this early so that we don't skip marking the object if it was >+- already loaded. */ >+- if (__glibc_unlikely (mode & RTLD_NODELETE)) >+- new->l_flags_1 |= DF_1_NODELETE; >+- >+ if (__glibc_unlikely (mode & __RTLD_SPROF)) >+ /* This happens only if we load a DSO for 'sprof'. */ >+ return; >+@@ -514,19 +542,37 @@ dl_open_worker (void *a) >+ _dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n", >+ new->l_name, new->l_ns, new->l_direct_opencount); >+ >+- /* If the user requested the object to be in the global namespace >+- but it is not so far, add it now. */ >++ /* If the user requested the object to be in the global >++ namespace but it is not so far, prepare to add it now. This >++ can raise an exception to do a malloc failure. */ >+ if ((mode & RTLD_GLOBAL) && new->l_global == 0) >++ add_to_global_resize (new); >++ >++ /* Mark the object as not deletable if the RTLD_NODELETE flags >++ was passed. */ >++ if (__glibc_unlikely (mode & RTLD_NODELETE)) >+ { >+- add_to_global_resize (new); >+- add_to_global_update (new); >++ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES) >++ && new->l_nodelete == link_map_nodelete_inactive) >++ _dl_debug_printf ("marking %s [%lu] as NODELETE\n", >++ new->l_name, new->l_ns); >++ new->l_nodelete = link_map_nodelete_active; >+ } >+ >++ /* Finalize the addition to the global scope. */ >++ if ((mode & RTLD_GLOBAL) && new->l_global == 0) >++ add_to_global_update (new); >++ >+ assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); >+ >+ return; >+ } >+ >++ /* Schedule NODELETE marking for the directly loaded object if >++ requested. */ >++ if (__glibc_unlikely (mode & RTLD_NODELETE)) >++ new->l_nodelete = link_map_nodelete_pending; >++ >+ /* Load that object's dependencies. */ >+ _dl_map_object_deps (new, NULL, 0, 0, >+ mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT)); >+@@ -598,6 +644,14 @@ dl_open_worker (void *a) >+ >+ int relocation_in_progress = 0; >+ >++ /* Perform relocation. This can trigger lazy binding in IFUNC >++ resolvers. For NODELETE mappings, these dependencies are not >++ recorded because the flag has not been applied to the newly >++ loaded objects. This means that upon dlopen failure, these >++ NODELETE objects can be unloaded despite existing references to >++ them. However, such relocation dependencies in IFUNC resolvers >++ are undefined anyway, so this is not a problem. */ >++ >+ for (unsigned int i = nmaps; i-- > 0; ) >+ { >+ l = maps[i]; >+@@ -627,7 +681,7 @@ dl_open_worker (void *a) >+ _dl_start_profile (); >+ >+ /* Prevent unloading the object. */ >+- GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE; >++ GL(dl_profile_map)->l_nodelete = link_map_nodelete_active; >+ } >+ } >+ else >+@@ -658,6 +712,8 @@ dl_open_worker (void *a) >+ All memory allocations for new objects must have happened >+ before. */ >+ >++ activate_nodelete (new, mode); >++ >+ /* Second stage after resize_scopes: Actually perform the scope >+ update. After this, dlsym and lazy binding can bind to new >+ objects. */ >+@@ -817,6 +873,10 @@ no more namespaces available for dlmopen()")); >+ GL(dl_tls_dtv_gaps) = true; >+ >+ _dl_close_worker (args.map, true); >++ >++ /* 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. */ >+ } >+ >+ assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT); >+diff --git a/elf/get-dynamic-info.h b/elf/get-dynamic-info.h >+index 4b1ea7c4078ee947..ea286abaea0128d1 100644 >+--- a/elf/get-dynamic-info.h >++++ b/elf/get-dynamic-info.h >+@@ -163,6 +163,8 @@ elf_get_dynamic_info (struct link_map *l, ElfW(Dyn) *temp) >+ if (info[VERSYMIDX (DT_FLAGS_1)] != NULL) >+ { >+ 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; >+ >+ /* 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/elf/tst-dlopenfail.c b/elf/tst-dlopenfail.c >+new file mode 100644 >+index 0000000000000000..ce3140c899562ca8 >+--- /dev/null >++++ b/elf/tst-dlopenfail.c >+@@ -0,0 +1,79 @@ >++/* Test dlopen rollback after failures involving NODELETE objects (bug 20839). >++ 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <dlfcn.h> >++#include <errno.h> >++#include <gnu/lib-names.h> >++#include <stddef.h> >++#include <stdio.h> >++#include <string.h> >++#include <support/check.h> >++#include <support/xdlfcn.h> >++ >++static int >++do_test (void) >++{ >++ /* This test uses libpthread as the canonical NODELETE module. If >++ libpthread is no longer NODELETE because it has been merged into >++ libc, the test needs to be updated. */ >++ TEST_VERIFY (dlsym (NULL, "pthread_create") == NULL); >++ >++ /* This is expected to fail because of the missing dependency. */ >++ puts ("info: attempting to load tst-dlopenfailmod1.so"); >++ TEST_VERIFY (dlopen ("tst-dlopenfailmod1.so", RTLD_LAZY) == NULL); >++ const char *message = dlerror (); >++ TEST_COMPARE_STRING (message, >++ "tst-dlopenfail-missingmod.so:" >++ " cannot open shared object file:" >++ " No such file or directory"); >++ >++ /* Do not probe for the presence of libpthread at this point because >++ that might trigger relocation if bug 20839 is present, obscuring >++ a subsequent crash. */ >++ >++ /* This is expected to succeed. */ >++ puts ("info: loading tst-dlopenfailmod2.so"); >++ void *handle = xdlopen ("tst-dlopenfailmod2.so", RTLD_NOW); >++ xdlclose (handle); >++ >++ /* libpthread should remain loaded. */ >++ TEST_VERIFY (dlopen (LIBPTHREAD_SO, RTLD_LAZY | RTLD_NOLOAD) != NULL); >++ TEST_VERIFY (dlsym (NULL, "pthread_create") == NULL); >++ >++ /* We can make libpthread global, and then the symbol should become >++ available. */ >++ TEST_VERIFY (dlopen (LIBPTHREAD_SO, RTLD_LAZY | RTLD_GLOBAL) != NULL); >++ TEST_VERIFY (dlsym (NULL, "pthread_create") != NULL); >++ >++ /* sem_open is sufficiently complex to depend on relocations. */ >++ void *(*sem_open_ptr) (const char *, int flag, ...) >++ = dlsym (NULL, "sem_open"); >++ if (sem_open_ptr == NULL) >++ /* Hurd does not implement sem_open. */ >++ puts ("warning: sem_open not found, further testing not possible"); >++ else >++ { >++ errno = 0; >++ TEST_VERIFY (sem_open_ptr ("/", 0) == NULL); >++ TEST_COMPARE (errno, EINVAL); >++ } >++ >++ return 0; >++} >++ >++#include <support/test-driver.c> >+diff --git a/elf/tst-dlopenfaillinkmod.c b/elf/tst-dlopenfaillinkmod.c >+new file mode 100644 >+index 0000000000000000..3b14b02bc9a12c0b >+--- /dev/null >++++ b/elf/tst-dlopenfaillinkmod.c >+@@ -0,0 +1,17 @@ >++/* Empty module with a soname which is not available at run time. >++ 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 >++ <https://www.gnu.org/licenses/>. */ >+diff --git a/elf/tst-dlopenfailmod1.c b/elf/tst-dlopenfailmod1.c >+new file mode 100644 >+index 0000000000000000..6ef48829899a5a64 >+--- /dev/null >++++ b/elf/tst-dlopenfailmod1.c >+@@ -0,0 +1,36 @@ >++/* Module which depends on two modules: one NODELETE, one missing. >++ 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++/* Note: Due to the missing second module, this object cannot be >++ loaded at run time. */ >++ >++#include <pthread.h> >++#include <stdio.h> >++#include <unistd.h> >++ >++/* Force linking against libpthread. */ >++void *pthread_create_reference = pthread_create; >++ >++/* The constructor will never be executed because the module cannot be >++ loaded. */ >++static void __attribute__ ((constructor)) >++init (void) >++{ >++ puts ("tst-dlopenfailmod1 constructor executed"); >++ _exit (1); >++} >+diff --git a/elf/tst-dlopenfailmod2.c b/elf/tst-dlopenfailmod2.c >+new file mode 100644 >+index 0000000000000000..7d600386c13b98bd >+--- /dev/null >++++ b/elf/tst-dlopenfailmod2.c >+@@ -0,0 +1,29 @@ >++/* Module which depends on on a NODELETE module, and can be loaded. >++ 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 >++ <https://www.gnu.org/licenses/>. */ >++ >++#include <pthread.h> >++#include <stdio.h> >++ >++/* Force linking against libpthread. */ >++void *pthread_create_reference = pthread_create; >++ >++static void __attribute__ ((constructor)) >++init (void) >++{ >++ puts ("info: tst-dlopenfailmod2.so constructor invoked"); >++} >+diff --git a/include/link.h b/include/link.h >+index 83b1c34b7b4db8f3..a277b77cad6b52b1 100644 >+--- a/include/link.h >++++ b/include/link.h >+@@ -79,6 +79,21 @@ 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. >+@@ -203,6 +218,11 @@ 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; >++ >+ #include <link_map.h> >+ >+ /* Collected information about own RPATH directories. */ >diff --git a/glibc-rh1410154-9.patch b/glibc-rh1410154-9.patch >new file mode 100644 >index 00000000..ab5a3df9 >--- /dev/null >+++ b/glibc-rh1410154-9.patch >@@ -0,0 +1,104 @@ >+commit a2e8aa0d9ea648068d8be52dd7b15f1b6a008e23 >+Author: Florian Weimer <fweimer@redhat.com> >+Date: Thu Oct 31 19:30:19 2019 +0100 >+ >+ Block signals during the initial part of dlopen >+ >+ Lazy binding in a signal handler that interrupts a dlopen sees >+ intermediate dynamic linker state. This has likely been always >+ unsafe, but with the new pending NODELETE state, this is clearly >+ incorrect. Other threads are excluded via the loader lock, but the >+ current thread is not. Blocking signals until right before ELF >+ constructors run is the safe thing to do. >+ >+ Change-Id: Iad079080ebe7442c13313ba11dc2797953faef35 >+ >+diff --git a/elf/dl-open.c b/elf/dl-open.c >+index 79c6e4c8ed1c9dfa..25838b073ac1edaf 100644 >+--- a/elf/dl-open.c >++++ b/elf/dl-open.c >+@@ -34,6 +34,7 @@ >+ #include <atomic.h> >+ #include <libc-internal.h> >+ #include <array_length.h> >++#include <internal-signals.h> >+ >+ #include <dl-dst.h> >+ #include <dl-prop.h> >+@@ -52,6 +53,10 @@ 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). */ >+@@ -524,12 +529,16 @@ 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'. */ >+- return; >++ { >++ /* This happens only if we load a DSO for 'sprof'. */ >++ __libc_signal_restore_set (&args->original_signal_mask); >++ return; >++ } >+ >+ /* This object is directly loaded. */ >+ ++new->l_direct_opencount; >+@@ -565,6 +574,7 @@ 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; >+ } >+ >+@@ -745,6 +755,10 @@ 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. */ >+@@ -834,6 +848,10 @@ 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); >+ >+@@ -874,10 +892,16 @@ 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 link_map_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 f5020e32..9e9e9c37 100644 >--- a/glibc.spec >+++ b/glibc.spec >@@ -1,6 +1,6 @@ > %define glibcsrcdir glibc-2.28 > %define glibcversion 2.28 >-%define glibcrelease 99%{?dist} >+%define glibcrelease 99.bz1410154.2%{?dist} > # Pre-release tarballs are pulled in from git using a command that is > # effectively: > # >@@ -380,6 +380,20 @@ Patch246: glibc-rh1764214.patch > Patch247: glibc-rh1774021.patch > Patch248: glibc-rh1775294.patch > Patch249: glibc-rh1777241.patch >+Patch250: glibc-rh1410154-1.patch >+Patch251: glibc-rh1410154-2.patch >+Patch252: glibc-rh1410154-3.patch >+Patch253: glibc-rh1410154-4.patch >+Patch254: glibc-rh1410154-5.patch >+Patch255: glibc-rh1410154-6.patch >+Patch256: glibc-rh1410154-7.patch >+Patch257: glibc-rh1410154-8.patch >+Patch258: glibc-rh1410154-9.patch >+Patch259: glibc-rh1410154-10.patch >+Patch260: glibc-rh1410154-11.patch >+Patch261: glibc-rh1410154-12.patch >+Patch262: glibc-rh1410154-13.patch >+Patch263: glibc-rh1410154-14.patch > > ############################################################################## > # Continued list of core "glibc" package information: >@@ -2266,6 +2280,9 @@ fi > %files -f compat-libpthread-nonshared.filelist -n compat-libpthread-nonshared > > %changelog >+* Mon Dec 9 2019 Florian Weimer <fweimer@redhat.com> - 2.28-99.bz1410154.2 >+- Roll back dynamic linker state on dlopen failure (#1410154) >+ > * Wed Nov 27 2019 Florian Weimer <fweimer@redhat.com> - 2.28-99 > - s390x: Fix z15 strstr for patterns crossing pages (#1777241) >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 1410154
: 1643297