aboutsummaryrefslogtreecommitdiff
path: root/elf/dl-open.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2019-10-11 16:11:21 +0200
committerFlorian Weimer <fweimer@redhat.com>2019-11-28 14:28:44 +0100
commit64afc586195fd8dfefd40f60db4e9bead983dbff (patch)
treeeaddace5e977bf7d6e465fd4a3f095d1245bf7e4 /elf/dl-open.c
parent5f53b8777bfaab2afbc56ecc2a223d7167314e83 (diff)
downloadglibc-fw/libc-early-init-2.tar
glibc-fw/libc-early-init-2.tar.gz
glibc-fw/libc-early-init-2.tar.bz2
glibc-fw/libc-early-init-2.zip
Implement __libc_early_initfw/libc-early-init-2
This function is defined in libc.so, and the dynamic loader calls right after relocation has been finished, before any ELF constructors or the preinit function is invoked. It is also used in the static build for initializing parts of the static libc. To locate __libc_early_init, a direct symbol lookup function is used, _dl_lookup_direct. It does not search the entire symbol scope and consults merely a single link map. This function could also be used to implement lookups in the vDSO (as an optimization). A per-namespace variable (libc_map) is added for locating libc.so, to avoid repeated traversals of the search scope. It is similar to GL(dl_initfirst). An alternative would have been to thread a context argument from _dl_open down to _dl_map_object_from_fd (where libc.so is identified). This could have avoided the global variable, but the change would be larger as a result. It would not have been possible to use this to replace GL(dl_initfirst) because that global variable is used to pass the function pointer past the stack switch from dl_main to the main program. Replacing that requires adding a new argument to _dl_init, which in turn needs changes to the architecture-specific libc.so startup code written in assembler. __libc_early_init should not be used to replace _dl_var_init (as it exists today on some architectures). Instead, _dl_lookup_direct should be used to look up a new variable symbol in libc.so, and that should then be initialized from the dynamic loader, immediately after the object has been loaded in _dl_map_object_from_fd (before relocation is run). This way, more IFUNC resolvers which depend on these variables will work.
Diffstat (limited to 'elf/dl-open.c')
-rw-r--r--elf/dl-open.c24
1 files changed, 24 insertions, 0 deletions
diff --git a/elf/dl-open.c b/elf/dl-open.c
index df9f29a5e5..9819b49e4f 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -35,6 +35,7 @@
#include <libc-internal.h>
#include <array_length.h>
#include <internal-signals.h>
+#include <libc-early-init.h>
#include <dl-dst.h>
#include <dl-prop.h>
@@ -53,6 +54,13 @@ struct dl_open_args
/* Namespace ID. */
Lmid_t nsid;
+ /* Set to true if libc.so was already loaded into the namespace at
+ the time dl_open_worker was called. This is used to determine
+ whether libc.so early initialization needs to before, and whether
+ to roll back the cached libc_map value in the namespace in case
+ of a dlopen failure. */
+ bool libc_already_loaded;
+
/* Original signal mask. Used for unblocking signal handlers before
running ELF constructors. */
sigset_t original_signal_mask;
@@ -511,6 +519,11 @@ dl_open_worker (void *a)
args->nsid = call_map->l_ns;
}
+ /* The namespace ID is now known. Keep track of whether libc.so was
+ already loaded, to determine whether it is necessary to call the
+ early initialization routine (or clear libc_map on error). */
+ args->libc_already_loaded = GL(dl_ns)[args->nsid].libc_map != NULL;
+
/* Retain the old value, so that it can be restored. */
args->original_global_scope_pending_adds
= GL (dl_ns)[args->nsid]._ns_global_scope_pending_adds;
@@ -745,6 +758,11 @@ dl_open_worker (void *a)
if (relocation_in_progress)
LIBC_PROBE (reloc_complete, 3, args->nsid, r, new);
+ /* If libc.so was not there before, attempt to call its early
+ initialization routine. */
+ if (!args->libc_already_loaded)
+ _dl_call_libc_early_init (GL(dl_ns)[args->nsid].libc_map);
+
#ifndef SHARED
DL_STATIC_INIT (new);
#endif
@@ -843,6 +861,7 @@ no more namespaces available for dlmopen()"));
args.caller_dlopen = caller_dlopen;
args.map = NULL;
args.nsid = nsid;
+ args.libc_already_loaded = true; /* No reset below with early failure. */
args.argc = argc;
args.argv = argv;
args.env = env;
@@ -875,6 +894,11 @@ no more namespaces available for dlmopen()"));
/* See if an error occurred during loading. */
if (__glibc_unlikely (exception.errstring != NULL))
{
+ /* Avoid keeping around a dangling reference to the libc.so link
+ map in case it has been cached in libc_map. */
+ if (!args.libc_already_loaded)
+ GL(dl_ns)[nsid].libc_map = NULL;
+
/* Remove the object from memory. It may be in an inconsistent
state if relocation failed, for example. */
if (args.map)