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 | 6 ++--- test/dlopen/Makefile.in | 4 +++- test/dlopen/nodelete1.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 test/dlopen/nodelete1.c
diff --git a/ldso/ldso/dl-elf.c b/ldso/ldso/dl-elf.c index 04e8c60..473ecc0 100644 --- a/ldso/ldso/dl-elf.c +++ b/ldso/ldso/dl-elf.c @@ -883,6 +883,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 42a09a8..eda3a33 100644 --- a/ldso/libdl/libdl.c +++ b/ldso/libdl/libdl.c @@ -396,7 +396,7 @@ static void *do_dlopen(const char *libname, int flag, ElfW(Addr) from) dyn_chain->next_handle = _dl_handles; _dl_handles = dyn_ptr = dyn_chain;
- if (tpnt->usage_count > 1) { + if (tpnt->init_flag & DL_OPENED) { _dl_if_debug_print("Lib: %s already opened\n", libname); /* see if there is a handle from a earlier dlopen */ for (handle = _dl_handles->next_handle; handle; handle = handle->next_handle) { @@ -820,7 +820,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; @@ -842,7 +842,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"
Hi Leonid, Leonid Lisovskiy wrote,
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
Unfortunately this is breaking following test: Could not open ./libafk.so: File not found FAIL dlafk
Any idea? (dlafk.c, any architecture)
best regards Waldemar
On Fri, May 27, 2016 at 9:30 PM, Waldemar Brodkorb wbx@uclibc-ng.org wrote:
Unfortunately this is breaking following test: Could not open ./libafk.so: File not found FAIL dlafk
My bad. Sorry for incomplete testing, will try to fix it and post new version.
Regards, Leonid