diff mbox

[uclibc-ng-devel,2/2,v3] ldso: fix dlsym hang when reloading DSOs

Message ID 1466443785-4311-3-git-send-email-lly.dev@gmail.com
State RFC
Headers show

Commit Message

Leonid Lisovskiy June 20, 2016, 5:29 p.m. UTC
It can happen under certain cases that the DSO had refcount 0,
but was already loaded. (NODELETE flag is set, or it is pulled
in via both NEEDED dependency and explicit dlopen()).

Add extra reference count for NODELETE objects, this will
ensure that the reference count never drops below one.

It is improved version of
 http://lists.busybox.net/pipermail/uclibc/2013-June/047826.html

Signed-off-by: Leonid Lisovskiy <lly.dev@gmail.com>
---
 ldso/ldso/dl-elf.c      |  2 ++
 ldso/libdl/libdl.c      |  4 ++--
 test/dlopen/Makefile.in |  4 +++-
 test/dlopen/nodelete1.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 66 insertions(+), 3 deletions(-)
 create mode 100644 test/dlopen/nodelete1.c
diff mbox

Patch

diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c
index 8f71aeb..a046aeb 100644
--- a/ldso/ldso/dl-elf.c
+++ b/ldso/ldso/dl-elf.c
@@ -884,6 +884,8 @@  struct elf_resolve *_dl_load_elf_shared_library(unsigned int rflags,
 #endif
 	(*rpnt)->dyn = tpnt;
 	tpnt->usage_count++;
+	if (tpnt->rtld_flags & RTLD_NODELETE)
+		tpnt->usage_count++;
 #ifdef __LDSO_STANDALONE_SUPPORT__
 	tpnt->libtype = (epnt->e_type == ET_DYN) ? elf_lib : elf_executable;
 #else
diff --git a/ldso/libdl/libdl.c b/ldso/libdl/libdl.c
index 489c787..0cf3b70 100644
--- a/ldso/libdl/libdl.c
+++ b/ldso/libdl/libdl.c
@@ -818,7 +818,7 @@  static int do_dlclose(void *vhandle, int need_fini)
 		_dl_handles = rpnt->next_handle;
 	_dl_if_debug_print("%s: usage count: %d\n",
 			handle->dyn->libname, handle->dyn->usage_count);
-	if (handle->dyn->usage_count != 1 || (handle->dyn->rtld_flags & RTLD_NODELETE)) {
+	if (handle->dyn->usage_count != 1) {
 		handle->dyn->usage_count--;
 		free(handle);
 		return 0;
@@ -840,7 +840,7 @@  static int do_dlclose(void *vhandle, int need_fini)
 	for (j = 0; j < handle->init_fini.nlist; ++j) {
 		tpnt = handle->init_fini.init_fini[j];
 		tpnt->usage_count--;
-		if (tpnt->usage_count == 0 && !(tpnt->rtld_flags & RTLD_NODELETE)) {
+		if (tpnt->usage_count == 0) {
 			if ((tpnt->dynamic_info[DT_FINI]
 			     || tpnt->dynamic_info[DT_FINI_ARRAY])
 			 && need_fini
diff --git a/test/dlopen/Makefile.in b/test/dlopen/Makefile.in
index 0bed0f7..740453a 100644
--- a/test/dlopen/Makefile.in
+++ b/test/dlopen/Makefile.in
@@ -5,7 +5,7 @@ 
 export UCLIBC_ONLY := 1
 
 TESTS := dltest dltest2 dlstatic test1 test2 test3 dlundef dlafk dladdr \
-	testscope nodelete tst-origin
+	testscope nodelete nodelete1 tst-origin
 
 ifneq ($(HAVE_SHARED),y)
 TESTS_DISABLED := test3
@@ -80,3 +80,5 @@  LDFLAGS_nodelete := -rdynamic -ldl
 LDFLAGS_nodelmod1.so := -Wl,-z,nodelete
 LDFLAGS_nodelmod3.so := ./nodelmod4.so
 LDFLAGS_nodelmod4.so := -Wl,-z,nodelete
+nodelete1: nodelmod1.so nodelmod2.so
+LDFLAGS_nodelete1 := -rdynamic -ldl
diff --git a/test/dlopen/nodelete1.c b/test/dlopen/nodelete1.c
new file mode 100644
index 0000000..720e37f
--- /dev/null
+++ b/test/dlopen/nodelete1.c
@@ -0,0 +1,59 @@ 
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+int fini_ran;
+
+#define LIBNAME1	"nodelmod1.so"
+
+static int
+do_test (void)
+{
+  /* Verify ability to reload RTLD_NODELETE libraries.
+   */
+  void *p;
+
+  p = dlopen (LIBNAME1, RTLD_NOW);
+  if (p == NULL)
+  {
+      printf ("failed to load "LIBNAME1": %s\n", dlerror ());
+      exit (1);
+  }
+
+  if (dlclose (p) != 0)
+  {
+      puts ("failed to close "LIBNAME1"");
+      exit (1);
+  }
+
+  p = dlopen (LIBNAME1, RTLD_LAZY);
+  if (p == NULL)
+  {
+      printf ("failed to load "LIBNAME1": %s\n", dlerror ());
+      exit (1);
+  }
+
+  if (dlclose (p) != 0)
+  {
+      puts ("failed to close "LIBNAME1"");
+      exit (1);
+  }
+
+  p = dlopen ("nodelmod2.so", RTLD_LAZY);
+  if (p == NULL)
+  {
+      printf ("failed to load \"nodelmod2.so\": %s\n", dlerror ());
+      exit (1);
+  }
+  if (dlclose (p) != 0)
+  {
+      puts ("failed to close \"nodelmod2.so\"");
+      exit (1);
+  }
+
+  return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"