about summary refs log tree commit diff
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.  */