--- /dev/null
+#
+# This is a special patch for rhel-6 to fix recursive dlopen.
+# It is likely the upstream patch will always be too risky for
+# rhel-6 and will involve reorganizing the way in which recursive
+# dlopen is allowed to operate and how the _r_debug and stap
+# points are used by gdb for the recursive case.
+#
+# This fix changes the internal API to duplicate the ldconfig
+# cache data. This means that at any point the cache can be
+# unmapped without any consequences. The caller is responsible
+# fore freeing the returned string.
+#
+# A regression test is added to verify the assertion for _r_debug
+# is no longer triggered due to the recursive dlopen. The test to
+# verify the fix in _dl_load_cache_lookup is not automated and
+# has to be run by hand.
+#
+diff -urN glibc-2.12-2-gc4ccff1/elf/dl-cache.c glibc-2.12-2-gc4ccff1.mod/elf/dl-cache.c
+--- glibc-2.12-2-gc4ccff1/elf/dl-cache.c 2010-05-04 07:27:23.000000000 -0400
++++ glibc-2.12-2-gc4ccff1.mod/elf/dl-cache.c 2014-12-10 21:54:08.801985045 -0500
+@@ -175,9 +175,12 @@
+
+
+ /* Look up NAME in ld.so.cache and return the file name stored there,
+- or null if none is found. */
+-
+-const char *
++ or null if none is found.
++ The caller is responsible for freeing the returned string. The ld.so.cache
++ may be unmapped at any time by a completing recursive dlopen and
++ this function must take care that it does not return references to
++ any data in the mapping. */
++char *
+ internal_function
+ _dl_load_cache_lookup (const char *name)
+ {
+@@ -290,7 +293,17 @@
+ && best != NULL)
+ _dl_debug_printf (" trying file=%s\n", best);
+
+- return best;
++ if (best == NULL)
++ return NULL;
++
++ /* The double copy is *required* since malloc may be interposed
++ and call dlopen itself whose completion would unmap the data
++ we are accessing. Therefore we must make the copy of the
++ mapping data without using malloc. */
++ char *temp;
++ temp = alloca (strlen (best) + 1);
++ strcpy (temp, best);
++ return strdup (temp);
+ }
+
+ #ifndef MAP_COPY
+diff -urN glibc-2.12-2-gc4ccff1/elf/dl-load.c glibc-2.12-2-gc4ccff1.mod/elf/dl-load.c
+--- glibc-2.12-2-gc4ccff1/elf/dl-load.c 2014-12-10 11:03:17.966048404 -0500
++++ glibc-2.12-2-gc4ccff1.mod/elf/dl-load.c 2014-12-10 21:47:29.319387538 -0500
+@@ -2126,7 +2126,7 @@
+ {
+ /* Check the list of libraries in the file /etc/ld.so.cache,
+ for compatibility with Linux's ldconfig program. */
+- const char *cached = _dl_load_cache_lookup (name);
++ char *cached = _dl_load_cache_lookup (name);
+
+ if (cached != NULL)
+ {
+@@ -2156,6 +2156,7 @@
+ if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
+ {
+ /* The prefix matches. Don't use the entry. */
++ free (cached);
+ cached = NULL;
+ break;
+ }
+@@ -2172,14 +2173,9 @@
+ &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+ LA_SER_CONFIG, &found_other_class, false);
+ if (__builtin_expect (fd != -1, 1))
+- {
+- realname = local_strdup (cached);
+- if (realname == NULL)
+- {
+- __close (fd);
+- fd = -1;
+- }
+- }
++ realname = cached;
++ else
++ free (cached);
+ }
+ }
+ }
+diff -urN glibc-2.12-2-gc4ccff1/elf/dl-open.c glibc-2.12-2-gc4ccff1.mod/elf/dl-open.c
+--- glibc-2.12-2-gc4ccff1/elf/dl-open.c 2014-12-10 11:03:18.083048497 -0500
++++ glibc-2.12-2-gc4ccff1.mod/elf/dl-open.c 2014-12-10 20:34:16.017503638 -0500
+@@ -220,7 +220,11 @@
+ }
+ }
+
+- assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
++ /* 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.
++ TODO: Fix all of the debug state so we end up at RT_CONSISTENT only when the last
++ recursive dlopen completes. */
++ _dl_debug_initialize (0, args->nsid);
+
+ /* Load the named object. */
+ struct link_map *new;
+diff -urN glibc-2.12-2-gc4ccff1/sysdeps/generic/ldsodefs.h glibc-2.12-2-gc4ccff1.mod/sysdeps/generic/ldsodefs.h
+--- glibc-2.12-2-gc4ccff1/sysdeps/generic/ldsodefs.h 2014-12-10 11:03:17.944048387 -0500
++++ glibc-2.12-2-gc4ccff1.mod/sysdeps/generic/ldsodefs.h 2014-12-10 21:46:14.071344018 -0500
+@@ -996,8 +996,8 @@
+ internal_function;
+
+ /* Look up NAME in ld.so.cache and return the file name stored there,
+- or null if none is found. */
+-extern const char *_dl_load_cache_lookup (const char *name)
++ or null if none is found. Caller must free returned string. */
++extern char *_dl_load_cache_lookup (const char *name)
+ internal_function;
+
+ /* If the system does not support MAP_COPY we cannot leave the file open
+diff -urN glibc-2.12-2-gc4ccff1/dlfcn/Makefile glibc-2.12-2-gc4ccff1.mod/dlfcn/Makefile
+--- glibc-2.12-2-gc4ccff1/dlfcn/Makefile 2010-05-04 07:27:23.000000000 -0400
++++ glibc-2.12-2-gc4ccff1.mod/dlfcn/Makefile 2014-12-11 16:58:55.719803063 -0500
+@@ -42,12 +42,12 @@
+ ifeq (yes,$(build-shared))
+ tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
+ bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
+- bug-atexit3 tstatexit
++ bug-atexit3 tstatexit tst-rec-dlopen
+ endif
+ modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
+ defaultmod2 errmsg1mod modatexit modcxaatexit \
+ bug-dlsym1-lib1 bug-dlsym1-lib2 bug-atexit1-lib \
+- bug-atexit2-lib bug-atexit3-lib
++ bug-atexit2-lib bug-atexit3-lib moddummy1 moddummy2
+
+ failtestmod.so-no-z-defs = yes
+ glreflib2.so-no-z-defs = yes
+@@ -142,6 +142,8 @@
+ $(objpfx)bug-atexit3-lib.so: $(common-objpfx)libc.so \
+ $(common-objpfx)libc_nonshared.a
+
++LDLIBS-tst-rec-dlopen = -ldl
++$(objpfx)tst-rec-dlopen: $(libdl)
+
+ # Depend on libc.so so a DT_NEEDED is generated in the shared objects.
+ # This ensures they will load libc.so for needed symbols if loaded by
+diff -urN glibc-2.12-2-gc4ccff1/dlfcn/moddummy1.c glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy1.c
+--- glibc-2.12-2-gc4ccff1/dlfcn/moddummy1.c 1969-12-31 19:00:00.000000000 -0500
++++ glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy1.c 2014-12-11 16:57:54.108797285 -0500
+@@ -0,0 +1,13 @@
++/* Provide a dummy DSO for tst-recursive-dlopen to use. */
++#include <stdio.h>
++#include <stdlib.h>
++
++int called_dummy1;
++
++void
++dummy1 (void)
++{
++ printf ("Called dummy1()\n");
++ called_dummy1++;
++}
++
+diff -urN glibc-2.12-2-gc4ccff1/dlfcn/moddummy2.c glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy2.c
+--- glibc-2.12-2-gc4ccff1/dlfcn/moddummy2.c 1969-12-31 19:00:00.000000000 -0500
++++ glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy2.c 2014-12-11 16:57:54.108797285 -0500
+@@ -0,0 +1,13 @@
++/* Provide a dummy DSO for tst-recursive-dlopen to use. */
++#include <stdio.h>
++#include <stdlib.h>
++
++int called_dummy2;
++
++void
++dummy2 (void)
++{
++ printf ("Called dummy2()\n");
++ called_dummy2++;
++}
++
+diff -urN glibc-2.12-2-gc4ccff1/dlfcn/tst-rec-dlopen.c glibc-2.12-2-gc4ccff1.mod/dlfcn/tst-rec-dlopen.c
+--- glibc-2.12-2-gc4ccff1/dlfcn/tst-rec-dlopen.c 1969-12-31 19:00:00.000000000 -0500
++++ glibc-2.12-2-gc4ccff1.mod/dlfcn/tst-rec-dlopen.c 2014-12-11 20:53:28.617848774 -0500
+@@ -0,0 +1,145 @@
++/* Test recursive dlopen using malloc hooks.
++ Copyright (C) 1998-2014 Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
++
++ 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
++ <http://www.gnu.org/licenses/>. */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <malloc.h>
++#include <dlfcn.h>
++
++#define DSO "moddummy1.so"
++#define FUNC "dummy1"
++
++#define DSO1 "moddummy2.so"
++#define FUNC1 "dummy2"
++
++/* Prevent the compiler from moving the assignment to called_func
++ before (*func)() since the compiler doesn't know we might abort
++ or catch a SIGSEGV signal and it may move the store. */
++volatile int called_func;
++
++/* Prototype for my hook. */
++void *custom_malloc_hook (size_t, const void *);
++
++/* Pointer to old malloc hooks. */
++void *(*old_malloc_hook) (size_t, const void *);
++
++/* Call function func_name in DSO dso_name via dlopen. */
++void
++call_func (const char *dso_name, const char *func_name)
++{
++ int ret;
++ void *dso;
++ void (*func) (void);
++ char *err;
++
++ /* Open the DSO. */
++ dso = dlopen (dso_name, RTLD_NOW|RTLD_GLOBAL);
++ if (dso == NULL)
++ {
++ err = dlerror ();
++ fprintf (stderr, "%s\n", err);
++ exit (1);
++ }
++ /* Clear any errors. */
++ dlerror ();
++
++ /* Lookup func. */
++ *(void **) (&func) = dlsym (dso, func_name);
++ if (func == NULL)
++ {
++ err = dlerror ();
++ if (err != NULL)
++ {
++ fprintf (stderr, "%s\n", err);
++ exit (1);
++ }
++ }
++ /* Call func. */
++ (*func) ();
++ called_func = 1;
++
++ /* Close the library and look for errors too. */
++ ret = dlclose (dso);
++ if (ret != 0)
++ {
++ err = dlerror ();
++ fprintf (stderr, "%s\n", err);
++ exit (1);
++ }
++
++}
++
++/* Empty hook that does nothing. */
++void *
++custom_malloc_hook (size_t size, const void *caller)
++{
++ void *result;
++ /* Restore old hooks. */
++ __malloc_hook = old_malloc_hook;
++ /* First call a function in another library via dlopen. */
++ call_func (DSO1, FUNC1);
++ /* Called recursively. */
++ result = malloc (size);
++ /* Restore new hooks. */
++ __malloc_hook = custom_malloc_hook;
++ return result;
++}
++
++static int
++do_test (void)
++{
++ /* Save old hook. */
++ old_malloc_hook = __malloc_hook;
++ /* Install new hook. */
++ __malloc_hook = custom_malloc_hook;
++
++ /* Bug 17702 fixes two things:
++ * A recursive dlopen unmapping the ld.so.cache.
++ * An assertion that _r_debug is RT_CONSISTENT at entry to dlopen.
++ We can only test the latter. Testing the former requires modifying
++ ld.so.conf to cache the dummy libraries, then running ldconfig,
++ then run the test. If you do all of that (and glibc's test
++ infrastructure doesn't support that yet) then the test will
++ SEGFAULT without the fix. If you don't do that, then the test
++ will abort because of the assert described in detail below. */
++ call_func (DSO, FUNC);
++
++ /* Restore old hook. */
++ __malloc_hook = old_malloc_hook;
++
++ /* The function dummy2() is called by the malloc hook. Check to
++ see that it was called. This ensures the second recursive
++ dlopen happened and we called the function in that library.
++
++ Before the fix you either get a SIGSEGV when accessing mmap'd
++ ld.so.cache data or an assertion failure about _r_debug not
++ beint RT_CONSISTENT. We don't test for the SIGSEGV since it
++ would require finding moddummy1 or moddummy2 in the cache and
++ we don't have any infrastructure to test that, but the _r_debug
++ assertion triggers. */
++ if (called_func > 0)
++ printf ("PASS: Function call_func() called more than once.\n");
++ else
++ printf ("FAIL: Function call_func() not called.\n");
++
++ return 0;
++}
++
++#define TEST_FUNCTION do_test ()
++#include "../test-skeleton.c"