aboutsummaryrefslogtreecommitdiff
path: root/elf/dl-open.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/dl-open.c')
-rw-r--r--elf/dl-open.c68
1 files changed, 59 insertions, 9 deletions
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 94d29d181c..7f8fe80bd7 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -419,6 +419,35 @@ TLS generation counter wrapped! Please report this."));
}
}
+/* Mark the objects as NODELETE if required. This is delayed until
+ after dlopen failure is not possible, so that _dl_close can clean
+ up objects if necessary. */
+static void
+activate_nodelete (struct link_map *new, int mode)
+{
+ if (mode & RTLD_NODELETE || new->l_nodelete == link_map_nodelete_pending)
+ {
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
+ _dl_debug_printf ("activating NODELETE for %s [%lu]\n",
+ new->l_name, new->l_ns);
+ new->l_nodelete = link_map_nodelete_active;
+ }
+
+ /* Update all objects in our scope. Any of them could have been
+ promoted to NODELETE status due to new relocations. */
+ for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
+ {
+ struct link_map *imap = new->l_searchlist.r_list[i];
+ if (imap->l_nodelete == link_map_nodelete_pending)
+ {
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
+ _dl_debug_printf ("activating NODELETE for %s [%lu]\n",
+ imap->l_name, imap->l_ns);
+ imap->l_nodelete = link_map_nodelete_active;
+ }
+ }
+}
+
/* struct dl_init_args and call_dl_init are used to call _dl_init with
exception handling disabled. */
struct dl_init_args
@@ -488,12 +517,6 @@ dl_open_worker (void *a)
return;
}
- /* Mark the object as not deletable if the RTLD_NODELETE flags was passed.
- Do this early so that we don't skip marking the object if it was
- already loaded. */
- if (__glibc_unlikely (mode & RTLD_NODELETE))
- new->l_flags_1 |= DF_1_NODELETE;
-
if (__glibc_unlikely (mode & __RTLD_SPROF))
/* This happens only if we load a DSO for 'sprof'. */
return;
@@ -509,16 +532,33 @@ dl_open_worker (void *a)
_dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
new->l_name, new->l_ns, new->l_direct_opencount);
- /* If the user requested the object to be in the global namespace
- but it is not so far, add it now. */
+ /* If the user requested the object to be in the global
+ namespace but it is not so far, add it now. This can raise
+ an exception to do a malloc failure. */
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
add_to_global (new);
+ /* Mark the object as not deletable if the RTLD_NODELETE flags
+ was passed. */
+ if (__glibc_unlikely (mode & RTLD_NODELETE))
+ {
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)
+ && new->l_nodelete == link_map_nodelete_inactive)
+ _dl_debug_printf ("marking %s [%lu] as NODELETE\n",
+ new->l_name, new->l_ns);
+ new->l_nodelete = link_map_nodelete_active;
+ }
+
assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
return;
}
+ /* Schedule NODELETE marking for the directly loaded object if
+ requested. */
+ if (__glibc_unlikely (mode & RTLD_NODELETE))
+ new->l_nodelete = link_map_nodelete_pending;
+
/* Load that object's dependencies. */
_dl_map_object_deps (new, NULL, 0, 0,
mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
@@ -590,6 +630,14 @@ dl_open_worker (void *a)
int relocation_in_progress = 0;
+ /* Perform relocation. This can trigger lazy binding in IFUNC
+ resolvers. For NODELETE mappings, these dependencies are not
+ recorded because the flag has not been applied to the newly
+ loaded objects. This means that upon dlopen failure, these
+ NODELETE objects can be unloaded despite existing references to
+ them. However, such relocation dependencies in IFUNC resolvers
+ are undefined anyway, so this is not a problem. */
+
for (unsigned int i = nmaps; i-- > 0; )
{
l = maps[i];
@@ -619,7 +667,7 @@ dl_open_worker (void *a)
_dl_start_profile ();
/* Prevent unloading the object. */
- GL(dl_profile_map)->l_flags_1 |= DF_1_NODELETE;
+ GL(dl_profile_map)->l_nodelete = link_map_nodelete_active;
}
}
else
@@ -650,6 +698,8 @@ dl_open_worker (void *a)
All memory allocations for new objects must have happened
before. */
+ activate_nodelete (new, mode);
+
/* Second stage after resize_scopes: Actually perform the scope
update. After this, dlsym and lazy binding can bind to new
objects. */