summary refs log tree commit diff
path: root/elf/dl-sym.c
diff options
context:
space:
mode:
Diffstat (limited to 'elf/dl-sym.c')
-rw-r--r--elf/dl-sym.c85
1 files changed, 81 insertions, 4 deletions
diff --git a/elf/dl-sym.c b/elf/dl-sym.c
index d2b0ec0dab..1c66310d7c 100644
--- a/elf/dl-sym.c
+++ b/elf/dl-sym.c
@@ -17,6 +17,7 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
+#include <assert.h>
 #include <stddef.h>
 #include <setjmp.h>
 #include <libintl.h>
@@ -58,6 +59,30 @@ _dl_tls_symaddr (struct link_map *map, const ElfW(Sym) *ref)
 #endif
 
 
+struct call_dl_lookup_args
+{
+  /* Arguments to do_dlsym.  */
+  struct link_map *map;
+  const char *name;
+  struct r_scope_elem **scope;
+  struct r_found_version *vers;
+  int flags;
+
+  /* Return values of do_dlsym.  */
+  lookup_t loadbase;
+  const ElfW(Sym) **refp;
+};
+
+static void
+call_dl_lookup (void *ptr)
+{
+  struct call_dl_lookup_args *args = (struct call_dl_lookup_args *) ptr;
+  args->map = GLRO(dl_lookup_symbol_x) (args->name, args->map, args->refp,
+					args->scope, args->vers, 0,
+					args->flags, NULL);
+}
+
+
 static void *
 internal_function
 do_sym (void *handle, const char *name, void *who,
@@ -84,10 +109,62 @@ do_sym (void *handle, const char *name, void *who,
 	}
 
   if (handle == RTLD_DEFAULT)
-    /* Search the global scope.  */
-    result = GLRO(dl_lookup_symbol_x) (name, match, &ref, match->l_scope,
-				       vers, 0, flags|DL_LOOKUP_ADD_DEPENDENCY,
-				       NULL);
+    {
+      /* Search the global scope.  We have the simple case where
+	 we look up in the scope of an object which was part of
+	 the initial binary.  And then the more complex part
+	 where the object is dynamically loaded and the scope
+	 array can change.  */
+      if (match->l_type != lt_loaded)
+	result = GLRO(dl_lookup_symbol_x) (name, match, &ref,
+					   match->l_scoperec->scope, vers, 0,
+					   flags | DL_LOOKUP_ADD_DEPENDENCY,
+					   NULL);
+      else
+	{
+	  __rtld_mrlock_lock (match->l_scoperec_lock);
+	  struct r_scoperec *scoperec = match->l_scoperec;
+	  atomic_increment (&scoperec->nusers);
+	  __rtld_mrlock_unlock (match->l_scoperec_lock);
+
+	  struct call_dl_lookup_args args;
+	  args.name = name;
+	  args.map = match;
+	  args.scope = scoperec->scope;
+	  args.vers = vers;
+	  args.flags = flags | DL_LOOKUP_ADD_DEPENDENCY;
+	  args.refp = &ref;
+
+	  const char *objname;
+	  const char *errstring = NULL;
+	  bool malloced;
+	  int err = GLRO(dl_catch_error) (&objname, &errstring, &malloced,
+					  call_dl_lookup, &args);
+
+	  if (atomic_decrement_val (&scoperec->nusers) == 0
+	      && __builtin_expect (scoperec->remove_after_use, 0))
+	    {
+	      if (scoperec->notify)
+		__rtld_notify (scoperec->nusers);
+	      else
+		free (scoperec);
+	    }
+
+	  if (__builtin_expect (errstring != NULL, 0))
+	    {
+	      /* The lookup was unsuccessful.  Rethrow the error.  */
+	      char *errstring_dup = strdupa (errstring);
+	      char *objname_dup = strdupa (objname);
+	      if (malloced)
+		free ((char *) errstring);
+
+	      GLRO(dl_signal_error) (err, objname_dup, NULL, errstring_dup);
+	      /* NOTREACHED */
+	    }
+
+	  result = args.map;
+	}
+    }
   else if (handle == RTLD_NEXT)
     {
       if (__builtin_expect (match == GL(dl_ns)[LM_ID_BASE]._ns_loaded, 0))