summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos O'Donell <carlos@systemhalted.org>2015-09-18 00:12:22 -0400
committerCarlos O'Donell <carlos@systemhalted.org>2015-09-18 00:12:22 -0400
commitf3f30c9d5cb98fb90fd6dd2e5362b7fe3ace3720 (patch)
tree06b957bbe6f2b85471697b8341733b162fae9da2
parent8a44513e9ccb271e368b4aa3c9493b94a45e039d (diff)
downloadglibc-carlos/dlmopen.tar.gz
Initial implemenation of full dlmopen support.carlos/dlmopen
-rw-r--r--dlfcn/dlmopen.c5
-rw-r--r--dlfcn/dlopen.c2
-rw-r--r--elf/dl-open.c62
-rw-r--r--elf/dl-support.c13
-rw-r--r--elf/dl-sym.c13
-rw-r--r--elf/tst-dlmopen1.c7
6 files changed, 80 insertions, 22 deletions
diff --git a/dlfcn/dlmopen.c b/dlfcn/dlmopen.c
index 38dca7abc8..ba468d21f0 100644
--- a/dlfcn/dlmopen.c
+++ b/dlfcn/dlmopen.c
@@ -61,11 +61,6 @@ dlmopen_doit (void *a)
if (args->file == NULL)
# endif
GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("invalid namespace"));
-
- /* It makes no sense to use RTLD_GLOBAL when loading a DSO into
- a namespace other than the base namespace. */
- if (__glibc_unlikely (args->mode & RTLD_GLOBAL))
- GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("invalid mode"));
}
args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN,
diff --git a/dlfcn/dlopen.c b/dlfcn/dlopen.c
index 8bf2752d9c..c5e5f1e044 100644
--- a/dlfcn/dlopen.c
+++ b/dlfcn/dlopen.c
@@ -65,7 +65,7 @@ dlopen_doit (void *a)
args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN,
args->caller,
- args->file == NULL ? LM_ID_BASE : NS,
+ NS,
__dlfcn_argc, __dlfcn_argv, __environ);
}
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 5429d181cb..6beac774b0 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -72,6 +72,31 @@ add_to_global (struct link_map *new)
if (new->l_searchlist.r_list[cnt]->l_global == 0)
++to_add;
+ struct link_namespaces *ns = &GL(dl_ns)[new->l_ns];
+
+ if (__glibc_unlikely (new->l_ns != LM_ID_BASE
+ && ns->_ns_main_searchlist == NULL))
+ {
+ /* An initial object was loaded with dlmopen into a distinct namespace
+ that has no global searchlist (RTLD_GLOBAL) and RTLD_GLOBAL was used.
+ Or that object then dlopened another object into the global
+ searchlist. We find ourselves with no global searchlist initialized.
+ We have two choices, either we forbid this scenario and return an
+ error or treat the first RTLD_GLOBAL DSOs searchlist as the global
+ searchlist of the namespace. We do the latter since it's the most
+ sensible course of action since you may dlmopen other libraries which
+ have no idea they have been isolated. Thus RTLD_GLOBAL dlopen calls
+ within the new namespace are restricted to the new namespace and may
+ reference the symbols of the initial RTLD_GLOBAL dlmopen'd
+ libraries. */
+ ns->_ns_main_searchlist = &new->l_searchlist;
+ /* Treat this list like it is read-only. A value of zero forces a copy
+ later if we need to extend this list. The list itself is already
+ being used as the primary scope for the first loaded RTLD_GLOBAL
+ object into the new namespace, thus we don't want to free it. */
+ ns->_ns_global_scope_alloc = 0;
+ }
+
/* The symbols of the new objects and its dependencies are to be
introduced into the global scope that will be used to resolve
references from other dynamically-loaded objects.
@@ -86,7 +111,6 @@ add_to_global (struct link_map *new)
in an realloc() call. Therefore we allocate a completely new
array the first time we have to add something to the locale scope. */
- struct link_namespaces *ns = &GL(dl_ns)[new->l_ns];
if (ns->_ns_global_scope_alloc == 0)
{
/* This is the first dynamic object given global scope. */
@@ -204,15 +228,33 @@ dl_open_worker (void *a)
{
const void *caller_dlopen = args->caller_dlopen;
- /* 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;
-
- struct link_map *l = _dl_find_dso_for_object ((ElfW(Addr)) caller_dlopen);
-
- if (l)
- call_map = l;
-
+ call_map = _dl_find_dso_for_object ((ElfW(Addr)) caller_dlopen);
+
+ /* We support dlmopen (ns, NULL, ...), which should do the same thing
+ as dlopen (NULL, ...). It should provide access to the base loaded
+ object in the namespace. In the case of LM_ID_BASE it's "" i.e. the
+ executable, but in the event of !LM_ID_BASE we need to return the
+ base object in the namespace. The FILE must be adjusted if we are
+ !LM_ID_BASE to be the name for the base object loaded into the
+ namespace or _dl_map_object will fail. */
+ if (call_map != NULL && call_map->l_ns != LM_ID_BASE && file[0] == '\0')
+ file = GL(dl_ns)[call_map->l_ns]._ns_loaded->l_name;
+
+ /* If we know we are using LM_ID_BASE explicitly, then fall back to
+ the base loaded object in that namespace if we failed to find the
+ caller. */
+ if (call_map == NULL && args->nsid == LM_ID_BASE)
+ call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+
+ /* If we don't know know which namespace we are using then a failure to
+ determine the namespace of the caller is now a hard error. In the
+ past we might have returned LM_ID_BASE, but that's invalid for dlmopen
+ since it would lead to leakage outside of the namespace. */
+ if (call_map == NULL && args->nsid == __LM_ID_CALLER)
+ _dl_signal_error (EINVAL, file, NULL, N_("\
+unable to determine caller's namespace"));
+
+ /* Use the caller's namespace if we were loaded into that namespace. */
if (args->nsid == __LM_ID_CALLER)
args->nsid = call_map->l_ns;
}
diff --git a/elf/dl-support.c b/elf/dl-support.c
index e7b5110b59..93670cf5b5 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -305,9 +305,22 @@ void
internal_function
_dl_non_dynamic_init (void)
{
+ extern const ElfW(Ehdr) __ehdr_start
+ __attribute__ ((weak, visibility ("hidden")));
+ extern const ElfW(Addr) __data_start;
+
_dl_main_map.l_origin = _dl_get_origin ();
_dl_main_map.l_phdr = GL(dl_phdr);
_dl_main_map.l_phnum = GL(dl_phnum);
+ /* Starting from binutils-2.23, the linker will define __ehdr_start
+ pointing at the ELF header, which is a good enough marker for the
+ start of main. The start of data (assumed to come after text) is
+ good enough for l_map_end. We want l_map_start and l_map_end set
+ to allow _dl_find_dso_for_object to be able to associate
+ _dl_main_map with a caller's addresses within main. */
+ _dl_main_map.l_map_start = (ElfW(Addr)) &__ehdr_start;
+ _dl_main_map.l_text_end = (ElfW(Addr)) &__data_start;
+ _dl_main_map.l_map_end = _dl_main_map.l_text_end;
if (HP_SMALL_TIMING_AVAIL)
HP_TIMING_NOW (_dl_cpuclock_offset);
diff --git a/elf/dl-sym.c b/elf/dl-sym.c
index 56fea86fa8..917b540e55 100644
--- a/elf/dl-sym.c
+++ b/elf/dl-sym.c
@@ -91,10 +91,17 @@ do_sym (void *handle, const char *name, void *who,
lookup_t result;
ElfW(Addr) caller = (ElfW(Addr)) who;
+ struct link_map *match = NULL;
struct link_map *l = _dl_find_dso_for_object (caller);
- /* If the address is not recognized the call comes from the main
- program (we hope). */
- struct link_map *match = l ? l : GL(dl_ns)[LM_ID_BASE]._ns_loaded;
+
+ /* Failure to determinet the caller's namespace is a fatal
+ error since we need it for handling namespace isolation
+ correctly. Falling back to LM_ID_BASE will break isolation. */
+ if (l == NULL)
+ GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("\
+unable to determine caller's namespace during symbol lookup"));
+
+ match = l;
if (handle == RTLD_DEFAULT)
{
diff --git a/elf/tst-dlmopen1.c b/elf/tst-dlmopen1.c
index 9839267d8f..5a05891846 100644
--- a/elf/tst-dlmopen1.c
+++ b/elf/tst-dlmopen1.c
@@ -2,6 +2,7 @@
#include <stdio.h>
#include <gnu/lib-names.h>
+#define TEST_SO "$ORIGIN/tst-dlmopen1mod.so"
static int
do_test (void)
@@ -34,7 +35,7 @@ do_test (void)
return 1;
}
- h = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", RTLD_LAZY);
+ h = dlmopen (LM_ID_NEWLM, TEST_SO, RTLD_LAZY);
if (h == NULL)
{
printf ("cannot get handle for %s: %s\n",
@@ -52,7 +53,7 @@ do_test (void)
if (ns == LM_ID_BASE)
{
- printf ("namespace for %s is LM_ID_BASE\n", LIBC_SO);
+ printf ("namespace for %s is LM_ID_BASE\n", TEST_SO);
return 1;
}
@@ -69,7 +70,7 @@ do_test (void)
if (dlclose (h) != 0)
{
printf ("dlclose for %s in %s failed: %s\n",
- LIBC_SO, __func__, dlerror ());
+ TEST_SO, __func__, dlerror ());
return 1;
}