[v3,28/32] elf: Add fast path to dlopen for fully-opened maps

Message ID a4400193c257759deb92bf9bf431b1fd71e6707f.1701944612.git.fweimer@redhat.com
State New
Headers
Series RELRO linkmaps |

Checks

Context Check Description
redhat-pt-bot/TryBot-still_applies warning Patch no longer applies to master

Commit Message

Florian Weimer Dec. 7, 2023, 10:32 a.m. UTC
  ---
 elf/dl-open.c | 36 +++++++++++++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 5 deletions(-)
  

Comments

Joseph Myers Feb. 28, 2024, 7:26 p.m. UTC | #1
On Thu, 7 Dec 2023, Florian Weimer wrote:

> +   l_direct_opencount update.  Needs to be kept in sync with the logic
> +   in dl_o-en_worker_begin after the l->l_searchlist.r_list != NULL check.

s/dl_o-en_worker_begin/dl_open_worker_begin/.  OK with that fix.
  

Patch

diff --git a/elf/dl-open.c b/elf/dl-open.c
index 52e44804cf..a8c9945f2b 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -533,6 +533,23 @@  call_dl_init (void *closure)
   _dl_init (args->new, args->argc, args->argv, args->env);
 }
 
+/* Return true if the object does not need any processing beyond the
+   l_direct_opencount update.  Needs to be kept in sync with the logic
+   in dl_o-en_worker_begin after the l->l_searchlist.r_list != NULL check.
+   MODE is the dlopen mode argument.  */
+static bool
+is_already_fully_open (struct link_map_private *map, int mode)
+{
+  return (map != NULL		/* An existing map was found.  */
+	  /* dlopen completed initialization of this map.  Maps with
+	     l_type == lt_library start out as partially initialized.  */
+	  && map->l_searchlist.r_list != NULL
+	  /* The object is already in the global scope if requested.  */
+	  && (!(mode & RTLD_GLOBAL) || map->l_global)
+	  /* The object is already NODELETE if requested.  */
+	  && (!(mode & RTLD_NODELETE) || map->l_rw->l_nodelete_active));
+}
+
 static void
 dl_open_worker_begin (void *a)
 {
@@ -559,9 +576,10 @@  dl_open_worker_begin (void *a)
   _dl_debug_initialize (0, args->nsid);
 
   /* Load the named object.  */
-  struct link_map_private *new;
-  args->map = new = _dl_map_object (args->caller_map, file, lt_loaded, 0,
-				    mode | __RTLD_CALLMAP, args->nsid);
+  struct link_map_private *new = args->map;
+  if (new == NULL)
+    args->map = new = _dl_map_new_object (args->caller_map, file, lt_loaded, 0,
+					  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.  */
@@ -578,7 +596,7 @@  dl_open_worker_begin (void *a)
   /* This object is directly loaded.  */
   ++new->l_rw->l_direct_opencount;
 
-  /* It was already open.  */
+  /* It was already open.  See is_already_fully_open above.  */
   if (__glibc_unlikely (new->l_searchlist.r_list != NULL))
     {
       /* Let the user know about the opencount.  */
@@ -881,7 +899,6 @@  no more namespaces available for dlmopen()"));
   struct dl_open_args args;
   args.file = file;
   args.mode = mode;
-  args.map = NULL;
   args.nsid = nsid;
   /* args.libc_already_loaded is always assigned by dl_open_worker
      (before any explicit/non-local returns).  */
@@ -906,6 +923,15 @@  no more namespaces available for dlmopen()"));
   else
     args.caller_map = NULL;
 
+  args.map = _dl_lookup_map (args.nsid, file);
+  if (is_already_fully_open (args.map, mode))
+    {
+      /* We can use the fast path.  */
+      ++args.map->l_rw->l_direct_opencount;
+      __rtld_lock_unlock_recursive (GL(dl_load_lock));
+      return args.map;
+    }
+
   struct dl_exception exception;
   int errcode = _dl_catch_exception (&exception, dl_open_worker, &args);