From c0f62c56788c48b9fb36dc609c0a9f9db3667306 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Thu, 14 Oct 2004 02:08:23 +0000 Subject: [BZ #77] Update. Add support for namespaces in the dynamic linker. * dlfcn/Makefile (libdl-routines): Add dlmopen. * dlfcn/Versions [libdl, GLIBC_2.3.4]: Add dlmopen. * dlfcn/dlfcn.h: Define Lmid_t, LM_ID_BASE, and LM_ID_NEWLM. Declare dlmopen. Document RTLD_DI_LMID. * dlfcn/dlinfo.c: Handle RTLD_DI_LMID. * dlfcn/dlmopen.c: New file. * dlfcn/dlopen.c: Pass new parameter to _dl_open. * dlfcn/dlopenold.c: Likewise. * elf/dl-addr.c: Adjust for removal of GL(dl_loaded). * elf/dl-caller.c: Likewise. * elf/dl-close.c: Likewise. * elf/dl-conflict.c: Likewise. * elf/dl-debug.c: Likewise. * elf/dl-lookup.c: Likewise. * elf/dl-sym.c: Likewise. * elf/dl-version.c: Likewise. * elf/do-lookup.h: Likewise. * elf/rtld.c: Likewise. * sysdeps/unix/sysv/linux/i386/dl-librecon.h: Likewise. * elf/dl-depsc: Likewise. Add new parameter to _dl_map_object. * elf/dl-fini.c: Call destructors in all namespaces. * elf/dl-iteratephdr.c: Compute total nloaded. Adjust for removal of GL(dl_loaded). * elf/dl-libc.c: Pass new parameter to _dl_open. Adjust for removal of GL(dl_loaded). * elf/dl-load.c (_dl_map_object_from_fd): Don't load ld.so a second time. Reuse the one from the main namespace in all others. Pass new parameter to _dl_new_object. Adjust for removal of GL(dl_loaded). * elf/dl-object.c: Take new parameter. Use it to initialize l_ns. Adjust for removal of GL(dl_loaded). * elf/dl-open.c (_dl_open): Take new parameter. Adjust for removal of GL(dl_loaded). * elf/dl-support.c: Replace global _dl_loaded etc variables with _dl_ns variable. * include/dlfcn.h: Adjust prototype of _dl_open. Define __LM_ID_CALLER. * include/link.h: Add l_real, l_ns, and l_direct_opencount elements. * sysdeps/generic/dl-tls.c: Bump TLS_STATIC_SURPLUS. Since libc is using TLS we need memory appropriate to the number of namespaces. * sysdeps/generic/ldsodefs.h (struct rtld_global): Replace _dl_loaded, _dl_nloaded, _dl_global_scope, _dl_main_searchlist, and _dl_global_scope_alloc with _dl_ns element. Define DL_NNS. Adjust prototypes of _dl_map_object and member in rtld_global_ro. * malloc/malloc.c: Include . * malloc/arena.c (ptmalloc_init): If libc is not in primary namespace, never use brk. * elf/Makefile: Add rules to build and run tst-dlmopen1 and tst-dlmopen2. * elf/tst-dlmopen1.c: New file. * elf/tst-dlmopen1mod.c: New file. * elf/tst-dlmopen2.c: New file. * elf/dl-close.c: Improve reference counting by tracking direct loads. * elf/dl-lookup.c (add_dependency): Likewise. * elf/dl-open.c (dl_open_worker): Likewise. * elf/rtld.c (dl_main): Likewise. 2004-09-09 GOTO Masanori [BZ #77] * elf/dl-close.c: Count down l_opencount to check not only for l_reldeps, but also l_initfini. 2004-10-13 Ulrich Drepper --- elf/Makefile | 12 ++- elf/dl-addr.c | 41 ++++---- elf/dl-caller.c | 62 ++++++------ elf/dl-close.c | 145 ++++++++++++++++++--------- elf/dl-conflict.c | 4 +- elf/dl-debug.c | 4 +- elf/dl-deps.c | 5 +- elf/dl-fini.c | 265 +++++++++++++++++++++++++++++--------------------- elf/dl-iteratephdr.c | 24 ++++- elf/dl-libc.c | 29 +++--- elf/dl-load.c | 90 ++++++++++++----- elf/dl-lookup.c | 19 ++-- elf/dl-object.c | 14 +-- elf/dl-open.c | 129 +++++++++++++++--------- elf/dl-support.c | 17 +--- elf/dl-sym.c | 22 +++-- elf/dl-version.c | 5 +- elf/do-lookup.h | 2 +- elf/rtld.c | 245 ++++++++++++++++++++++++---------------------- elf/tst-dlmopen1.c | 80 +++++++++++++++ elf/tst-dlmopen1mod.c | 38 ++++++++ elf/tst-dlmopen2.c | 70 +++++++++++++ 22 files changed, 862 insertions(+), 460 deletions(-) create mode 100644 elf/tst-dlmopen1.c create mode 100644 elf/tst-dlmopen1mod.c create mode 100644 elf/tst-dlmopen2.c (limited to 'elf') diff --git a/elf/Makefile b/elf/Makefile index d40d3fd..0bec4ab 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -154,7 +154,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \ circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \ tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-align \ $(tests-execstack-$(have-z-execstack)) tst-dlmodcount \ - tst-dlopenrpath tst-deep1 + tst-dlopenrpath tst-deep1 tst-dlmopen1 tst-dlmopen2 # reldep9 test-srcs = tst-pathopt tests-vis-yes = vismain @@ -187,7 +187,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ reldep8mod1 reldep8mod2 reldep8mod3 \ reldep9mod1 reldep9mod2 reldep9mod3 \ tst-alignmod $(modules-execstack-$(have-z-execstack)) \ - tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 + tst-dlopenrpathmod tst-deep1mod1 tst-deep1mod2 tst-deep1mod3 \ + tst-dlmopen1mod ifeq (yes,$(have-initfini-array)) modules-names += tst-array2dep endif @@ -762,3 +763,10 @@ $(objpfx)tst-deep1: $(libdl) $(objpfx)tst-deep1mod1.so $(objpfx)tst-deep1.out: $(objpfx)tst-deep1mod2.so LDFLAGS-tst-deep1 += -rdynamic tst-deep1mod3.so-no-z-defs = yes + +$(objpfx)tst-dlmopen1mod.so: $(libdl) +$(objpfx)tst-dlmopen1: $(libdl) +$(objpfx)tst-dlmopen1.out: $(objpfx)tst-dlmopen1mod.so + +$(objpfx)tst-dlmopen2: $(libdl) +$(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so diff --git a/elf/dl-addr.c b/elf/dl-addr.c index c373eb3..c418784 100644 --- a/elf/dl-addr.c +++ b/elf/dl-addr.c @@ -38,26 +38,27 @@ _dl_addr (const void *address, Dl_info *info, /* Find the highest-addressed object that ADDRESS is not below. */ match = NULL; - for (struct link_map *l = GL(dl_loaded); l; l = l->l_next) - if (addr >= l->l_map_start && addr < l->l_map_end) - { - /* We know ADDRESS lies within L if in any shared object. - Make sure it isn't past the end of L's segments. */ - size_t n = l->l_phnum; - if (n > 0) - { - do - --n; - while (l->l_phdr[n].p_type != PT_LOAD); - if (addr >= (l->l_addr + - l->l_phdr[n].p_vaddr + l->l_phdr[n].p_memsz)) - /* Off the end of the highest-addressed shared object. */ - continue; - } - - match = l; - break; - } + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) + for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l; l = l->l_next) + if (addr >= l->l_map_start && addr < l->l_map_end) + { + /* We know ADDRESS lies within L if in any shared object. + Make sure it isn't past the end of L's segments. */ + size_t n = l->l_phnum; + if (n > 0) + { + do + --n; + while (l->l_phdr[n].p_type != PT_LOAD); + if (addr >= (l->l_addr + + l->l_phdr[n].p_vaddr + l->l_phdr[n].p_memsz)) + /* Off the end of the highest-addressed shared object. */ + continue; + } + + match = l; + break; + } int result = 0; if (match != NULL) diff --git a/elf/dl-caller.c b/elf/dl-caller.c index ddabbd0..b0c1264 100644 --- a/elf/dl-caller.c +++ b/elf/dl-caller.c @@ -35,44 +35,46 @@ _dl_check_caller (const void *caller, enum allowmask mask) #endif static const char expected4[] = LD_SO; - for (struct link_map *l = GL(dl_loaded); l != NULL; l = l->l_next) - if (caller >= (const void *) l->l_map_start - && caller < (const void *) l->l_text_end) - { - /* The address falls into this DSO's address range. Check the - name. */ - if ((mask & allow_libc) && strcmp (expected1, l->l_name) == 0) - return 0; - if ((mask & allow_libdl) && strcmp (expected2, l->l_name) == 0) - return 0; + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) + for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL; + l = l->l_next) + if (caller >= (const void *) l->l_map_start + && caller < (const void *) l->l_text_end) + { + /* The address falls into this DSO's address range. Check the + name. */ + if ((mask & allow_libc) && strcmp (expected1, l->l_name) == 0) + return 0; + if ((mask & allow_libdl) && strcmp (expected2, l->l_name) == 0) + return 0; #ifdef LIBPTHREAD_SO - if ((mask & allow_libpthread) && strcmp (expected3, l->l_name) == 0) - return 0; + if ((mask & allow_libpthread) && strcmp (expected3, l->l_name) == 0) + return 0; #endif - if ((mask & allow_ldso) && strcmp (expected4, l->l_name) == 0) - return 0; + if ((mask & allow_ldso) && strcmp (expected4, l->l_name) == 0) + return 0; - struct libname_list *runp = l->l_libname; + struct libname_list *runp = l->l_libname; - while (runp != NULL) - { - if ((mask & allow_libc) && strcmp (expected1, runp->name) == 0) - return 0; - if ((mask & allow_libdl) && strcmp (expected2, runp->name) == 0) - return 0; + while (runp != NULL) + { + if ((mask & allow_libc) && strcmp (expected1, runp->name) == 0) + return 0; + if ((mask & allow_libdl) && strcmp (expected2, runp->name) == 0) + return 0; #ifdef LIBPTHREAD_SO - if ((mask & allow_libpthread) - && strcmp (expected3, runp->name) == 0) - return 0; + if ((mask & allow_libpthread) + && strcmp (expected3, runp->name) == 0) + return 0; #endif - if ((mask & allow_ldso) && strcmp (expected4, runp->name) == 0) - return 0; + if ((mask & allow_ldso) && strcmp (expected4, runp->name) == 0) + return 0; - runp = runp->next; - } + runp = runp->next; + } - break; - } + break; + } /* Maybe the dynamic linker is not yet on the list. */ if ((mask & allow_ldso) != 0 diff --git a/elf/dl-close.c b/elf/dl-close.c index 7c26839..4f015fd 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -130,6 +130,10 @@ _dl_close (void *_map) /* Acquire the lock. */ __rtld_lock_lock_recursive (GL(dl_load_lock)); + /* One less direct use. */ + assert (map->l_direct_opencount > 0); + --map->l_direct_opencount; + /* Decrement the reference count. */ if (map->l_opencount > 1 || map->l_type != lt_loaded) { @@ -141,6 +145,12 @@ _dl_close (void *_map) /* Decrement the object's reference counter, not the dependencies'. */ --map->l_opencount; + /* If the direct use counter reaches zero we have to decrement + all the dependencies' usage counter. */ + if (map->l_direct_opencount == 0) + for (i = 1; i < map->l_searchlist.r_nlist; ++i) + --map->l_searchlist.r_list[i]->l_opencount; + __rtld_lock_unlock_recursive (GL(dl_load_lock)); return; } @@ -167,12 +177,13 @@ _dl_close (void *_map) for (i = 1; list[i] != NULL; ++i) if ((list[i]->l_flags_1 & DF_1_NODELETE) == 0 /* Decrement counter. */ - && --new_opencount[i] == 0) + && (assert (new_opencount[i] > 0), --new_opencount[i] == 0)) { void mark_removed (struct link_map *remmap) { /* Test whether this object was also loaded directly. */ - if (remmap->l_searchlist.r_list != NULL) + if (remmap->l_searchlist.r_list != NULL + && remmap->l_direct_opencount > 0) { /* In this case we have to decrement all the dependencies of this object. They are all in MAP's dependency list. */ @@ -184,6 +195,7 @@ _dl_close (void *_map) || ! dep_list[j]->l_init_called) { assert (dep_list[j]->l_idx < map->l_searchlist.r_nlist); + assert (new_opencount[dep_list[j]->l_idx] > 0); if (--new_opencount[dep_list[j]->l_idx] == 0) { assert (dep_list[j]->l_type == lt_loaded); @@ -197,17 +209,53 @@ _dl_close (void *_map) unsigned int j; for (j = 0; j < remmap->l_reldepsact; ++j) { + struct link_map *depmap = remmap->l_reldeps[j]; + /* Find out whether this object is in our list. */ - if (remmap->l_reldeps[j]->l_idx < nopencount - && (list[remmap->l_reldeps[j]->l_idx] - == remmap->l_reldeps[j])) - /* Yes, it is. */ - if (--new_opencount[remmap->l_reldeps[j]->l_idx] == 0) - { - /* This one is now gone, too. */ - assert (remmap->l_reldeps[j]->l_type == lt_loaded); - mark_removed (remmap->l_reldeps[j]); - } + if (depmap->l_idx < nopencount + && list[depmap->l_idx] == depmap) + { + /* Yes, it is. If is has a search list, make a + recursive call to handle this. */ + if (depmap->l_searchlist.r_list != NULL) + { + assert (new_opencount[depmap->l_idx] > 0); + if (--new_opencount[depmap->l_idx] == 0) + { + /* This one is now gone, too. */ + assert (depmap->l_type == lt_loaded); + mark_removed (depmap); + } + } + else + { + /* Otherwise we have to handle the dependency + deallocation here. */ + unsigned int k; + for (k = 0; depmap->l_initfini[k] != NULL; ++k) + { + struct link_map *rl = depmap->l_initfini[k]; + + if (rl->l_idx < nopencount + & list[rl->l_idx] == rl) + { + assert (new_opencount[rl->l_idx] > 0); + if (--new_opencount[rl->l_idx] == 0) + { + /* Another module to remove. */ + assert (rl->l_type == lt_loaded); + mark_removed (rl); + } + } + else + { + assert (rl->l_opencount > 0); + if (--rl->l_opencount == 0) + mark_removed (rl); + } + } + } + } } } } @@ -225,7 +273,8 @@ _dl_close (void *_map) { /* When debugging print a message first. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) - GLRO(dl_debug_printf) ("\ncalling fini: %s\n\n", imap->l_name); + GLRO(dl_debug_printf) ("\ncalling fini: %s [%lu]\n\n", + imap->l_name, imap->l_ns); /* Call its termination function. Do not do it for half-cooked objects. */ @@ -340,18 +389,21 @@ _dl_close (void *_map) if (__builtin_expect (imap->l_global, 0)) { /* This object is in the global scope list. Remove it. */ - unsigned int cnt = GL(dl_main_searchlist)->r_nlist; + unsigned int cnt + = GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_nlist; do --cnt; - while (GL(dl_main_searchlist)->r_list[cnt] != imap); + while (GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_list[cnt] + != imap); /* The object was already correctly registered. */ - while (++cnt < GL(dl_main_searchlist)->r_nlist) - GL(dl_main_searchlist)->r_list[cnt - 1] - = GL(dl_main_searchlist)->r_list[cnt]; + while (++cnt + < GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_nlist) + GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_list[cnt - 1] + = GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_list[cnt]; - --GL(dl_main_searchlist)->r_nlist; + --GL(dl_ns)[imap->l_ns]._ns_main_searchlist->r_nlist; } #ifdef USE_TLS @@ -442,19 +494,18 @@ _dl_close (void *_map) DL_UNMAP (imap); /* Finally, unlink the data structure and free it. */ -#ifdef SHARED - /* We will unlink the first object only if this is a statically - linked program. */ - assert (imap->l_prev != NULL); - imap->l_prev->l_next = imap->l_next; -#else if (imap->l_prev != NULL) imap->l_prev->l_next = imap->l_next; else - GL(dl_loaded) = imap->l_next; + { +#ifdef SHARED + assert (imap->l_ns != LM_ID_BASE); #endif - --GL(dl_nloaded); - if (imap->l_next) + GL(dl_ns)[imap->l_ns]._ns_loaded = imap->l_next; + } + + --GL(dl_ns)[imap->l_ns]._ns_nloaded; + if (imap->l_next != NULL) imap->l_next->l_prev = imap->l_prev; free (imap->l_versions); @@ -528,7 +579,7 @@ _dl_close (void *_map) if (any_tls) { if (__builtin_expect (++GL(dl_tls_generation) == 0, 0)) - __libc_fatal (_("TLS generation counter wrapped! Please send report as described in .")); + __libc_fatal (_("TLS generation counter wrapped! Please report as described in .")); if (tls_free_end == GL(dl_tls_static_used)) GL(dl_tls_static_used) = tls_free_start; @@ -596,22 +647,26 @@ free_slotinfo (struct dtv_slotinfo_list **elemp) libc_freeres_fn (free_mem) { - if (__builtin_expect (GL(dl_global_scope_alloc), 0) != 0 - && (GL(dl_main_searchlist)->r_nlist - == GLRO(dl_initial_searchlist).r_nlist)) - { - /* All object dynamically loaded by the program are unloaded. Free - the memory allocated for the global scope variable. */ - struct link_map **old = GL(dl_main_searchlist)->r_list; - - /* Put the old map in. */ - GL(dl_main_searchlist)->r_list = GLRO(dl_initial_searchlist).r_list; - /* Signal that the original map is used. */ - GL(dl_global_scope_alloc) = 0; - - /* Now free the old map. */ - free (old); - } + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) + if (__builtin_expect (GL(dl_ns)[ns]._ns_global_scope_alloc, 0) != 0 + && (GL(dl_ns)[ns]._ns_main_searchlist->r_nlist + // XXX Check whether we need NS-specific initial_searchlist + == GLRO(dl_initial_searchlist).r_nlist)) + { + /* All object dynamically loaded by the program are unloaded. Free + the memory allocated for the global scope variable. */ + struct link_map **old = GL(dl_ns)[ns]._ns_main_searchlist->r_list; + + /* Put the old map in. */ + GL(dl_ns)[ns]._ns_main_searchlist->r_list + // XXX Check whether we need NS-specific initial_searchlist + = GLRO(dl_initial_searchlist).r_list; + /* Signal that the original map is used. */ + GL(dl_ns)[ns]._ns_global_scope_alloc = 0; + + /* Now free the old map. */ + free (old); + } #ifdef USE_TLS if (USE___THREAD || GL(dl_tls_dtv_slotinfo_list) != NULL) diff --git a/elf/dl-conflict.c b/elf/dl-conflict.c index 8546d36..4ced40f 100644 --- a/elf/dl-conflict.c +++ b/elf/dl-conflict.c @@ -54,8 +54,10 @@ _dl_resolve_conflicts (struct link_map *l, ElfW(Rela) *conflict, (map) = resolve_conflict_map; \ } while (0) + /* Prelinking makes no sense for anything but the main namespace. */ + assert (l->l_ns == LM_ID_BASE); struct link_map *resolve_conflict_map __attribute__ ((__unused__)) - = GL(dl_loaded); + = GL(dl_ns)[LM_ID_BASE]._ns_loaded; #include "dynamic-link.h" diff --git a/elf/dl-debug.c b/elf/dl-debug.c index 2feb0c5..2e7b9e8 100644 --- a/elf/dl-debug.c +++ b/elf/dl-debug.c @@ -39,7 +39,9 @@ _dl_debug_initialize (ElfW(Addr) ldbase) /* Tell the debugger where to find the map of loaded objects. */ _r_debug.r_version = 1 /* R_DEBUG_VERSION XXX */; _r_debug.r_ldbase = ldbase; - _r_debug.r_map = GL(dl_loaded); + // XXX This is problematic. It means we cannot tell the debugger + // XXX about namespaces other than the main one. + _r_debug.r_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; _r_debug.r_brk = (ElfW(Addr)) &_dl_debug_state; } diff --git a/elf/dl-deps.c b/elf/dl-deps.c index 1a0fedf..a1c16d7 100644 --- a/elf/dl-deps.c +++ b/elf/dl-deps.c @@ -63,7 +63,8 @@ openaux (void *a) args->aux = _dl_map_object (args->map, args->name, 0, (args->map->l_type == lt_executable ? lt_library : args->map->l_type), - args->trace_mode, args->open_mode); + args->trace_mode, args->open_mode, + args->map->l_ns); } static ptrdiff_t @@ -510,7 +511,7 @@ _dl_map_object_deps (struct link_map *map, } if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK, 0) != 0 - && map == GL(dl_loaded)) + && map == GL(dl_ns)[LM_ID_BASE]._ns_loaded) { /* If we are to compute conflicts, we have to build local scope for each library, not just the ultimate loader. */ diff --git a/elf/dl-fini.c b/elf/dl-fini.c index dd405de..33036b5 100644 --- a/elf/dl-fini.c +++ b/elf/dl-fini.c @@ -32,10 +32,10 @@ internal_function _dl_fini (void) { /* Lots of fun ahead. We have to call the destructors for all still - loaded objects. The problem is that the ELF specification now - demands that dependencies between the modules are taken into account. - I.e., the destructor for a module is called before the ones for any - of its dependencies. + loaded objects, in all namespaces. The problem is that the ELF + specification now demands that dependencies between the modules + are taken into account. I.e., the destructor for a module is + called before the ones for any of its dependencies. To make things more complicated, we cannot simply use the reverse order of the constructors. Since the user might have loaded objects @@ -45,140 +45,179 @@ _dl_fini (void) unsigned int i; unsigned int nloaded; struct link_map *l; - struct link_map **maps; - - /* Protect against concurrent loads and unloads. */ - __rtld_lock_lock_recursive (GL(dl_load_lock)); - - nloaded = GL(dl_nloaded); - - /* XXX Could it be (in static binaries) that there is no object loaded? */ - assert (nloaded > 0); - - /* Now we can allocate an array to hold all the pointers and copy - the pointers in. */ - maps = (struct link_map **) alloca (nloaded * sizeof (struct link_map *)); - for (l = GL(dl_loaded), i = 0; l != NULL; l = l->l_next) + struct link_map **maps = NULL; + size_t maps_size = 0; + + /* We First run the destructors of the main namespaces, then the + other ones. The order should not matter since the namespace + content is supposed to be independent. But we can have auditing + code in a auxiliaty namespace and we want it to monitor the + destructors. */ + for (Lmid_t cnt = 0; cnt < DL_NNS; ++cnt) { - assert (i < nloaded); + /* Protect against concurrent loads and unloads. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); - maps[i++] = l; + nloaded = GL(dl_ns)[cnt]._ns_nloaded; - /* Bump l_opencount of all objects so that they are not dlclose()ed - from underneath us. */ - ++l->l_opencount; - } - assert (i == nloaded); - - /* Now we have to do the sorting. */ - for (l = GL(dl_loaded)->l_next; l != NULL; l = l->l_next) - { - unsigned int j; - unsigned int k; + /* XXX Could it be (in static binaries) that there is no object + loaded? */ + assert (cnt != LM_ID_BASE || nloaded > 0); - /* Find the place in the 'maps' array. */ - for (j = 1; maps[j] != l; ++j) - ; + /* Now we can allocate an array to hold all the pointers and copy + the pointers in. */ + if (maps_size < nloaded * sizeof (struct link_map *)) + { + if (maps_size == 0) + { + maps_size = nloaded * sizeof (struct link_map *); + maps = (struct link_map **) alloca (maps_size); + } + else + maps = (struct link_map **) + extend_alloca (maps, maps_size, + nloaded * sizeof (struct link_map *)); + } - /* Find all object for which the current one is a dependency and - move the found object (if necessary) in front. */ - for (k = j + 1; k < nloaded; ++k) + for (l = GL(dl_ns)[cnt]._ns_loaded, i = 0; l != NULL; l = l->l_next) { - struct link_map **runp = maps[k]->l_initfini; - if (runp != NULL) + assert (i < nloaded); + + /* Do not handle ld.so in secondary namespaces. */ + if (l == l->l_real) { - while (*runp != NULL) - if (*runp == l) - { - struct link_map *here = maps[k]; - - /* Move it now. */ - memmove (&maps[j] + 1, - &maps[j], - (k - j) * sizeof (struct link_map *)); - maps[j++] = here; - - break; - } - else - ++runp; + maps[i++] = l; + + /* Bump l_opencount of all objects so that they are not + dlclose()ed from underneath us. */ + ++l->l_opencount; } + } + assert (cnt != LM_ID_BASE || i == nloaded); + assert (cnt == LM_ID_BASE || i == nloaded || i == nloaded - 1); + unsigned int nmaps = i; - if (__builtin_expect (maps[k]->l_reldeps != NULL, 0)) + if (nmaps != 0) + { + /* Now we have to do the sorting. */ + l = GL(dl_ns)[cnt]._ns_loaded; + if (cnt == LM_ID_BASE) + /* The main executable always comes first. */ + l = l->l_next; + for (; l != NULL; l = l->l_next) { - unsigned int m = maps[k]->l_reldepsact; - struct link_map **relmaps = maps[k]->l_reldeps; + unsigned int j; + unsigned int k; - while (m-- > 0) + /* Find the place in the 'maps' array. */ + for (j = 1; maps[j] != l; ++j) + ; + + /* Find all object for which the current one is a dependency and + move the found object (if necessary) in front. */ + for (k = j + 1; k < nmaps; ++k) { - if (relmaps[m] == l) + struct link_map **runp = maps[k]->l_initfini; + if (runp != NULL) { - struct link_map *here = maps[k]; - - /* Move it now. */ - memmove (&maps[j] + 1, - &maps[j], - (k - j) * sizeof (struct link_map *)); - maps[j] = here; + while (*runp != NULL) + if (*runp == l) + { + struct link_map *here = maps[k]; + + /* Move it now. */ + memmove (&maps[j] + 1, + &maps[j], + (k - j) * sizeof (struct link_map *)); + maps[j++] = here; + + break; + } + else + ++runp; + } - break; + if (__builtin_expect (maps[k]->l_reldeps != NULL, 0)) + { + unsigned int m = maps[k]->l_reldepsact; + struct link_map **relmaps = maps[k]->l_reldeps; + + while (m-- > 0) + { + if (relmaps[m] == l) + { + struct link_map *here = maps[k]; + + /* Move it now. */ + memmove (&maps[j] + 1, + &maps[j], + (k - j) * sizeof (struct link_map *)); + maps[j] = here; + + break; + } + } } } } } - } - - /* We do not rely on the linked list of loaded object anymore from - this point on. We have our own list here (maps). The various - members of this list cannot vanish since the open count is too - high and will be decremented in this loop. So we release the - lock so that some code which might be called from a destructor - can directly or indirectly access the lock. */ - __rtld_lock_unlock_recursive (GL(dl_load_lock)); - - /* 'maps' now contains the objects in the right order. Now call the - destructors. We have to process this array from the front. */ - for (i = 0; i < nloaded; ++i) - { - l = maps[i]; - if (l->l_init_called) + /* We do not rely on the linked list of loaded object anymore from + this point on. We have our own list here (maps). The various + members of this list cannot vanish since the open count is too + high and will be decremented in this loop. So we release the + lock so that some code which might be called from a destructor + can directly or indirectly access the lock. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + /* 'maps' now contains the objects in the right order. Now call the + destructors. We have to process this array from the front. */ + for (i = 0; i < nmaps; ++i) { - /* Make sure nothing happens if we are called twice. */ - l->l_init_called = 0; + l = maps[i]; - /* Don't call the destructors for objects we are not supposed to. */ - if (l->l_name[0] == '\0' && l->l_type == lt_executable) - continue; - - /* Is there a destructor function? */ - if (l->l_info[DT_FINI_ARRAY] == NULL && l->l_info[DT_FINI] == NULL) - continue; - - /* When debugging print a message first. */ - if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) - _dl_debug_printf ("\ncalling fini: %s\n\n", - l->l_name[0] ? l->l_name : rtld_progname); - - /* First see whether an array is given. */ - if (l->l_info[DT_FINI_ARRAY] != NULL) + if (l->l_init_called) { - ElfW(Addr) *array = - (ElfW(Addr) *) (l->l_addr - + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); - unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val - / sizeof (ElfW(Addr))); - while (i-- > 0) - ((fini_t) array[i]) (); + /* Make sure nothing happens if we are called twice. */ + l->l_init_called = 0; + + /* Don't call the destructors for objects we are not + supposed to. */ + if (l->l_name[0] == '\0' && l->l_type == lt_executable) + continue; + + /* Is there a destructor function? */ + if (l->l_info[DT_FINI_ARRAY] == NULL + && l->l_info[DT_FINI] == NULL) + continue; + + /* When debugging print a message first. */ + if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, + 0)) + _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", + l->l_name[0] ? l->l_name : rtld_progname, + cnt); + + /* First see whether an array is given. */ + if (l->l_info[DT_FINI_ARRAY] != NULL) + { + ElfW(Addr) *array = + (ElfW(Addr) *) (l->l_addr + + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr); + unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val + / sizeof (ElfW(Addr))); + while (i-- > 0) + ((fini_t) array[i]) (); + } + + /* Next try the old-style destructor. */ + if (l->l_info[DT_FINI] != NULL) + ((fini_t) DL_DT_FINI_ADDRESS (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr)) (); } - /* Next try the old-style destructor. */ - if (l->l_info[DT_FINI] != NULL) - ((fini_t) DL_DT_FINI_ADDRESS (l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr)) (); + /* Correct the previous increment. */ + --l->l_opencount; } - - /* Correct the previous increment. */ - --l->l_opencount; } if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS, 0)) diff --git a/elf/dl-iteratephdr.c b/elf/dl-iteratephdr.c index d615cd6..6ed90c7 100644 --- a/elf/dl-iteratephdr.c +++ b/elf/dl-iteratephdr.c @@ -42,14 +42,32 @@ __dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, __rtld_lock_lock_recursive (GL(dl_load_lock)); __libc_cleanup_push (cancel_handler, 0); - for (l = GL(dl_loaded); l != NULL; l = l->l_next) + /* We have to determine the namespace of the caller since this determines + which namespace is reported. */ + const void *caller = RETURN_ADDRESS (0); + size_t nloaded = GL(dl_ns)[0]._ns_nloaded; + Lmid_t ns = 0; + for (Lmid_t cnt = DL_NNS - 1; cnt > 0; --cnt) + for (struct link_map *l = GL(dl_ns)[cnt]._ns_loaded; l; l = l->l_next) + { + /* We have to count the total number of loaded objects. */ + nloaded += GL(dl_ns)[cnt]._ns_nloaded; + + if (caller >= (const void *) l->l_map_start + && caller < (const void *) l->l_map_end) + /* There must be exactly one DSO for the range of the virtual + memory. Otherwise something is really broken. */ + ns = cnt; + } + + for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) { info.dlpi_addr = l->l_addr; info.dlpi_name = l->l_name; info.dlpi_phdr = l->l_phdr; info.dlpi_phnum = l->l_phnum; info.dlpi_adds = GL(dl_load_adds); - info.dlpi_subs = GL(dl_load_adds) - GL(dl_nloaded); + info.dlpi_subs = GL(dl_load_adds) - nloaded; ret = callback (&info, sizeof (struct dl_phdr_info), data); if (ret) break; @@ -87,7 +105,7 @@ dl_iterate_phdr (int (*callback) (struct dl_phdr_info *info, info.dlpi_phdr = _dl_phdr; info.dlpi_phnum = _dl_phnum; info.dlpi_adds = GL(dl_load_adds); - info.dlpi_subs = GL(dl_load_adds) - GL(dl_nloaded); + info.dlpi_subs = GL(dl_load_adds) - GL(dl_ns)[LM_ID_BASE]._ns_nloaded; ret = (*callback) (&info, sizeof (struct dl_phdr_info), data); if (ret) return ret; diff --git a/elf/dl-libc.c b/elf/dl-libc.c index a8b851c..2602feb 100644 --- a/elf/dl-libc.c +++ b/elf/dl-libc.c @@ -77,7 +77,7 @@ do_dlopen (void *ptr) { struct do_dlopen_args *args = (struct do_dlopen_args *) ptr; /* Open and relocate the shared object. */ - args->map = _dl_open (args->name, args->mode, NULL); + args->map = _dl_open (args->name, args->mode, NULL, __LM_ID_CALLER); } static void @@ -217,18 +217,19 @@ libc_freeres_fn (free_mem) } /* Remove all additional names added to the objects. */ - for (l = GL(dl_loaded); l != NULL; l = l->l_next) - { - struct libname_list *lnp = l->l_libname->next; - - l->l_libname->next = NULL; - - while (lnp != NULL) - { - struct libname_list *old = lnp; - lnp = lnp->next; - if (! old->dont_free) + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) + for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) + { + struct libname_list *lnp = l->l_libname->next; + + l->l_libname->next = NULL; + + while (lnp != NULL) + { + struct libname_list *old = lnp; + lnp = lnp->next; + if (! old->dont_free) free (old); - } - } + } + } } diff --git a/elf/dl-load.c b/elf/dl-load.c index 15fff3c..83d46f0 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -699,7 +699,7 @@ _dl_init_paths (const char *llp) #ifdef SHARED /* This points to the map of the main object. */ - l = GL(dl_loaded); + l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; if (l != NULL) { assert (l->l_type != lt_loaded); @@ -795,10 +795,10 @@ lose (int code, int fd, const char *name, char *realname, struct link_map *l, if (l->l_prev == NULL) /* No other module loaded. This happens only in the static library, or in rtld under --verify. */ - GL(dl_loaded) = NULL; + GL(dl_ns)[l->l_ns]._ns_loaded = NULL; else l->l_prev->l_next = NULL; - --GL(dl_nloaded); + --GL(dl_ns)[l->l_ns]._ns_nloaded; free (l); } free (realname); @@ -815,7 +815,7 @@ static struct link_map * _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, char *realname, struct link_map *loader, int l_type, - int mode, void **stack_endp) + int mode, void **stack_endp, Lmid_t nsid) { struct link_map *l = NULL; const ElfW(Ehdr) *header; @@ -839,7 +839,7 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, } /* Look again to see if the real name matched another already loaded. */ - for (l = GL(dl_loaded); l; l = l->l_next) + for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next) if (l->l_ino == st.st_ino && l->l_dev == st.st_dev) { /* The object is already loaded. @@ -854,6 +854,31 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, return l; } +#ifdef SHARED + /* When loading into a namespace other than the base one we must + avoid loading ld.so since there can only be one copy. Ever. */ + if (__builtin_expect (nsid != LM_ID_BASE, 0) + && ((st.st_ino == GL(dl_rtld_map).l_ino + && st.st_dev == GL(dl_rtld_map).l_dev) + || _dl_name_match_p (name, &GL(dl_rtld_map)))) + { + /* This is indeed ld.so. Create a new link_map which refers to + the real one for almost everything. */ + l = _dl_new_object (realname, name, l_type, loader, mode, nsid); + if (l == NULL) + goto fail_new; + + /* Refer to the real descriptor. */ + l->l_real = &GL(dl_rtld_map); + + /* No need to bump the refcount of the real object, ld.so will + never be unloaded. */ + __close (fd); + + return l; + } +#endif + if (mode & RTLD_NOLOAD) /* We are not supposed to load the object unless it is already loaded. So return now. */ @@ -861,7 +886,7 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, /* Print debugging message. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) - _dl_debug_printf ("file=%s; generating link map\n", name); + _dl_debug_printf ("file=%s [%lu]; generating link map\n", name, nsid); /* This is the ELF header. We read it in `open_verify'. */ header = (void *) fbp->buf; @@ -881,9 +906,10 @@ _dl_map_object_from_fd (const char *name, int fd, struct filebuf *fbp, #endif /* Enter the new object in the list of loaded objects. */ - l = _dl_new_object (realname, name, l_type, loader, mode); - if (__builtin_expect (! l, 0)) + l = _dl_new_object (realname, name, l_type, loader, mode, nsid); + if (__builtin_expect (l == NULL, 0)) { + fail_new: errstring = N_("cannot create shared object descriptor"); goto call_lose_errno; } @@ -1771,7 +1797,7 @@ open_path (const char *name, size_t namelen, int preloaded, struct link_map * internal_function _dl_map_object (struct link_map *loader, const char *name, int preloaded, - int type, int trace_mode, int mode) + int type, int trace_mode, int mode, Lmid_t nsid) { int fd; char *realname; @@ -1779,8 +1805,11 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, struct link_map *l; struct filebuf fb; + assert (nsid >= 0); + assert (nsid < DL_NNS); + /* Look for this name among those already loaded. */ - for (l = GL(dl_loaded); l; l = l->l_next) + for (l = GL(dl_ns)[nsid]._ns_loaded; l; l = l->l_next) { /* If the requested name matches the soname of a loaded object, use that object. Elide this check for names that have not @@ -1812,9 +1841,9 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, /* Display information if we are debugging. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0) && loader != NULL) - _dl_debug_printf ("\nfile=%s; needed by %s\n", name, + _dl_debug_printf ("\nfile=%s [%lu]; needed by %s [%lu]\n", name, nsid, loader->l_name[0] - ? loader->l_name : rtld_progname); + ? loader->l_name : rtld_progname, loader->l_ns); if (strchr (name, '/') == NULL) { @@ -1823,7 +1852,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, size_t namelen = strlen (name) + 1; if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0)) - _dl_debug_printf ("find library=%s; searching\n", name); + _dl_debug_printf ("find library=%s [%lu]; searching\n", name, nsid); fd = -1; @@ -1839,12 +1868,15 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, &realname, &fb); /* If dynamically linked, try the DT_RPATH of the executable - itself. */ - l = GL(dl_loaded); - if (fd == -1 && l && l->l_type != lt_loaded && l != loader - && l->l_rpath_dirs.dirs != (void *) -1) - fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs, - &realname, &fb); + itself. NB: we do this for lookups in any namespace. */ + if (fd == -1) + { + l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + if (l && l->l_type != lt_loaded && l != loader + && l->l_rpath_dirs.dirs != (void *) -1) + fd = open_path (name, namelen, preloaded, &l->l_rpath_dirs, + &realname, &fb); + } } /* Try the LD_LIBRARY_PATH environment variable. */ @@ -1870,7 +1902,8 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, if (cached != NULL) { #ifdef SHARED - l = loader ?: GL(dl_loaded); + // XXX Correct to unconditionally default to namespace 0? + l = loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded; #else l = loader; #endif @@ -1920,7 +1953,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, /* Finally, try the default path. */ if (fd == -1 - && ((l = loader ?: GL(dl_loaded)) == NULL + && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL || __builtin_expect (!(l->l_flags_1 & DF_1_NODEFLIB), 1)) && rtld_search_dirs.dirs != (void *) -1) fd = open_path (name, namelen, preloaded, &rtld_search_dirs, @@ -1966,7 +1999,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, /* Enter the new object in the list of loaded objects. */ if ((name_copy = local_strdup (name)) == NULL || (l = _dl_new_object (name_copy, name, type, loader, - mode)) == NULL) + mode, nsid)) == NULL) _dl_signal_error (ENOMEM, name, NULL, N_("cannot create shared object descriptor")); /* Signal that this is a faked entry. */ @@ -1987,7 +2020,7 @@ _dl_map_object (struct link_map *loader, const char *name, int preloaded, void *stack_end = __libc_stack_end; return _dl_map_object_from_fd (name, fd, &fb, realname, loader, type, mode, - &stack_end); + &stack_end, nsid); } @@ -2047,10 +2080,13 @@ _dl_rtld_di_serinfo (struct link_map *loader, Dl_serinfo *si, bool counting) while (l != NULL); /* If dynamically linked, try the DT_RPATH of the executable itself. */ - l = GL(dl_loaded); - if (l != NULL && l->l_type != lt_loaded && l != loader) - if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) - add_path (&l->l_rpath_dirs, XXX_RPATH); + if (loader->l_ns == LM_ID_BASE) + { + l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + if (l != NULL && l->l_type != lt_loaded && l != loader) + if (cache_rpath (l, &l->l_rpath_dirs, DT_RPATH, "RPATH")) + add_path (&l->l_rpath_dirs, XXX_RPATH); + } } /* Try the LD_LIBRARY_PATH environment variable. */ diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index d2a6976..fdb0769 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -137,7 +137,7 @@ add_dependency (struct link_map *undef_map, struct link_map *map) reference is still available. There is a brief period in which the object could have been removed since we found the definition. */ - runp = GL(dl_loaded); + runp = GL(dl_ns)[undef_map->l_ns]._ns_loaded; while (runp != NULL && runp != map) runp = runp->l_next; @@ -182,13 +182,18 @@ add_dependency (struct link_map *undef_map, struct link_map *map) for (list = map->l_initfini; *list != NULL; ++list) ++(*list)->l_opencount; + /* As if it is opened through _dl_open. */ + ++map->l_direct_opencount; + /* Display information if we are debugging. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) _dl_debug_printf ("\ -\nfile=%s; needed by %s (relocation dependency)\n\n", +\nfile=%s [%lu]; needed by %s [%lu] (relocation dependency)\n\n", map->l_name[0] ? map->l_name : rtld_progname, + map->l_ns, undef_map->l_name[0] - ? undef_map->l_name : rtld_progname); + ? undef_map->l_name : rtld_progname, + undef_map->l_ns); } else /* Whoa, that was bad luck. We have to search again. */ @@ -408,8 +413,8 @@ _dl_debug_bindings (const char *undef_name, struct link_map *undef_map, struct sym_val val = { NULL, NULL }; if ((GLRO(dl_trace_prelink_map) == NULL - || GLRO(dl_trace_prelink_map) == GL(dl_loaded)) - && undef_map != GL(dl_loaded)) + || GLRO(dl_trace_prelink_map) == GL(dl_ns)[LM_ID_BASE]._ns_loaded) + && undef_map != GL(dl_ns)[LM_ID_BASE]._ns_loaded) { const unsigned long int hash = _dl_elf_hash (undef_name); @@ -421,12 +426,12 @@ _dl_debug_bindings (const char *undef_name, struct link_map *undef_map, conflict = 1; } -#ifdef USE_TLS +# ifdef USE_TLS if (value->s && (__builtin_expect (ELFW(ST_TYPE) (value->s->st_info) == STT_TLS, 0))) type_class = 4; -#endif +# endif if (conflict || GLRO(dl_trace_prelink_map) == undef_map diff --git a/elf/dl-object.c b/elf/dl-object.c index 91b1fa2..b46ebdc 100644 --- a/elf/dl-object.c +++ b/elf/dl-object.c @@ -32,7 +32,7 @@ struct link_map * internal_function _dl_new_object (char *realname, const char *libname, int type, - struct link_map *loader, int mode) + struct link_map *loader, int mode, Lmid_t nsid) { struct link_map *l; int idx; @@ -45,6 +45,7 @@ _dl_new_object (char *realname, const char *libname, int type, if (new == NULL) return NULL; + new->l_real = new; new->l_libname = newname = (struct libname_list *) (new + 1); newname->name = (char *) memcpy (newname + 1, libname, libname_len); /* newname->next = NULL; We use calloc therefore not necessary. */ @@ -56,6 +57,7 @@ _dl_new_object (char *realname, const char *libname, int type, #if defined USE_TLS && NO_TLS_OFFSET != 0 new->l_tls_offset = NO_TLS_OFFSET; #endif + new->l_ns = nsid; /* new->l_global = 0; We use calloc therefore not necessary. */ @@ -68,9 +70,9 @@ _dl_new_object (char *realname, const char *libname, int type, /* Counter for the scopes we have to handle. */ idx = 0; - if (GL(dl_loaded) != NULL) + if (GL(dl_ns)[nsid]._ns_loaded != NULL) { - l = GL(dl_loaded); + l = GL(dl_ns)[nsid]._ns_loaded; while (l->l_next != NULL) l = l->l_next; new->l_prev = l; @@ -78,11 +80,11 @@ _dl_new_object (char *realname, const char *libname, int type, l->l_next = new; /* Add the global scope. */ - new->l_scope[idx++] = &GL(dl_loaded)->l_searchlist; + new->l_scope[idx++] = &GL(dl_ns)[nsid]._ns_loaded->l_searchlist; } else - GL(dl_loaded) = new; - ++GL(dl_nloaded); + GL(dl_ns)[nsid]._ns_loaded = new; + ++GL(dl_ns)[nsid]._ns_nloaded; ++GL(dl_load_adds); /* If we have no loader the new object acts as it. */ diff --git a/elf/dl-open.c b/elf/dl-open.c index c9b4a45..34e2aff 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -72,6 +72,8 @@ struct dl_open_args /* This is the caller if _dl_open(). */ const void *caller_dl_open; struct link_map *map; + /* Namespace ID. */ + Lmid_t nsid; }; @@ -101,15 +103,17 @@ 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. */ - if (GL(dl_global_scope_alloc) == 0) + if (GL(dl_ns)[new->l_ns]._ns_global_scope_alloc == 0) { /* This is the first dynamic object given global scope. */ - GL(dl_global_scope_alloc) = GL(dl_main_searchlist)->r_nlist + to_add + 8; + GL(dl_ns)[new->l_ns]._ns_global_scope_alloc + = GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist + to_add + 8; new_global = (struct link_map **) - malloc (GL(dl_global_scope_alloc) * sizeof (struct link_map *)); + malloc (GL(dl_ns)[new->l_ns]._ns_global_scope_alloc + * sizeof (struct link_map *)); if (new_global == NULL) { - GL(dl_global_scope_alloc) = 0; + GL(dl_ns)[new->l_ns]._ns_global_scope_alloc = 0; nomem: GLRO(dl_signal_error) (ENOMEM, new->l_libname->name, NULL, N_("cannot extend global scope")); @@ -117,25 +121,26 @@ add_to_global (struct link_map *new) } /* Copy over the old entries. */ - memcpy (new_global, GL(dl_main_searchlist)->r_list, - (GL(dl_main_searchlist)->r_nlist * sizeof (struct link_map *))); - - GL(dl_main_searchlist)->r_list = new_global; + GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list + = memcpy (new_global, + GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list, + (GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist + * sizeof (struct link_map *))); } - else if (GL(dl_main_searchlist)->r_nlist + to_add - > GL(dl_global_scope_alloc)) + else if (GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist + to_add + > GL(dl_ns)[new->l_ns]._ns_global_scope_alloc) { /* We have to extend the existing array of link maps in the main map. */ new_global = (struct link_map **) - realloc (GL(dl_main_searchlist)->r_list, - ((GL(dl_global_scope_alloc) + to_add + 8) + realloc (GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list, + ((GL(dl_ns)[new->l_ns]._ns_global_scope_alloc + to_add + 8) * sizeof (struct link_map *))); if (new_global == NULL) goto nomem; - GL(dl_global_scope_alloc) += to_add + 8; - GL(dl_main_searchlist)->r_list = new_global; + GL(dl_ns)[new->l_ns]._ns_global_scope_alloc += to_add + 8; + GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list = new_global; } /* Now add the new entries. */ @@ -146,9 +151,9 @@ add_to_global (struct link_map *new) if (map->l_global == 0) { map->l_global = 1; - GL(dl_main_searchlist)->r_list[GL(dl_main_searchlist)->r_nlist] + GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list[GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist] = map; - ++GL(dl_main_searchlist)->r_nlist; + ++GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_nlist; } } @@ -175,28 +180,34 @@ dl_open_worker (void *a) GLRO(dl_signal_error) (0, "dlopen", NULL, N_("invalid caller")); /* Determine the caller's map if necessary. This is needed in case - we have a DST or when the file name has no path in which case we - need to look along the RUNPATH/RPATH of the caller. */ + we have a DST, when we don't know the namespace ID we have to put + the new object in, or when the file name has no path in which + case we need to look along the RUNPATH/RPATH of the caller. */ const char *dst = strchr (file, '$'); - if (dst != NULL || strchr (file, '/') == NULL) + if (dst != NULL || args->nsid == __LM_ID_CALLER + || strchr (file, '/') == NULL) { const void *caller_dlopen = args->caller_dlopen; - /* We have to find out from which object the caller is calling. */ - call_map = NULL; - for (l = GL(dl_loaded); l; l = l->l_next) - if (caller_dlopen >= (const void *) l->l_map_start - && caller_dlopen < (const void *) l->l_map_end) - { - /* There must be exactly one DSO for the range of the virtual - memory. Otherwise something is really broken. */ - call_map = l; - break; - } + /* We have to find out from which object the caller is calling. + By default we assume this is the main application. */ + call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) + for (l = GL(dl_ns)[ns]._ns_loaded; l != NULL; l = l->l_next) + if (caller_dlopen >= (const void *) l->l_map_start + && caller_dlopen < (const void *) l->l_map_end) + { + /* There must be exactly one DSO for the range of the virtual + memory. Otherwise something is really broken. */ + assert (ns == l->l_ns); + call_map = l; + goto found_caller; + } - if (call_map == NULL) - /* In this case we assume this is the main application. */ - call_map = GL(dl_loaded); + found_caller: + if (args->nsid == __LM_ID_CALLER) + args->nsid = call_map->l_ns; } /* Maybe we have to expand a DST. */ @@ -238,7 +249,7 @@ dl_open_worker (void *a) /* Load the named object. */ args->map = new = GLRO(dl_map_object) (call_map, file, 0, lt_loaded, 0, - mode | __RTLD_CALLMAP); + mode | __RTLD_CALLMAP, args->nsid); /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is set and the object is not already loaded. */ @@ -252,21 +263,30 @@ dl_open_worker (void *a) /* This happens only if we load a DSO for 'sprof'. */ return; + /* This object is directly loaded. */ + ++new->l_direct_opencount; + /* It was already open. */ if (__builtin_expect (new->l_searchlist.r_list != NULL, 0)) { /* Let the user know about the opencount. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) - GLRO(dl_debug_printf) ("opening file=%s; opencount == %u\n\n", - new->l_name, new->l_opencount); + GLRO(dl_debug_printf) ("opening file=%s [%lu]; opencount=%u\n\n", + new->l_name, new->l_ns, new->l_opencount); /* 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); - /* Increment just the reference counter of the object. */ - ++new->l_opencount; + if (new->l_direct_opencount == 1) + /* This is the only direct reference. Increment all the + dependencies' reference counter. */ + for (i = 0; i < new->l_searchlist.r_nlist; ++i) + ++new->l_searchlist.r_list[i]->l_opencount; + else + /* Increment just the reference counter of the object. */ + ++new->l_opencount; return; } @@ -277,8 +297,9 @@ dl_open_worker (void *a) /* So far, so good. Now check the versions. */ for (i = 0; i < new->l_searchlist.r_nlist; ++i) - if (new->l_searchlist.r_list[i]->l_versions == NULL) - (void) GLRO(dl_check_map_versions) (new->l_searchlist.r_list[i], 0, 0); + if (new->l_searchlist.r_list[i]->l_real->l_versions == NULL) + (void) GLRO(dl_check_map_versions) (new->l_searchlist.r_list[i]->l_real, + 0, 0); #ifdef SCOPE_DEBUG show_scope (new); @@ -295,7 +316,7 @@ dl_open_worker (void *a) l = l->l_next; while (1) { - if (! l->l_relocated) + if (! l->l_real->l_relocated) { #ifdef SHARED if (GLRO(dl_profile) != NULL) @@ -349,7 +370,7 @@ dl_open_worker (void *a) loaded object to the scope. */ for (i = 0; i < new->l_searchlist.r_nlist; ++i) if (++new->l_searchlist.r_list[i]->l_opencount > 1 - && new->l_searchlist.r_list[i]->l_type == lt_loaded) + && new->l_real->l_searchlist.r_list[i]->l_type == lt_loaded) { struct link_map *imap = new->l_searchlist.r_list[i]; struct r_scope_elem **runp = imap->l_scope; @@ -503,14 +524,14 @@ cannot create TLS data structures")); /* Let the user know about the opencount. */ if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_FILES, 0)) - GLRO(dl_debug_printf) ("opening file=%s; opencount == %u\n\n", - new->l_name, new->l_opencount); + GLRO(dl_debug_printf) ("opening file=%s [%lu]; opencount=%u\n\n", + new->l_name, new->l_ns, new->l_opencount); } void * internal_function -_dl_open (const char *file, int mode, const void *caller_dlopen) +_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid) { struct dl_open_args args; const char *objname; @@ -525,11 +546,29 @@ _dl_open (const char *file, int mode, const void *caller_dlopen) /* Make sure we are alone. */ __rtld_lock_lock_recursive (GL(dl_load_lock)); + if (nsid == LM_ID_NEWLM) + { + /* Find a new namespace. */ + for (nsid = 1; nsid < DL_NNS; ++nsid) + if (GL(dl_ns)[nsid]._ns_loaded == NULL) + break; + + if (nsid == DL_NNS) + { + /* No more namespace available. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + + GLRO(dl_signal_error) (EINVAL, file, NULL, N_("\ +no more namespaces available for dlmopen()")); + } + } + args.file = file; args.mode = mode; args.caller_dlopen = caller_dlopen; args.caller_dl_open = RETURN_ADDRESS (0); args.map = NULL; + args.nsid = nsid; errcode = GLRO(dl_catch_error) (&objname, &errstring, dl_open_worker, &args); #ifndef MAP_COPY diff --git a/elf/dl-support.c b/elf/dl-support.c index c5976cd..d8957ce 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -67,10 +67,8 @@ const char *_dl_origin_path; /* Nonzero if runtime lookup should not update the .got/.plt. */ int _dl_bind_not; -/* Initially empty list of loaded objects. */ -struct link_map *_dl_loaded; -/* Number of object in the _dl_loaded list. */ -unsigned int _dl_nloaded; +/* Namespace information. */ +struct link_namespaces _dl_ns[DL_NNS]; /* Incremented whenever something may have been added to dl_loaded. */ unsigned long long _dl_load_adds; @@ -79,12 +77,6 @@ unsigned long long _dl_load_adds; main application but here we don't have something like this. So create a fake scope containing nothing. */ struct r_scope_elem _dl_initial_searchlist; -/* Variable which can be used in lookup to process the global scope. */ -struct r_scope_elem *_dl_global_scope[2] = { &_dl_initial_searchlist, NULL }; -/* This is a global pointer to this structure which is public. It is - used by dlopen/dlclose to add and remove objects from what is regarded - to be the global scope. */ -struct r_scope_elem *_dl_main_searchlist = &_dl_initial_searchlist; #ifndef HAVE_INLINED_SYSCALLS /* Nonzero during startup. */ @@ -109,11 +101,6 @@ hp_timing_t _dl_cpuclock_offset; void (*_dl_init_static_tls) (struct link_map *) = &_dl_nothread_init_static_tls; #endif -/* 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 _dl_global_scope_alloc; - size_t _dl_pagesize; unsigned int _dl_osversion; diff --git a/elf/dl-sym.c b/elf/dl-sym.c index a946102..ba00ef5 100644 --- a/elf/dl-sym.c +++ b/elf/dl-sym.c @@ -69,17 +69,19 @@ do_sym (void *handle, const char *name, void *who, /* If the address is not recognized the call comes from the main program (we hope). */ - struct link_map *match = GL(dl_loaded); + struct link_map *match = GL(dl_ns)[LM_ID_BASE]._ns_loaded; /* Find the highest-addressed object that CALLER is not below. */ - for (struct link_map *l = GL(dl_loaded); l != NULL; l = l->l_next) - if (caller >= l->l_map_start && caller < l->l_map_end) - { - /* There must be exactly one DSO for the range of the virtual - memory. Otherwise something is really broken. */ - match = l; - break; - } + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) + for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL; + l = l->l_next) + if (caller >= l->l_map_start && caller < l->l_map_end) + { + /* There must be exactly one DSO for the range of the virtual + memory. Otherwise something is really broken. */ + match = l; + break; + } if (handle == RTLD_DEFAULT) /* Search the global scope. */ @@ -88,7 +90,7 @@ do_sym (void *handle, const char *name, void *who, NULL); else if (handle == RTLD_NEXT) { - if (__builtin_expect (match == GL(dl_loaded), 0)) + if (__builtin_expect (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded, 0)) { if (match == NULL || caller < match->l_map_start diff --git a/elf/dl-version.c b/elf/dl-version.c index 9176aeb..d5fe2f4 100644 --- a/elf/dl-version.c +++ b/elf/dl-version.c @@ -59,7 +59,8 @@ find_needed (const char *name, struct link_map *map) struct link_map *tmap; unsigned int n; - for (tmap = GL(dl_loaded); tmap != NULL; tmap = tmap->l_next) + for (tmap = GL(dl_ns)[map->l_ns]._ns_loaded; tmap != NULL; + tmap = tmap->l_next) if (_dl_name_match_p (name, tmap)) return tmap; @@ -243,7 +244,7 @@ _dl_check_map_versions (struct link_map *map, int verbose, int trace_mode) ? map->l_name : rtld_progname), aux->vna_hash, strtab + aux->vna_name, - needed, verbose, + needed->l_real, verbose, aux->vna_flags & VER_FLG_WEAK); /* Compare the version index. */ diff --git a/elf/do-lookup.h b/elf/do-lookup.h index 9e78dbf..e57d9df 100644 --- a/elf/do-lookup.h +++ b/elf/do-lookup.h @@ -42,7 +42,7 @@ do_lookup_x (const char *undef_name, unsigned long int hash, int num_versions = 0; const ElfW(Sym) *versioned_sym = NULL; - map = list[i]; + map = list[i]->l_real; /* Here come the extra test needed for `_dl_lookup_symbol_skip'. */ if (skip != NULL && map == skip) diff --git a/elf/rtld.c b/elf/rtld.c index 5e18a49..3346abf 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -269,6 +269,7 @@ _dl_start_final (void *arg, struct dl_start_final_info *info) GL(dl_rtld_map).l_mach = info->l.l_mach; #endif _dl_setup_hash (&GL(dl_rtld_map)); + GL(dl_rtld_map).l_real = &GL(dl_rtld_map); GL(dl_rtld_map).l_opencount = 1; GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin; GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end; @@ -585,14 +586,16 @@ map_doit (void *a) { struct map_args *args = (struct map_args *) a; args->map = _dl_map_object (args->loader, args->str, - args->is_preloaded, lt_library, 0, args->mode); + args->is_preloaded, lt_library, 0, args->mode, + LM_ID_BASE); } static void version_check_doit (void *a) { struct version_check_args *args = (struct version_check_args *) a; - if (_dl_check_all_versions (GL(dl_loaded), 1, args->dotrace) && args->doexit) + if (_dl_check_all_versions (GL(dl_ns)[LM_ID_BASE]._ns_loaded, 1, + args->dotrace) && args->doexit) /* We cannot start the application. Abort now. */ _exit (1); } @@ -601,11 +604,12 @@ version_check_doit (void *a) static inline struct link_map * find_needed (const char *name) { - unsigned int n = GL(dl_loaded)->l_searchlist.r_nlist; + struct r_scope_elem *scope = &GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_searchlist; + unsigned int n = scope->r_nlist; while (n-- > 0) - if (_dl_name_match_p (name, GL(dl_loaded)->l_searchlist.r_list[n])) - return GL(dl_loaded)->l_searchlist.r_list[n]; + if (_dl_name_match_p (name, scope->r_list[n])) + return scope->r_list[n]; /* Should never happen. */ return NULL; @@ -685,6 +689,7 @@ dl_main (const ElfW(Phdr) *phdr, enum mode mode; struct link_map **preloads; unsigned int npreloads; + struct link_map *main_map; size_t file_size; char *file; bool has_interp = false; @@ -860,31 +865,35 @@ of this helper program; chances are you did not intend to run this program.\n\ { HP_TIMING_NOW (start); _dl_map_object (NULL, rtld_progname, 0, lt_library, 0, - __RTLD_OPENEXEC); + __RTLD_OPENEXEC, LM_ID_BASE); HP_TIMING_NOW (stop); HP_TIMING_DIFF (load_time, start, stop); } - phdr = GL(dl_loaded)->l_phdr; - phnum = GL(dl_loaded)->l_phnum; + /* Now the map for the main executable is available. */ + main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + + phdr = main_map->l_phdr; + phnum = main_map->l_phnum; /* We overwrite here a pointer to a malloc()ed string. But since the malloc() implementation used at this point is the dummy implementations which has no real free() function it does not makes sense to free the old string first. */ - GL(dl_loaded)->l_name = (char *) ""; - *user_entry = GL(dl_loaded)->l_entry; + main_map->l_name = (char *) ""; + *user_entry = main_map->l_entry; } else { /* Create a link_map for the executable itself. This will be what dlopen on "" returns. */ - _dl_new_object ((char *) "", "", lt_executable, NULL, 0); - if (GL(dl_loaded) == NULL) + _dl_new_object ((char *) "", "", lt_executable, NULL, 0, LM_ID_BASE); + main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + if (main_map == NULL) _dl_fatal_printf ("cannot allocate memory for link map\n"); - GL(dl_loaded)->l_phdr = phdr; - GL(dl_loaded)->l_phnum = phnum; - GL(dl_loaded)->l_entry = *user_entry; + main_map->l_phdr = phdr; + main_map->l_phnum = phnum; + main_map->l_entry = *user_entry; /* At this point we are in a bit of trouble. We would have to fill in the values for l_dev and l_ino. But in general we @@ -905,12 +914,14 @@ of this helper program; chances are you did not intend to run this program.\n\ information for the program. */ } - GL(dl_loaded)->l_map_end = 0; - GL(dl_loaded)->l_text_end = 0; + main_map->l_map_end = 0; + main_map->l_text_end = 0; /* Perhaps the executable has no PT_LOAD header entries at all. */ - GL(dl_loaded)->l_map_start = ~0; + main_map->l_map_start = ~0; /* We opened the file, account for it. */ - ++GL(dl_loaded)->l_opencount; + ++main_map->l_opencount; + /* And it was opened directly. */ + ++main_map->l_direct_opencount; /* Scan the program header table for the dynamic section. */ for (ph = phdr; ph < &phdr[phnum]; ++ph) @@ -918,12 +929,12 @@ of this helper program; chances are you did not intend to run this program.\n\ { case PT_PHDR: /* Find out the load address. */ - GL(dl_loaded)->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr; + main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr; break; case PT_DYNAMIC: /* This tells us where to find the dynamic section, which tells us everything we need to do. */ - GL(dl_loaded)->l_ld = (void *) GL(dl_loaded)->l_addr + ph->p_vaddr; + main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr; break; case PT_INTERP: /* This "interpreter segment" was used by the program loader to @@ -932,7 +943,7 @@ of this helper program; chances are you did not intend to run this program.\n\ dlopen call or DT_NEEDED entry, for something that wants to link against the dynamic linker as a shared library, will know that the shared object is already loaded. */ - _dl_rtld_libname.name = ((const char *) GL(dl_loaded)->l_addr + _dl_rtld_libname.name = ((const char *) main_map->l_addr + ph->p_vaddr); /* _dl_rtld_libname.next = NULL; Already zero. */ GL(dl_rtld_map).l_libname = &_dl_rtld_libname; @@ -968,17 +979,16 @@ of this helper program; chances are you did not intend to run this program.\n\ ElfW(Addr) allocend; /* Remember where the main program starts in memory. */ - mapstart = (GL(dl_loaded)->l_addr - + (ph->p_vaddr & ~(ph->p_align - 1))); - if (GL(dl_loaded)->l_map_start > mapstart) - GL(dl_loaded)->l_map_start = mapstart; + mapstart = (main_map->l_addr + (ph->p_vaddr & ~(ph->p_align - 1))); + if (main_map->l_map_start > mapstart) + main_map->l_map_start = mapstart; /* Also where it ends. */ - allocend = GL(dl_loaded)->l_addr + ph->p_vaddr + ph->p_memsz; - if (GL(dl_loaded)->l_map_end < allocend) - GL(dl_loaded)->l_map_end = allocend; - if ((ph->p_flags & PF_X) && allocend > GL(dl_loaded)->l_text_end) - GL(dl_loaded)->l_text_end = allocend; + allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz; + if (main_map->l_map_end < allocend) + main_map->l_map_end = allocend; + if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end) + main_map->l_text_end = allocend; } break; #ifdef USE_TLS @@ -989,18 +999,18 @@ of this helper program; chances are you did not intend to run this program.\n\ here since we read the PT_TLS entry already in _dl_start_final. But the result is repeatable so do not check for this special but unimportant case. */ - GL(dl_loaded)->l_tls_blocksize = ph->p_memsz; - GL(dl_loaded)->l_tls_align = ph->p_align; + main_map->l_tls_blocksize = ph->p_memsz; + main_map->l_tls_align = ph->p_align; if (ph->p_align == 0) - GL(dl_loaded)->l_tls_firstbyte_offset = 0; + main_map->l_tls_firstbyte_offset = 0; else - GL(dl_loaded)->l_tls_firstbyte_offset = (ph->p_vaddr - & (ph->p_align - 1)); - GL(dl_loaded)->l_tls_initimage_size = ph->p_filesz; - GL(dl_loaded)->l_tls_initimage = (void *) ph->p_vaddr; + main_map->l_tls_firstbyte_offset = (ph->p_vaddr + & (ph->p_align - 1)); + main_map->l_tls_initimage_size = ph->p_filesz; + main_map->l_tls_initimage = (void *) ph->p_vaddr; /* This image gets the ID one. */ - GL(dl_tls_max_dtv_idx) = GL(dl_loaded)->l_tls_modid = 1; + GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1; } break; #endif @@ -1009,21 +1019,21 @@ of this helper program; chances are you did not intend to run this program.\n\ break; case PT_GNU_RELRO: - GL(dl_loaded)->l_relro_addr = ph->p_vaddr; - GL(dl_loaded)->l_relro_size = ph->p_memsz; + main_map->l_relro_addr = ph->p_vaddr; + main_map->l_relro_size = ph->p_memsz; break; } #ifdef USE_TLS /* Adjust the address of the TLS initialization image in case the executable is actually an ET_DYN object. */ - if (GL(dl_loaded)->l_tls_initimage != NULL) - GL(dl_loaded)->l_tls_initimage - = (char *) GL(dl_loaded)->l_tls_initimage + GL(dl_loaded)->l_addr; + if (main_map->l_tls_initimage != NULL) + main_map->l_tls_initimage + = (char *) main_map->l_tls_initimage + main_map->l_addr; #endif - if (! GL(dl_loaded)->l_map_end) - GL(dl_loaded)->l_map_end = ~0; - if (! GL(dl_loaded)->l_text_end) - GL(dl_loaded)->l_text_end = ~0; + if (! main_map->l_map_end) + main_map->l_map_end = ~0; + if (! main_map->l_text_end) + main_map->l_text_end = ~0; if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name) { /* We were invoked directly, so the program might not have a @@ -1038,9 +1048,9 @@ of this helper program; chances are you did not intend to run this program.\n\ if (! rtld_is_main) { /* Extract the contents of the dynamic section for easy access. */ - elf_get_dynamic_info (GL(dl_loaded), NULL); + elf_get_dynamic_info (main_map, NULL); /* Set up our cache of pointers into the hash table. */ - _dl_setup_hash (GL(dl_loaded)); + _dl_setup_hash (main_map); } if (__builtin_expect (mode, normal) == verify) @@ -1049,7 +1059,7 @@ of this helper program; chances are you did not intend to run this program.\n\ executable using us as the program interpreter. Exit with an error if we were not able to load the binary or no interpreter is specified (i.e., this is no dynamically linked binary. */ - if (GL(dl_loaded)->l_ld == NULL) + if (main_map->l_ld == NULL) _exit (1); /* We allow here some platform specific code. */ @@ -1072,16 +1082,16 @@ of this helper program; chances are you did not intend to run this program.\n\ found by the PT_INTERP name. */ GL(dl_rtld_map).l_name = (char *) GL(dl_rtld_map).l_libname->name; GL(dl_rtld_map).l_type = lt_library; - GL(dl_loaded)->l_next = &GL(dl_rtld_map); - GL(dl_rtld_map).l_prev = GL(dl_loaded); - ++GL(dl_nloaded); + main_map->l_next = &GL(dl_rtld_map); + GL(dl_rtld_map).l_prev = main_map; + ++GL(dl_ns)[LM_ID_BASE]._ns_nloaded; ++GL(dl_load_adds); /* If LD_USE_LOAD_BIAS env variable has not been seen, default to not using bias for non-prelinked PIEs and libraries and using it for executables or prelinked PIEs or libraries. */ if (GLRO(dl_use_load_bias) == (ElfW(Addr)) -2) - GLRO(dl_use_load_bias) = (GL(dl_loaded)->l_addr == 0) ? -1 : 0; + GLRO(dl_use_load_bias) = main_map->l_addr == 0 ? -1 : 0; /* Set up the program header information for the dynamic linker itself. It is needed in the dl_iterate_phdr() callbacks. */ @@ -1125,8 +1135,9 @@ of this helper program; chances are you did not intend to run this program.\n\ && (__builtin_expect (! INTUSE(__libc_enable_secure), 1) || strchr (p, '/') == NULL)) { - struct link_map *new_map = _dl_map_object (GL(dl_loaded), p, 1, - lt_library, 0, 0); + struct link_map *new_map = _dl_map_object (main_map, p, 1, + lt_library, 0, 0, + LM_ID_BASE); if (++new_map->l_opencount == 1) /* It is no duplicate. */ ++npreloads; @@ -1208,7 +1219,7 @@ of this helper program; chances are you did not intend to run this program.\n\ struct map_args args; args.str = p; - args.loader = GL(dl_loaded); + args.loader = main_map; args.is_preloaded = 1; args.mode = 0; @@ -1231,8 +1242,9 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", if (problem != NULL) { char *p = strndupa (problem, file_size - (problem - file)); - struct link_map *new_map = _dl_map_object (GL(dl_loaded), p, 1, - lt_library, 0, 0); + struct link_map *new_map = _dl_map_object (main_map, p, 1, + lt_library, 0, 0, + LM_ID_BASE); if (++new_map->l_opencount == 1) /* It is no duplicate. */ ++npreloads; @@ -1272,7 +1284,7 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", We just want our data structures to describe it as if we had just mapped and relocated it normally. */ struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL, - 0); + 0, LM_ID_BASE); if (__builtin_expect (l != NULL, 1)) { static ElfW(Dyn) dyn_temp[DL_RO_DYN_TEMP_CNT] attribute_relro; @@ -1337,18 +1349,18 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", specified some libraries to load, these are inserted before the actual dependencies in the executable's searchlist for symbol resolution. */ HP_TIMING_NOW (start); - _dl_map_object_deps (GL(dl_loaded), preloads, npreloads, mode == trace, 0); + _dl_map_object_deps (main_map, preloads, npreloads, mode == trace, 0); HP_TIMING_NOW (stop); HP_TIMING_DIFF (diff, start, stop); HP_TIMING_ACCUM_NT (load_time, diff); /* Mark all objects as being in the global scope and set the open counter. */ - for (i = GL(dl_loaded)->l_searchlist.r_nlist; i > 0; ) + for (i = main_map->l_searchlist.r_nlist; i > 0; ) { --i; - GL(dl_loaded)->l_searchlist.r_list[i]->l_global = 1; - ++GL(dl_loaded)->l_searchlist.r_list[i]->l_opencount; + main_map->l_searchlist.r_list[i]->l_global = 1; + ++main_map->l_searchlist.r_list[i]->l_opencount; } #ifndef MAP_ANON @@ -1369,13 +1381,13 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", chain in symbol search order because gdb uses the chain's order as its symbol search order. */ i = 1; - while (GL(dl_loaded)->l_searchlist.r_list[i] != &GL(dl_rtld_map)) + while (main_map->l_searchlist.r_list[i] != &GL(dl_rtld_map)) ++i; - GL(dl_rtld_map).l_prev = GL(dl_loaded)->l_searchlist.r_list[i - 1]; + GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1]; if (__builtin_expect (mode, normal) == normal) { - GL(dl_rtld_map).l_next = (i + 1 < GL(dl_loaded)->l_searchlist.r_nlist - ? GL(dl_loaded)->l_searchlist.r_list[i + 1] + GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist + ? main_map->l_searchlist.r_list[i + 1] : NULL); #ifdef NEED_DL_SYSINFO if (sysinfo_map != NULL @@ -1459,7 +1471,7 @@ ERROR: ld.so: object '%s' from %s cannot be preloaded: ignored.\n", GL(dl_tls_dtv_slotinfo_list)->next = NULL; /* Fill in the information from the loaded modules. */ - for (l = GL(dl_loaded), i = 0; l != NULL; l = l->l_next) + for (l = main_map, i = 0; l != NULL; l = l->l_next) if (l->l_tls_blocksize != 0) /* This is a module with TLS data. Store the map reference. The generation counter is zero. */ @@ -1495,7 +1507,7 @@ cannot allocate TLS data structures for initial thread"); if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) { - struct r_scope_elem *scope = &GL(dl_loaded)->l_searchlist; + struct r_scope_elem *scope = &main_map->l_searchlist; for (i = 0; i < scope->r_nlist; i++) { @@ -1531,7 +1543,7 @@ cannot allocate TLS data structures for initial thread"); /* Look through the dependencies of the main executable and determine which of them is not actually required. */ - struct link_map *l = GL(dl_loaded); + struct link_map *l = main_map; /* Relocate the main executable. */ struct relocate_args args = { .l = l, .lazy = GLRO(dl_lazy) }; @@ -1539,7 +1551,7 @@ cannot allocate TLS data structures for initial thread"); /* This loop depends on the dependencies of the executable to correspond in number and order to the DT_NEEDED entries. */ - ElfW(Dyn) *dyn = GL(dl_loaded)->l_ld; + ElfW(Dyn) *dyn = main_map->l_ld; bool first = true; while (dyn->d_tag != DT_NULL) { @@ -1564,11 +1576,11 @@ cannot allocate TLS data structures for initial thread"); _exit (first != true); } - else if (! GL(dl_loaded)->l_info[DT_NEEDED]) + else if (! main_map->l_info[DT_NEEDED]) _dl_printf ("\tstatically linked\n"); else { - for (l = GL(dl_loaded)->l_next; l; l = l->l_next) + for (l = main_map->l_next; l; l = l->l_next) if (l->l_faked) /* The library was not found. */ _dl_printf ("\t%s => not found\n", l->l_libname->name); @@ -1589,8 +1601,8 @@ cannot allocate TLS data structures for initial thread"); ElfW(Addr) loadbase; lookup_t result; - result = _dl_lookup_symbol_x (INTUSE(_dl_argv)[i], GL(dl_loaded), - &ref, GL(dl_loaded)->l_scope, NULL, + result = _dl_lookup_symbol_x (INTUSE(_dl_argv)[i], main_map, + &ref, main_map->l_scope, NULL, ELF_RTYPE_CLASS_PLT, DL_LOOKUP_ADD_DEPENDENCY, NULL); @@ -1613,7 +1625,7 @@ cannot allocate TLS data structures for initial thread"); args.lazy = GLRO(dl_lazy); - l = GL(dl_loaded); + l = main_map; while (l->l_next) l = l->l_next; do @@ -1629,7 +1641,7 @@ cannot allocate TLS data structures for initial thread"); if ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK) && GL(dl_rtld_map).l_opencount > 1) - _dl_relocate_object (&GL(dl_rtld_map), GL(dl_loaded)->l_scope, + _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0); } @@ -1639,9 +1651,9 @@ cannot allocate TLS data structures for initial thread"); /* Print more information. This means here, print information about the versions needed. */ int first = 1; - struct link_map *map = GL(dl_loaded); + struct link_map *map; - for (map = GL(dl_loaded); map != NULL; map = map->l_next) + for (map = main_map; map != NULL; map = map->l_next) { const char *strtab; ElfW(Dyn) *dyn = map->l_info[VERNEEDTAG]; @@ -1709,28 +1721,27 @@ cannot allocate TLS data structures for initial thread"); _exit (0); } - if (GL(dl_loaded)->l_info [ADDRIDX (DT_GNU_LIBLIST)] + if (main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)] && ! __builtin_expect (GLRO(dl_profile) != NULL, 0)) { ElfW(Lib) *liblist, *liblistend; struct link_map **r_list, **r_listend, *l; - const char *strtab = (const void *) D_PTR (GL(dl_loaded), - l_info[DT_STRTAB]); + const char *strtab = (const void *) D_PTR (main_map, l_info[DT_STRTAB]); - assert (GL(dl_loaded)->l_info [VALIDX (DT_GNU_LIBLISTSZ)] != NULL); + assert (main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)] != NULL); liblist = (ElfW(Lib) *) - GL(dl_loaded)->l_info [ADDRIDX (DT_GNU_LIBLIST)]->d_un.d_ptr; + main_map->l_info[ADDRIDX (DT_GNU_LIBLIST)]->d_un.d_ptr; liblistend = (ElfW(Lib) *) - ((char *) liblist - + GL(dl_loaded)->l_info [VALIDX (DT_GNU_LIBLISTSZ)]->d_un.d_val); - r_list = GL(dl_loaded)->l_searchlist.r_list; - r_listend = r_list + GL(dl_loaded)->l_searchlist.r_nlist; + ((char *) liblist + + main_map->l_info[VALIDX (DT_GNU_LIBLISTSZ)]->d_un.d_val); + r_list = main_map->l_searchlist.r_list; + r_listend = r_list + main_map->l_searchlist.r_nlist; for (; r_list < r_listend && liblist < liblistend; r_list++) { l = *r_list; - if (l == GL(dl_loaded)) + if (l == main_map) continue; /* If the library is not mapped where it should, fail. */ @@ -1767,9 +1778,7 @@ cannot allocate TLS data structures for initial thread"); /* Initialize _r_debug. */ struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr); { - struct link_map *l; - - l = GL(dl_loaded); + struct link_map *l = main_map; #ifdef ELF_MACHINE_DEBUG_SETUP @@ -1793,18 +1802,18 @@ cannot allocate TLS data structures for initial thread"); } /* Now set up the variable which helps the assembler startup code. */ - GL(dl_main_searchlist) = &GL(dl_loaded)->l_searchlist; - GL(dl_global_scope)[0] = &GL(dl_loaded)->l_searchlist; + GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist = &main_map->l_searchlist; + GL(dl_ns)[LM_ID_BASE]._ns_global_scope[0] = &main_map->l_searchlist; /* Save the information about the original global scope list since we need it in the memory handling later. */ - GLRO(dl_initial_searchlist) = *GL(dl_main_searchlist); + GLRO(dl_initial_searchlist) = *GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist; if (prelinked) { struct link_map *l; - if (GL(dl_loaded)->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL) + if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL) { ElfW(Rela) *conflict, *conflictend; #ifndef HP_TIMING_NONAVAIL @@ -1813,20 +1822,20 @@ cannot allocate TLS data structures for initial thread"); #endif HP_TIMING_NOW (start); - assert (GL(dl_loaded)->l_info [VALIDX (DT_GNU_CONFLICTSZ)] != NULL); + assert (main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)] != NULL); conflict = (ElfW(Rela) *) - GL(dl_loaded)->l_info [ADDRIDX (DT_GNU_CONFLICT)]->d_un.d_ptr; + main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)]->d_un.d_ptr; conflictend = (ElfW(Rela) *) ((char *) conflict - + GL(dl_loaded)->l_info [VALIDX (DT_GNU_CONFLICTSZ)]->d_un.d_val); - _dl_resolve_conflicts (GL(dl_loaded), conflict, conflictend); + + main_map->l_info [VALIDX (DT_GNU_CONFLICTSZ)]->d_un.d_val); + _dl_resolve_conflicts (main_map, conflict, conflictend); HP_TIMING_NOW (stop); HP_TIMING_DIFF (relocate_time, start, stop); } /* Mark all the objects so we know they have been already relocated. */ - for (l = GL(dl_loaded); l != NULL; l = l->l_next) + for (l = main_map; l != NULL; l = l->l_next) { l->l_relocated = 1; if (l->l_relro_size) @@ -1857,7 +1866,7 @@ cannot allocate TLS data structures for initial thread"); /* If we are profiling we also must do lazy reloaction. */ GLRO(dl_lazy) |= consider_profiling; - l = GL(dl_loaded); + l = main_map; while (l->l_next) l = l->l_next; @@ -1906,7 +1915,7 @@ cannot allocate TLS data structures for initial thread"); /* There was an explicit ref to the dynamic linker as a shared lib. Re-relocate ourselves with user-controlled symbol definitions. */ HP_TIMING_NOW (start); - _dl_relocate_object (&GL(dl_rtld_map), GL(dl_loaded)->l_scope, 0, 0); + _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0); HP_TIMING_NOW (stop); HP_TIMING_DIFF (add, start, stop); HP_TIMING_ACCUM_NT (relocate_time, add); @@ -2323,20 +2332,24 @@ print_statistics (hp_timing_t *rtld_total_timep) #endif unsigned long int num_relative_relocations = 0; - struct r_scope_elem *scope = &GL(dl_loaded)->l_searchlist; - unsigned int i; - - for (i = 0; i < scope->r_nlist; i++) + for (Lmid_t ns = 0; ns < DL_NNS; ++ns) { - struct link_map *l = scope->r_list [i]; + struct r_scope_elem *scope = &GL(dl_ns)[ns]._ns_loaded->l_searchlist; - if (!l->l_addr) - continue; + for (unsigned int i = 0; i < scope->r_nlist; i++) + { + struct link_map *l = scope->r_list [i]; + + if (!l->l_addr) + continue; - if (l->l_info[VERSYMIDX (DT_RELCOUNT)]) - num_relative_relocations += l->l_info[VERSYMIDX (DT_RELCOUNT)]->d_un.d_val; - if (l->l_info[VERSYMIDX (DT_RELACOUNT)]) - num_relative_relocations += l->l_info[VERSYMIDX (DT_RELACOUNT)]->d_un.d_val; + if (l->l_info[VERSYMIDX (DT_RELCOUNT)]) + num_relative_relocations + += l->l_info[VERSYMIDX (DT_RELCOUNT)]->d_un.d_val; + if (l->l_info[VERSYMIDX (DT_RELACOUNT)]) + num_relative_relocations + += l->l_info[VERSYMIDX (DT_RELACOUNT)]->d_un.d_val; + } } _dl_debug_printf (" number of relocations: %lu\n" diff --git a/elf/tst-dlmopen1.c b/elf/tst-dlmopen1.c new file mode 100644 index 0000000..9839267 --- /dev/null +++ b/elf/tst-dlmopen1.c @@ -0,0 +1,80 @@ +#include +#include +#include + + +static int +do_test (void) +{ + void *h = dlopen (LIBC_SO, RTLD_LAZY|RTLD_NOLOAD); + if (h == NULL) + { + printf ("cannot get handle for %s: %s\n", LIBC_SO, dlerror ()); + return 1; + } + + Lmid_t ns = -10; + if (dlinfo (h, RTLD_DI_LMID, &ns) != 0) + { + printf ("dlinfo for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + if (ns != LM_ID_BASE) + { + printf ("namespace for %s not LM_ID_BASE\n", LIBC_SO); + return 1; + } + + if (dlclose (h) != 0) + { + printf ("dlclose for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + h = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", RTLD_LAZY); + if (h == NULL) + { + printf ("cannot get handle for %s: %s\n", + "tst-dlmopen1mod.so", dlerror ()); + return 1; + } + + ns = -10; + if (dlinfo (h, RTLD_DI_LMID, &ns) != 0) + { + printf ("dlinfo for %s in %s failed: %s\n", + "tst-dlmopen1mod.so", __func__, dlerror ()); + return 1; + } + + if (ns == LM_ID_BASE) + { + printf ("namespace for %s is LM_ID_BASE\n", LIBC_SO); + return 1; + } + + int (*fct) (Lmid_t) = dlsym (h, "foo"); + if (fct == NULL) + { + printf ("could not find %s: %s\n", "foo", dlerror ()); + return 1; + } + + if (fct (ns) != 0) + return 1; + + if (dlclose (h) != 0) + { + printf ("dlclose for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/elf/tst-dlmopen1mod.c b/elf/tst-dlmopen1mod.c new file mode 100644 index 0000000..40a4c8f --- /dev/null +++ b/elf/tst-dlmopen1mod.c @@ -0,0 +1,38 @@ +#include +#include +#include + + +int +foo (Lmid_t ns2) +{ + void *h = dlopen (LIBC_SO, RTLD_LAZY|RTLD_NOLOAD); + if (h == NULL) + { + printf ("cannot get handle for %s: %s\n", LIBC_SO, dlerror ()); + return 1; + } + + Lmid_t ns = -10; + if (dlinfo (h, RTLD_DI_LMID, &ns) != 0) + { + printf ("dlinfo for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + if (ns != ns2) + { + printf ("namespace for %s not LM_ID_BASE\n", LIBC_SO); + return 1; + } + + if (dlclose (h) != 0) + { + printf ("dlclose for %s in %s failed: %s\n", + LIBC_SO, __func__, dlerror ()); + return 1; + } + + return 0; +} diff --git a/elf/tst-dlmopen2.c b/elf/tst-dlmopen2.c new file mode 100644 index 0000000..0569997 --- /dev/null +++ b/elf/tst-dlmopen2.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + + +static int +do_test (void) +{ + int result = 0; + + for (int i = 1; i <= 10; ++i) + { + void *h[DL_NNS - 1]; + char used[DL_NNS]; + + printf ("round %d\n", i); + + memset (used, '\0', sizeof (used)); + used[LM_ID_BASE] = 1; + + for (int j = 0; j < DL_NNS - 1; ++j) + { + h[j] = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", + RTLD_LAZY); + if (h[j] == NULL) + { + printf ("round %d, namespace %d: load failed: %s\n", + i, j, dlerror ()); + return 1; + } + Lmid_t ns; + if (dlinfo (h[j], RTLD_DI_LMID, &ns) != 0) + { + printf ("round %d, namespace %d: dlinfo failed: %s\n", + i, j, dlerror ()); + return 1; + } + if (ns < 0 || ns >= DL_NNS) + { + printf ("round %d, namespace %d: invalid namespace %ld", + i, j, (long int) ns); + result = 1; + } + else if (used[ns] != 0) + { + printf ("\ +round %d, namespace %d: duplicate allocate of namespace %ld", + i, j, (long int) ns); + result = 1; + } + else + used[ns] = 1; + } + + for (int j = 0; j < DL_NNS - 1; ++j) + if (dlclose (h[j]) != 0) + { + printf ("round %d, namespace %d: close failed: %s\n", + i, j, dlerror ()); + return 1; + } + } + + return result; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" -- cgit v1.1