rtld: Implement ELF filters (DT_FILTER and DT_AUXILIARY)
authorJohn Marino <draco@marino.st>
Sat, 21 Jan 2012 17:31:30 +0000 (18:31 +0100)
committerJohn Marino <draco@marino.st>
Mon, 23 Jan 2012 16:13:17 +0000 (17:13 +0100)
Filtrees are loaded on demand unless LD_LOADFLTR environment variable is
set or -z loadfltr was specified during the linking.  This forces rtld
to upgrade read-locked rtld_bind_lock to write lock when it encounters an
object with filter during symbol lookup.

Consolidate common arguments of the symbol lookup functions in the SymLook
structure.

Taken-from: FreeBSD SVN 216728 (27 DEC 2010)

libexec/rtld-elf/i386/reloc.c
libexec/rtld-elf/rtld.1
libexec/rtld-elf/rtld.c
libexec/rtld-elf/rtld.h
libexec/rtld-elf/x86_64/reloc.c

index 85b3509..5b0c68a 100644 (file)
@@ -65,29 +65,34 @@ do_copy_relocations(Obj_Entry *dstobj)
 
     assert(dstobj->mainprog);  /* COPY relocations are invalid elsewhere */
 
-    rellim = (const Elf_Rel *) ((c_caddr_t) dstobj->rel + dstobj->relsize);
+    rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize);
     for (rel = dstobj->rel;  rel < rellim;  rel++) {
        if (ELF_R_TYPE(rel->r_info) == R_386_COPY) {
            void *dstaddr;
            const Elf_Sym *dstsym;
            const char *name;
-           unsigned long hash;
            size_t size;
            const void *srcaddr;
            const Elf_Sym *srcsym;
-           const Ver_Entry *ve;
-           Obj_Entry *srcobj;
+           const Obj_Entry *srcobj, *defobj;
+           SymLook req;
+           int res;
 
            dstaddr = (void *) (dstobj->relocbase + rel->r_offset);
            dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info);
            name = dstobj->strtab + dstsym->st_name;
-           hash = elf_hash(name);
            size = dstsym->st_size;
-           ve = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info));
-
-           for (srcobj = dstobj->next;  srcobj != NULL;  srcobj = srcobj->next)
-               if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL)
+           symlook_init(&req, name);
+           req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info));
+
+           for (srcobj = dstobj->next;  srcobj != NULL;  srcobj = srcobj->next) {
+               res = symlook_obj(&req, srcobj);
+               if (res == 0) {
+                   srcsym = req.sym_out;
+                   defobj = req.defobj_out;
                    break;
+               }
+           }
 
            if (srcobj == NULL) {
                _rtld_error("Undefined symbol \"%s\" referenced from COPY"
@@ -95,7 +100,7 @@ do_copy_relocations(Obj_Entry *dstobj)
                return -1;
            }
 
-           srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value);
+           srcaddr = (const void *) (defobj->relocbase + srcsym->st_value);
            memcpy(dstaddr, srcaddr, size);
        }
     }
@@ -115,7 +120,7 @@ init_pltgot(Obj_Entry *obj)
 
 /* Process the non-PLT relocations. */
 int
-reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, RtldLockState *lockstate)
 {
        const Elf_Rel *rellim;
        const Elf_Rel *rel;
@@ -132,7 +137,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
        } else
            cache = NULL;
 
-       rellim = (const Elf_Rel *) ((c_caddr_t) obj->rel + obj->relsize);
+       rellim = (const Elf_Rel *) ((caddr_t) obj->rel + obj->relsize);
        for (rel = obj->rel;  rel < rellim;  rel++) {
            Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rel->r_offset);
 
@@ -147,7 +152,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -166,7 +171,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -196,7 +201,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -214,7 +219,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -274,7 +279,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -288,7 +293,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -339,14 +344,14 @@ reloc_plt(Obj_Entry *obj)
 
 /* Relocate the jump slots in an object. */
 int
-reloc_jmpslots(Obj_Entry *obj)
+reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate)
 {
     const Elf_Rel *rellim;
     const Elf_Rel *rel;
 
     if (obj->jmpslots_done)
        return 0;
-    rellim = (const Elf_Rel *)((const char *)obj->pltrel + obj->pltrelsize);
+    rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize);
     for (rel = obj->pltrel;  rel < rellim;  rel++) {
        Elf_Addr *where, target;
        const Elf_Sym *def;
@@ -355,7 +360,8 @@ reloc_jmpslots(Obj_Entry *obj)
        switch (ELF_R_TYPE(rel->r_info)) {
        case R_386_JMP_SLOT:
          where = (Elf_Addr *)(obj->relocbase + rel->r_offset);
-         def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL);
+         def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL,
+             lockstate);
          if (def == NULL)
              return (-1);
          target = (Elf_Addr)(defobj->relocbase + def->st_value);
index fb035fd..edb79c5 100644 (file)
@@ -28,7 +28,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 21, 2011
+.Dd January 20, 2012
 .Dt RTLD 1
 .Os
 .Sh NAME
@@ -220,6 +220,14 @@ If set,
 .Nm
 will log events such as the loading and unloading of shared objects via
 .Xr utrace 2 .
+.Pp
+.It Ev LD_LOADFLTR
+If set,
+.Nm
+will process the filtee dependencies of the loaded objects immediately,
+instead of postponing it until required.
+Normally, the filtees are opened at the time of the first symbol resolution
+from the filter object.
 .El
 .Pp
 If a shared object preloaded by the
index ce457e3..a165c64 100644 (file)
@@ -73,16 +73,6 @@ typedef void (*func_ptr_type)();
 typedef void * (*path_enum_proc) (const char *path, size_t len, void *arg);
 
 /*
- * This structure provides a reentrant way to keep a list of objects and
- * check which ones have already been processed in some way.
- */
-typedef struct Struct_DoneList {
-    const Obj_Entry **objs;            /* Array of object pointers */
-    unsigned int num_alloc;            /* Allocated size of the array */
-    unsigned int num_used;             /* Number of array slots used */
-} DoneList;
-
-/*
  * Function declarations.
  */
 static const char *_getenv_ld(const char *id);
@@ -93,6 +83,8 @@ static void digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *);
 static void digest_dynamic(Obj_Entry *, int);
 static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
 static Obj_Entry *dlcheck(void *);
+static Obj_Entry *dlopen_object(const char *name, Obj_Entry *refobj,
+    int lo_flags, int mode);
 static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int);
 static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *);
 static bool donelist_check(DoneList *, const Obj_Entry *);
@@ -109,6 +101,8 @@ static void initlist_add_objects(Obj_Entry *, Obj_Entry **, Objlist *);
 static bool is_exported(const Elf_Sym *);
 static void linkmap_add(Obj_Entry *);
 static void linkmap_delete(Obj_Entry *);
+static void load_filtees(Obj_Entry *, int flags, RtldLockState *);
+static void unload_filtees(Obj_Entry *);
 static int load_needed_objects(Obj_Entry *, int);
 static int load_preload_objects(void);
 static Obj_Entry *load_object(const char *, const Obj_Entry *, int);
@@ -122,20 +116,18 @@ static void objlist_push_head(Objlist *, Obj_Entry *);
 static void objlist_push_tail(Objlist *, Obj_Entry *);
 static void objlist_remove(Objlist *, Obj_Entry *);
 static void *path_enumerate(const char *, path_enum_proc, void *);
-static int relocate_objects(Obj_Entry *, bool, Obj_Entry *);
+static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, RtldLockState *);
 static int rtld_dirname(const char *, char *);
 static int rtld_dirname_abs(const char *, char *);
 static void rtld_exit(void);
 static char *search_library_path(const char *, const char *);
 static const void **get_program_var_addr(const char *);
 static void set_program_var(const char *, const void *);
-static const Elf_Sym *symlook_default(const char *, unsigned long,
-  const Obj_Entry *, const Obj_Entry **, const Ver_Entry *, int);
-static const Elf_Sym *symlook_list(const char *, unsigned long, const Objlist *,
-  const Obj_Entry **, const Ver_Entry *, int, DoneList *);
-static const Elf_Sym *symlook_needed(const char *, unsigned long,
-  const Needed_Entry *, const Obj_Entry **, const Ver_Entry *,
-  int, DoneList *);
+static int symlook_default(SymLook *, const Obj_Entry *refobj);
+static void symlook_init_from_req(SymLook *, const SymLook *);
+static int symlook_list(SymLook *, const Objlist *, DoneList *);
+static int symlook_needed(SymLook *, const Needed_Entry *, DoneList *);
+static int symlook_obj1(SymLook *, const Obj_Entry *);
 static void trace_loaded_objects(Obj_Entry *);
 static void unlink_object(Obj_Entry *);
 static void unload_object(Obj_Entry *);
@@ -160,6 +152,7 @@ void r_debug_state(struct r_debug *, struct link_map *) __noinline;
 static char *error_message;    /* Message for dlerror(), or NULL */
 struct r_debug r_debug;                /* for GDB; */
 static bool libmap_disable;    /* Disable libmap */
+static bool ld_loadfltr;       /* Immediate filters processing */
 static char *libmap_override;  /* Maps to use in addition to libmap.conf */
 static bool trust;             /* False for setuid and setgid programs */
 static bool dangerous_ld_env;  /* True if environment variables have been
@@ -177,7 +170,7 @@ static int (*rtld_functrace)(   /* Optional function call tracing hook */
        const char *callee_obj,
        const char *callee_func,
        void *stack);
-static Obj_Entry *rtld_functrace_obj;  /* Object thereof */
+static const Obj_Entry *rtld_functrace_obj;    /* Object thereof */
 static Obj_Entry *obj_list;    /* Head of linked list of shared objects */
 static Obj_Entry **obj_tail;   /* Link field of last object in list */
 static Obj_Entry **preload_tail;
@@ -348,7 +341,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
     /* marino: DO NOT MOVE THESE VARIABLES TO _rtld
              Obj_Entry **preload_tail;
              Objlist initlist;
-       from global to here.  It will break the DRAWF2 unwind scheme.
+       from global to here.  It will break the DWARF2 unwind scheme.
        The system compilers were unaffected, but not gcc 4.6
     */
 
@@ -413,20 +406,23 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
            || unsetenv("LD_ELF_HINTS_PATH")
            || unsetenv("LD_LIBMAP")
            || unsetenv("LD_LIBMAP_DISABLE")
+           || unsetenv("LD_LOADFLTR")
        ) {
            _rtld_error("environment corrupt; aborting");
            die();
        }
     }
     ld_debug = _getenv_ld("LD_DEBUG");
+    libmap_disable = _getenv_ld("LD_LIBMAP_DISABLE") != NULL;
+    libmap_override = (char *)_getenv_ld("LD_LIBMAP");
     ld_library_path = _getenv_ld("LD_LIBRARY_PATH");
     ld_preload = (char *)_getenv_ld("LD_PRELOAD");
     ld_elf_hints_path = _getenv_ld("LD_ELF_HINTS_PATH");
-    libmap_override = (char *)_getenv_ld("LD_LIBMAP");
-    libmap_disable = _getenv_ld("LD_LIBMAP_DISABLE") != NULL;
+    ld_loadfltr = _getenv_ld("LD_LOADFLTR") != NULL;
     dangerous_ld_env = (ld_library_path != NULL)
                        || (ld_preload != NULL)
                        || (ld_elf_hints_path != NULL)
+                       || ld_loadfltr
                        || (libmap_override != NULL)
                        || libmap_disable
                        ;
@@ -606,7 +602,7 @@ resident_skip1:
      */
 
     if (relocate_objects(obj_main,
-       ld_bind_now != NULL && *ld_bind_now != '\0', &obj_rtld) == -1)
+      ld_bind_now != NULL && *ld_bind_now != '\0', &obj_rtld, NULL) == -1)
        die();
 
     dbg("doing copy relocations");
@@ -672,10 +668,16 @@ void
 _rtld_call_init(void)
 {
     RtldLockState lockstate;
+    Obj_Entry *obj;
 
     wlock_acquire(rtld_bind_lock, &lockstate);
     objlist_call_init(&initlist, &lockstate);
     objlist_clear(&initlist);
+    dbg("loading filtees");
+    for (obj = obj_list->next; obj != NULL; obj = obj->next) {
+       if (ld_loadfltr || obj->z_loadfltr)
+           load_filtees(obj, 0, &lockstate);
+    }
     lock_release(rtld_bind_lock, &lockstate);
 }
 
@@ -688,7 +690,6 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff, void *stack)
     Elf_Addr *where;
     Elf_Addr target;
     RtldLockState lockstate;
-    int do_reloc = 1;
 
     rlock_acquire(rtld_bind_lock, &lockstate);
     if (sigsetjmp(lockstate.env, 0) != 0)
@@ -699,7 +700,8 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff, void *stack)
        rel = (const Elf_Rel *) ((caddr_t) obj->pltrela + reloff);
 
     where = (Elf_Addr *) (obj->relocbase + rel->r_offset);
-    def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL);
+    def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, true, NULL,
+       &lockstate);
     if (def == NULL)
        die();
 
@@ -723,11 +725,18 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff, void *stack)
                           defobj->path,
                           defobj->strtab + def->st_name,
                           stack))
-           do_reloc = 0;
+       lock_release(rtld_bind_lock, &lockstate);
+       return target;
     }
 
-    if (do_reloc)
-       target = reloc_jmpslot(where, target, defobj, obj, rel);
+    /*
+     * Write the new contents for the jmpslot. Note that depending on
+     * architecture, the value which we need to return back to the
+     * lazy binding trampoline may or may not be the target
+     * address. The value returned from reloc_jmpslot() is the value
+     * that the trampoline needs.
+     */
+    target = reloc_jmpslot(where, target, defobj, obj, rel);
     lock_release(rtld_bind_lock, &lockstate);
     return target;
 }
@@ -878,6 +887,8 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
 {
     const Elf_Dyn *dynp;
     Needed_Entry **needed_tail = &obj->needed;
+    Needed_Entry **needed_filtees_tail = &obj->needed_filtees;
+    Needed_Entry **needed_aux_filtees_tail = &obj->needed_aux_filtees;
     int plttype = DT_REL;
 
     *dyn_rpath = NULL;
@@ -988,6 +999,30 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
            }
            break;
 
+       case DT_FILTER:
+           if (!obj->rtld) {
+               Needed_Entry *nep = NEW(Needed_Entry);
+               nep->name = dynp->d_un.d_val;
+               nep->obj = NULL;
+               nep->next = NULL;
+
+               *needed_filtees_tail = nep;
+               needed_filtees_tail = &nep->next;
+           }
+           break;
+
+       case DT_AUXILIARY:
+           if (!obj->rtld) {
+               Needed_Entry *nep = NEW(Needed_Entry);
+               nep->name = dynp->d_un.d_val;
+               nep->obj = NULL;
+               nep->next = NULL;
+
+               *needed_aux_filtees_tail = nep;
+               needed_aux_filtees_tail = &nep->next;
+           }
+           break;
+
        case DT_PLTGOT:
            obj->pltgot = (Elf_Addr *) (obj->relocbase + dynp->d_un.d_ptr);
            break;
@@ -1052,6 +1087,8 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath,
                    obj->bind_now = true;
                if (dynp->d_un.d_val & DF_1_NODELETE)
                    obj->z_nodelete = true;
+               if (dynp->d_un.d_val & DF_1_LOADFLTR)
+                   obj->z_loadfltr = true;
            break;
 
        default:
@@ -1297,14 +1334,15 @@ find_library(const char *xname, const Obj_Entry *refobj)
  */
 const Elf_Sym *
 find_symdef(unsigned long symnum, const Obj_Entry *refobj,
-    const Obj_Entry **defobj_out, int flags, SymCache *cache)
+    const Obj_Entry **defobj_out, int flags, SymCache *cache,
+    RtldLockState *lockstate)
 {
     const Elf_Sym *ref;
     const Elf_Sym *def;
     const Obj_Entry *defobj;
-    const Ver_Entry *ventry;
+    SymLook req;
     const char *name;
-    unsigned long hash;
+    int res;
 
     /*
      * If we have already found this symbol, get the information from
@@ -1319,6 +1357,7 @@ find_symdef(unsigned long symnum, const Obj_Entry *refobj,
 
     ref = refobj->symtab + symnum;
     name = refobj->strtab + ref->st_name;
+    def = NULL;
     defobj = NULL;
 
     /*
@@ -1336,9 +1375,15 @@ find_symdef(unsigned long symnum, const Obj_Entry *refobj,
            _rtld_error("%s: Bogus symbol table entry %lu", refobj->path,
                symnum);
        }
-       ventry = fetch_ventry(refobj, symnum);
-       hash = elf_hash(name);
-       def = symlook_default(name, hash, refobj, &defobj, ventry, flags);
+       symlook_init(&req, name);
+       req.flags = flags;
+       req.ventry = fetch_ventry(refobj, symnum);
+       req.lockstate = lockstate;
+       res = symlook_default(&req, refobj);
+       if (res == 0) {
+           def = req.sym_out;
+           defobj = req.defobj_out;
+       }
     } else {
        def = ref;
        defobj = refobj;
@@ -1415,6 +1460,7 @@ init_dag(Obj_Entry *root)
        return;
     donelist_init(&donelist);
     init_dag1(root, root, &donelist);
+    root->dag_inited = true;
 }
 
 static void
@@ -1430,7 +1476,6 @@ init_dag1(Obj_Entry *root, Obj_Entry *obj, DoneList *dlp)
     for (needed = obj->needed;  needed != NULL;  needed = needed->next)
        if (needed->obj != NULL)
            init_dag1(root, needed->obj, dlp);
-    root->dag_inited = true;
 }
 
 /*
@@ -1469,7 +1514,7 @@ init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info)
         * that symbols can be found.
         */
 
-       relocate_objects(&objtmp, true, &objtmp);
+       relocate_objects(&objtmp, true, &objtmp, NULL);
     }
 
     /* Initialize the object list. */
@@ -1562,6 +1607,77 @@ is_exported(const Elf_Sym *def)
     return false;
 }
 
+static void
+free_needed_filtees(Needed_Entry *n)
+{
+    Needed_Entry *needed, *needed1;
+
+    for (needed = n; needed != NULL; needed = needed->next) {
+       if (needed->obj != NULL) {
+           dlclose(needed->obj);
+           needed->obj = NULL;
+       }
+    }
+    for (needed = n; needed != NULL; needed = needed1) {
+       needed1 = needed->next;
+       free(needed);
+    }
+}
+
+static void
+unload_filtees(Obj_Entry *obj)
+{
+
+    free_needed_filtees(obj->needed_filtees);
+    obj->needed_filtees = NULL;
+    free_needed_filtees(obj->needed_aux_filtees);
+    obj->needed_aux_filtees = NULL;
+    obj->filtees_loaded = false;
+}
+
+static void
+load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags)
+{
+
+    for (; needed != NULL; needed = needed->next) {
+       needed->obj = dlopen_object(obj->strtab + needed->name, obj,
+         flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW : RTLD_LAZY) |
+         RTLD_LOCAL);
+    }
+}
+
+static void
+load_filtees(Obj_Entry *obj, int flags, RtldLockState *lockstate)
+{
+
+    lock_restart_for_upgrade(lockstate);
+    if (!obj->filtees_loaded) {
+       load_filtee1(obj, obj->needed_filtees, flags);
+       load_filtee1(obj, obj->needed_aux_filtees, flags);
+       obj->filtees_loaded = true;
+    }
+}
+
+static int
+process_needed(Obj_Entry *obj, Needed_Entry *needed, int flags)
+{
+    Obj_Entry *obj1;
+
+    for (; needed != NULL; needed = needed->next) {
+       obj1 = needed->obj = load_object(obj->strtab + needed->name, obj,
+         flags & ~RTLD_LO_NOLOAD);
+       if (obj1 == NULL && !ld_tracing && (flags & RTLD_LO_FILTEES) == 0)
+           return (-1);
+       if (obj1 != NULL && obj1->z_nodelete && !obj1->ref_nodel) {
+           dbg("obj %s nodelete", obj1->path);
+           init_dag(obj1);
+           ref_dag(obj1);
+           obj1->ref_nodel = true;
+       }
+    }
+    return (0);
+}
+
 /*
  * Given a shared object, traverse its list of needed objects, and load
  * each of them.  Returns 0 on success.  Generates an error message and
@@ -1570,29 +1686,15 @@ is_exported(const Elf_Sym *def)
 static int
 load_needed_objects(Obj_Entry *first, int flags)
 {
-    Obj_Entry *obj, *obj1;
+    Obj_Entry *obj;
 
     for (obj = first;  obj != NULL;  obj = obj->next) {
-       Needed_Entry *needed;
-
-       for (needed = obj->needed;  needed != NULL;  needed = needed->next) {
-           obj1 = needed->obj = load_object(obj->strtab + needed->name, obj,
-               flags & ~RTLD_LO_NOLOAD);
-           if (obj1 == NULL && !ld_tracing)
-               return -1;
-           if (obj1 != NULL && obj1->z_nodelete && !obj1->ref_nodel) {
-               dbg("obj %s nodelete", obj1->path);
-               init_dag(obj1);
-               ref_dag(obj1);
-               obj1->ref_nodel = true;
-           }
-       }
+       if (process_needed(obj, obj->needed, flags) == -1)
+           return (-1);
     }
     return (0);
 }
 
-#define RTLD_FUNCTRACE "_rtld_functrace"
-
 static int
 load_preload_objects(void)
 {
@@ -1607,7 +1709,8 @@ load_preload_objects(void)
        size_t len = strcspn(p, delim);
        char savech;
        Obj_Entry *obj;
-       const Elf_Sym *sym;
+       SymLook req;
+       int res;
 
        savech = p[len];
        p[len] = '\0';
@@ -1619,10 +1722,12 @@ load_preload_objects(void)
        p += strspn(p, delim);
 
        /* Check for the magic tracing function */
-       sym = symlook_obj(RTLD_FUNCTRACE, elf_hash(RTLD_FUNCTRACE), obj, NULL, 1);
-       if (sym != NULL) {
-               rtld_functrace = (void *)(obj->relocbase + sym->st_value);
-               rtld_functrace_obj = obj;
+       symlook_init(&req, RTLD_FUNCTRACE);
+       res = symlook_obj(&req, obj);
+       if (res == 0) {
+           rtld_functrace = (void *)(req.defobj_out->relocbase +
+                                     req.sym_out->st_value);
+           rtld_functrace_obj = req.defobj_out;
        }
     }
     LD_UTRACE(UTRACE_PRELOAD_FINISHED, NULL, NULL, 0, 0, NULL);
@@ -1929,7 +2034,8 @@ objlist_remove(Objlist *list, Obj_Entry *obj)
  * or -1 on failure.
  */
 static int
-relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj)
+relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj,
+    RtldLockState *lockstate)
 {
     Obj_Entry *obj;
 
@@ -1954,7 +2060,7 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj)
        }
 
        /* Process the non-PLT relocations. */
-       if (reloc_non_plt(obj, rtldobj))
+       if (reloc_non_plt(obj, rtldobj, lockstate))
                return -1;
 
        /*
@@ -1985,7 +2091,7 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj)
            return -1;
        /* Relocate the jump slots if we are doing immediate binding. */
        if (obj->bind_now || bind_now)
-           if (reloc_jmpslots(obj) == -1)
+           if (reloc_jmpslots(obj, lockstate) == -1)
                return -1;
 
        /* Set the special PLT or GOT entries. */
@@ -2162,11 +2268,7 @@ dlerror(void)
 void *
 dlopen(const char *name, int mode)
 {
-    Obj_Entry **old_obj_tail;
-    Obj_Entry *obj;
-    Objlist initlist;
-    RtldLockState lockstate;
-    int result, lo_flags;
+    int lo_flags;
 
     LD_UTRACE(UTRACE_DLOPEN_START, NULL, NULL, 0, mode, name);
     ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1";
@@ -2180,6 +2282,19 @@ dlopen(const char *name, int mode)
     if (ld_tracing != NULL)
            lo_flags |= RTLD_LO_TRACE;
 
+    return (dlopen_object(name, obj_main, lo_flags,
+      mode & (RTLD_MODEMASK | RTLD_GLOBAL)));
+}
+
+static Obj_Entry *
+dlopen_object(const char *name, Obj_Entry *refobj, int lo_flags, int mode)
+{
+    Obj_Entry **old_obj_tail;
+    Obj_Entry *obj;
+    Objlist initlist;
+    RtldLockState lockstate;
+    int result;
+
     objlist_init(&initlist);
 
     wlock_acquire(rtld_bind_lock, &lockstate);
@@ -2191,25 +2306,24 @@ dlopen(const char *name, int mode)
        obj = obj_main;
        obj->refcount++;
     } else {
-       obj = load_object(name, obj_main, lo_flags);
+       obj = load_object(name, refobj, lo_flags);
     }
 
     if (obj) {
        obj->dl_refcount++;
        if (mode & RTLD_GLOBAL && objlist_find(&list_global, obj) == NULL)
            objlist_push_tail(&list_global, obj);
-       mode &= RTLD_MODEMASK;
        if (*old_obj_tail != NULL) {            /* We loaded something new. */
            assert(*old_obj_tail == obj);
-           result = load_needed_objects(obj, RTLD_LO_DLOPEN);
+           result = load_needed_objects(obj, lo_flags & RTLD_LO_DLOPEN);
            init_dag(obj);
            ref_dag(obj);
            if (result != -1)
                result = rtld_verify_versions(&obj->dagmembers);
            if (result != -1 && ld_tracing)
                goto trace;
-           if (result == -1 ||
-             (relocate_objects(obj, mode == RTLD_NOW, &obj_rtld)) == -1) {
+           if (result == -1 || (relocate_objects(obj, (mode & RTLD_MODEMASK)
+             == RTLD_NOW, &obj_rtld, &lockstate)) == -1) {
                obj->dl_refcount--;
                unref_dag(obj);
                if (obj->refcount == 0)
@@ -2262,14 +2376,17 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
 {
     DoneList donelist;
     const Obj_Entry *obj, *defobj;
-    const Elf_Sym *def, *symp;
-    unsigned long hash;
+    const Elf_Sym *def;
+    SymLook req;
     RtldLockState lockstate;
+    int res;
 
-    hash = elf_hash(name);
     def = NULL;
     defobj = NULL;
-    flags |= SYMLOOK_IN_PLT;
+    symlook_init(&req, name);
+    req.ventry = ve;
+    req.flags = flags | SYMLOOK_IN_PLT;
+    req.lockstate = &lockstate;
 
     rlock_acquire(rtld_bind_lock, &lockstate);
     if (sigsetjmp(lockstate.env, 0) != 0)
@@ -2283,17 +2400,22 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
            return NULL;
        }
        if (handle == NULL) {   /* Just the caller's shared object. */
-           def = symlook_obj(name, hash, obj, ve, flags);
-           defobj = obj;
+           res = symlook_obj(&req, obj);
+           if (res == 0) {
+               def = req.sym_out;
+               defobj = req.defobj_out;
+           }
        } else if (handle == RTLD_NEXT || /* Objects after caller's */
                   handle == RTLD_SELF) { /* ... caller included */
            if (handle == RTLD_NEXT)
                obj = obj->next;
            for (; obj != NULL; obj = obj->next) {
-               if ((symp = symlook_obj(name, hash, obj, ve, flags)) != NULL) {
-                   if (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK) {
-                       def = symp;
-                       defobj = obj;
+               res = symlook_obj(&req, obj);
+               if (res == 0) {
+                   if (def == NULL ||
+                     ELF_ST_BIND(req.sym_out->st_info) != STB_WEAK) {
+                       def = req.sym_out;
+                       defobj = req.defobj_out;
                        if (ELF_ST_BIND(def->st_info) != STB_WEAK)
                            break;
                    }
@@ -2305,15 +2427,19 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
             * dynamic linker services such as dlopen.
             */
            if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
-               symp = symlook_obj(name, hash, &obj_rtld, ve, flags);
-               if (symp != NULL) {
-                   def = symp;
-                   defobj = &obj_rtld;
+               res = symlook_obj(&req, &obj_rtld);
+               if (res == 0 && is_exported(req.sym_out)) {
+                   def = req.sym_out;
+                   defobj = req.defobj_out;
                }
            }
        } else {
            assert(handle == RTLD_DEFAULT);
-           def = symlook_default(name, hash, obj, &defobj, ve, flags);
+           res = symlook_default(&req, obj);
+           if (res == 0) {
+               defobj = req.defobj_out;
+               def = req.sym_out;
+           }
        }
     } else {
        if ((obj = dlcheck(handle)) == NULL) {
@@ -2324,18 +2450,23 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
        donelist_init(&donelist);
        if (obj->mainprog) {
            /* Search main program and all libraries loaded by it. */
-           def = symlook_list(name, hash, &list_main, &defobj, ve, flags,
-                              &donelist);
-
+           res = symlook_list(&req, &list_main, &donelist);
+           if (res == 0) {
+               def = req.sym_out;
+               defobj = req.defobj_out;
+           } else {
            /*
-            * We do not distinguish between 'main' object and global scope.
-            * If symbol is not defined by objects loaded at startup, continue
-            * search among dynamically loaded objects with RTLD_GLOBAL
-            * scope.
+                * We do not distinguish between 'main' object and
+                * global scope.  If symbol is not defined by objects
+                * loaded at startup, continue search among
+                * dynamically loaded objects with RTLD_GLOBAL scope.
             */
-           if (def == NULL)
-               def = symlook_list(name, hash, &list_global, &defobj, ve,
-                                  flags, &donelist);
+               res = symlook_list(&req, &list_global, &donelist);
+               if (res == 0) {
+                   def = req.sym_out;
+                   defobj = req.defobj_out;
+               }
+           }
        } else {
            Needed_Entry fake;
 
@@ -2343,8 +2474,11 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve,
            fake.next = NULL;
            fake.obj = (Obj_Entry *)obj;
            fake.name = 0;
-           def = symlook_needed(name, hash, &fake, &defobj, ve, flags,
-                                &donelist);
+           res = symlook_needed(&req, &fake, &donelist);
+           if (res == 0) {
+               def = req.sym_out;
+               defobj = req.defobj_out;
+           }
        }
     }
 
@@ -2795,17 +2929,13 @@ static const void **
 get_program_var_addr(const char *name)
 {
     const Obj_Entry *obj;
-    unsigned long hash;
+    SymLook req;
 
-    hash = elf_hash(name);
+    symlook_init(&req, name);
     for (obj = obj_main;  obj != NULL;  obj = obj->next) {
-       const Elf_Sym *def;
-
-       if ((def = symlook_obj(name, hash, obj, NULL, 0)) != NULL) {
-           const void **addr;
-
-           addr = (const void **)(obj->relocbase + def->st_value);
-           return addr;
+       if (symlook_obj(&req, obj) == 0) {
+           return ((const void **)(req.defobj_out->relocbase +
+             req.sym_out->st_value));
        }
     }
     return (NULL);
@@ -2863,37 +2993,38 @@ _getenv_ld(const char *id)
  * no definition was found.  Returns a pointer to the Obj_Entry of the
  * defining object via the reference parameter DEFOBJ_OUT.
  */
-static const Elf_Sym *
-symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj,
-    const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags)
+static int
+symlook_default(SymLook *req, const Obj_Entry *refobj)
 {
     DoneList donelist;
     const Elf_Sym *def;
-    const Elf_Sym *symp;
-    const Obj_Entry *obj;
     const Obj_Entry *defobj;
     const Objlist_Entry *elm;
+    SymLook req1;
+    int res;
     def = NULL;
     defobj = NULL;
     donelist_init(&donelist);
+    symlook_init_from_req(&req1, req);
 
     /* Look first in the referencing object if linked symbolically. */
     if (refobj->symbolic && !donelist_check(&donelist, refobj)) {
-       symp = symlook_obj(name, hash, refobj, ventry, flags);
-       if (symp != NULL) {
-           def = symp;
-           defobj = refobj;
+       res = symlook_obj(&req1, refobj);
+       if (res == 0) {
+           def = req1.sym_out;
+           defobj = req1.defobj_out;
+           assert(defobj != NULL);
        }
     }
 
     /* Search all objects loaded at program start up. */
     if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
-       symp = symlook_list(name, hash, &list_main, &obj, ventry, flags,
-           &donelist);
-       if (symp != NULL &&
-         (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
-           def = symp;
-           defobj = obj;
+       res = symlook_list(&req1, &list_main, &donelist);
+       if (res == 0 &&
+         (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+           def = req1.sym_out;
+           defobj = req1.defobj_out;
+           assert(defobj != NULL);
        }
     }
 
@@ -2901,12 +3032,12 @@ symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj,
     STAILQ_FOREACH(elm, &list_global, link) {
        if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK)
            break;
-       symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, ventry,
-          flags, &donelist);
-       if (symp != NULL &&
-         (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
-           def = symp;
-           defobj = obj;
+       res = symlook_list(&req1, &elm->obj->dagmembers, &donelist);
+       if (res == 0 &&
+         (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+           def = req1.sym_out;
+           defobj = req1.defobj_out;
+           assert(defobj != NULL);
        }
     }
 
@@ -2914,12 +3045,12 @@ symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj,
     STAILQ_FOREACH(elm, &refobj->dldags, link) {
        if (def != NULL && ELF_ST_BIND(def->st_info) != STB_WEAK)
            break;
-       symp = symlook_list(name, hash, &elm->obj->dagmembers, &obj, ventry,
-           flags, &donelist);
-       if (symp != NULL &&
-         (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK)) {
-           def = symp;
-           defobj = obj;
+       res = symlook_list(&req1, &elm->obj->dagmembers, &donelist);
+       if (res == 0 &&
+         (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) {
+           def = req1.sym_out;
+           defobj = req1.defobj_out;
+           assert(defobj != NULL);
        }
     }
 
@@ -2930,45 +3061,53 @@ symlook_default(const char *name, unsigned long hash, const Obj_Entry *refobj,
      * in the "exports" array can be resolved from the dynamic linker.
      */
     if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) {
-       symp = symlook_obj(name, hash, &obj_rtld, ventry, flags);
-       if (symp != NULL && is_exported(symp)) {
-           def = symp;
-           defobj = &obj_rtld;
+       res = symlook_obj(&req1, &obj_rtld);
+       if (res == 0 && is_exported(req1.sym_out)) {
+           def = req1.sym_out;
+           defobj = req1.defobj_out;
+           assert(defobj != NULL);
        }
     }
 
-    if (def != NULL)
-       *defobj_out = defobj;
-    return def;
+    if (def != NULL) {
+       assert(defobj != NULL);
+       req->defobj_out = defobj;
+       req->sym_out = def;
+       return (0);
+    }
+    return (ESRCH);
 }
 
-static const Elf_Sym *
-symlook_list(const char *name, unsigned long hash, const Objlist *objlist,
-  const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags,
-  DoneList *dlp)
+static int
+symlook_list(SymLook *req, const Objlist *objlist, DoneList *dlp)
 {
-    const Elf_Sym *symp;
     const Elf_Sym *def;
     const Obj_Entry *defobj;
     const Objlist_Entry *elm;
+    SymLook req1;
+    int res;
 
     def = NULL;
     defobj = NULL;
     STAILQ_FOREACH(elm, objlist, link) {
        if (donelist_check(dlp, elm->obj))
            continue;
-       if ((symp = symlook_obj(name, hash, elm->obj, ventry, flags)) != NULL) {
-           if (def == NULL || ELF_ST_BIND(symp->st_info) != STB_WEAK) {
-               def = symp;
-               defobj = elm->obj;
+       symlook_init_from_req(&req1, req);
+       if ((res = symlook_obj(&req1, elm->obj)) == 0) {
+           if (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK) {
+               def = req1.sym_out;
+               defobj = req1.defobj_out;
                if (ELF_ST_BIND(def->st_info) != STB_WEAK)
                    break;
            }
        }
     }
-    if (def != NULL)
-       *defobj_out = defobj;
-    return def;
+    if (def != NULL) {
+       req->sym_out = def;
+       req->defobj_out = defobj;
+       return (0);
+    }
+    return (ESRCH);
 }
 
 /*
@@ -2977,26 +3116,28 @@ symlook_list(const char *name, unsigned long hash, const Objlist *objlist,
  * breadth-first.  Returns a pointer to the symbol, or NULL if no
  * definition was found.
  */
-static const Elf_Sym *
-symlook_needed(const char *name, unsigned long hash, const Needed_Entry *needed,
-  const Obj_Entry **defobj_out, const Ver_Entry *ventry, int flags,
-  DoneList *dlp)
+static int
+symlook_needed(SymLook *req, const Needed_Entry *needed, DoneList *dlp)
 {
     const Elf_Sym *def, *def_w;
     const Needed_Entry *n;
-    const Obj_Entry *obj, *defobj, *defobj1;
+    const Obj_Entry *defobj, *defobj1;
+    SymLook req1;
+    int res;
 
     def = def_w = NULL;
     defobj = NULL;
+    symlook_init_from_req(&req1, req);
     for (n = needed; n != NULL; n = n->next) {
-       if ((obj = n->obj) == NULL ||
-           donelist_check(dlp, obj) ||
-           (def = symlook_obj(name, hash, obj, ventry, flags)) == NULL)
+       if (n->obj == NULL || donelist_check(dlp, n->obj) ||
+           (res = symlook_obj(&req1, n->obj)) != 0)
            continue;
-       defobj = obj;
+       def = req1.sym_out;
+       defobj = req1.defobj_out;
        if (ELF_ST_BIND(def->st_info) != STB_WEAK) {
-           *defobj_out = defobj;
-           return (def);
+           req->defobj_out = defobj;
+           req->sym_out = def;
+           return (0);
        }
     }
     /*
@@ -3004,12 +3145,13 @@ symlook_needed(const char *name, unsigned long hash, const Needed_Entry *needed,
      * directly needed objects, or found symbol is weak.
      */
     for (n = needed; n != NULL; n = n->next) {
-       if ((obj = n->obj) == NULL)
+       if (n->obj == NULL)
            continue;
-       def_w = symlook_needed(name, hash, obj->needed, &defobj1,
-                              ventry, flags, dlp);
-       if (def_w == NULL)
+       res = symlook_needed(&req1, n->obj->needed, dlp);
+       if (res != 0)
            continue;
+       def_w = req1.sym_out;
+       defobj1 = req1.defobj_out;
        if (def == NULL || ELF_ST_BIND(def_w->st_info) != STB_WEAK) {
            def = def_w;
            defobj = defobj1;
@@ -3017,22 +3159,60 @@ symlook_needed(const char *name, unsigned long hash, const Needed_Entry *needed,
        if (ELF_ST_BIND(def_w->st_info) != STB_WEAK)
            break;
     }
-    if (def != NULL)
-       *defobj_out = defobj;
-    return (def);
+    if (def != NULL) {
+       req->sym_out = def;
+       req->defobj_out = defobj;
+       return (0);
+    }
+    return (ESRCH);
 }
 
 /*
  * Search the symbol table of a single shared object for a symbol of
  * the given name and version, if requested.  Returns a pointer to the
- * symbol, or NULL if no definition was found.
+ * symbol, or NULL if no definition was found.  If the object is
+ * filter, return filtered symbol from filtee.
  *
  * The symbol's hash value is passed in for efficiency reasons; that
  * eliminates many recomputations of the hash value.
  */
-const Elf_Sym *
-symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
-    const Ver_Entry *ventry, int flags)
+int
+symlook_obj(SymLook *req, const Obj_Entry *obj)
+{
+    DoneList donelist;
+    SymLook req1;
+    int res, mres;
+
+    mres = symlook_obj1(req, obj);
+    if (mres == 0) {
+       if (obj->needed_filtees != NULL) {
+           load_filtees(__DECONST(Obj_Entry *, obj), 0, req->lockstate);
+           donelist_init(&donelist);
+           symlook_init_from_req(&req1, req);
+           res = symlook_needed(&req1, obj->needed_filtees, &donelist);
+           if (res == 0) {
+               req->sym_out = req1.sym_out;
+               req->defobj_out = req1.defobj_out;
+           }
+           return (res);
+       }
+       if (obj->needed_aux_filtees != NULL) {
+           load_filtees(__DECONST(Obj_Entry *, obj), 0, req->lockstate);
+           donelist_init(&donelist);
+           symlook_init_from_req(&req1, req);
+           res = symlook_needed(&req1, obj->needed_aux_filtees, &donelist);
+           if (res == 0) {
+               req->sym_out = req1.sym_out;
+               req->defobj_out = req1.defobj_out;
+               return (res);
+           }
+       }
+    }
+    return (mres);
+}
+
+static int
+symlook_obj1(SymLook *req, const Obj_Entry *obj)
 {
     unsigned long symnum;
     const Elf_Sym *vsymp;
@@ -3040,18 +3220,18 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
     int vcount;
 
     if (obj->buckets == NULL)
-       return NULL;
+       return (ESRCH);
 
     vsymp = NULL;
     vcount = 0;
-    symnum = obj->buckets[hash % obj->nbuckets];
+    symnum = obj->buckets[req->hash % obj->nbuckets];
 
     for (; symnum != STN_UNDEF; symnum = obj->chains[symnum]) {
        const Elf_Sym *symp;
        const char *strp;
 
        if (symnum >= obj->nchains)
-           return NULL;        /* Bad object */
+           return (ESRCH);     /* Bad object */
 
        symp = obj->symtab + symnum;
        strp = obj->strtab + symp->st_name;
@@ -3066,17 +3246,17 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
        case STT_TLS:
            if (symp->st_shndx != SHN_UNDEF)
                break;
-           else if (((flags & SYMLOOK_IN_PLT) == 0) &&
+           else if (((req->flags & SYMLOOK_IN_PLT) == 0) &&
                 (ELF_ST_TYPE(symp->st_info) == STT_FUNC))
                break;
                /* fallthrough */
        default:
            continue;
        }
-       if (name[0] != strp[0] || strcmp(name, strp) != 0)
+       if (req->name[0] != strp[0] || strcmp(req->name, strp) != 0)
            continue;
 
-       if (ventry == NULL) {
+       if (req->ventry == NULL) {
            if (obj->versyms != NULL) {
                verndx = VER_NDX(obj->versyms[symnum]);
                if (verndx > obj->vernum) {
@@ -3095,8 +3275,12 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
                 * end of the function. If symbol is global (verndx < 2)
                 * accept it unconditionally.
                 */
-               if ((flags & SYMLOOK_DLSYM) == 0 && verndx == VER_NDX_GIVEN)
-                   return symp;
+               if ((req->flags & SYMLOOK_DLSYM) == 0 &&
+                 verndx == VER_NDX_GIVEN) {
+                   req->sym_out = symp;
+                   req->defobj_out = obj;
+                   return (0);
+               }
                else if (verndx >= VER_NDX_GIVEN) {
                    if ((obj->versyms[symnum] & VER_NDX_HIDDEN) == 0) {
                        if (vsymp == NULL)
@@ -3106,13 +3290,15 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
                    continue;
                }
            }
-           return symp;
+           req->sym_out = symp;
+           req->defobj_out = obj;
+           return (0);
        } else {
            if (obj->versyms == NULL) {
-               if (object_match_name(obj, ventry->name)) {
+               if (object_match_name(obj, req->ventry->name)) {
                    _rtld_error("%s: object %s should provide version %s for "
-                       "symbol %s", obj_rtld.path, obj->path, ventry->name,
-                       obj->strtab + symnum);
+                       "symbol %s", obj_rtld.path, obj->path,
+                       req->ventry->name, obj->strtab + symnum);
                    continue;
                }
            } else {
@@ -3122,8 +3308,8 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
                        obj->path, obj->strtab + symnum, verndx);
                    continue;
                }
-               if (obj->vertab[verndx].hash != ventry->hash ||
-                   strcmp(obj->vertab[verndx].name, ventry->name)) {
+               if (obj->vertab[verndx].hash != req->ventry->hash ||
+                   strcmp(obj->vertab[verndx].name, req->ventry->name)) {
                    /*
                     * Version does not match. Look if this is a global symbol
                     * and if it is not hidden. If global symbol (verndx < 2)
@@ -3131,16 +3317,23 @@ symlook_obj(const char *name, unsigned long hash, const Obj_Entry *obj,
                     * called by dlvsym, because dlvsym looks for a specific
                     * version and default one is not what dlvsym wants.
                     */
-                   if ((flags & SYMLOOK_DLSYM) ||
+                   if ((req->flags & SYMLOOK_DLSYM) ||
                        (obj->versyms[symnum] & VER_NDX_HIDDEN) ||
                        (verndx >= VER_NDX_GIVEN))
                        continue;
                }
            }
-           return symp;
+           req->sym_out = symp;
+           req->defobj_out = obj;
+           return (0);
        }
     }
-    return (vcount == 1) ? vsymp : NULL;
+    if (vcount == 1) {
+       req->sym_out = vsymp;
+       req->defobj_out = obj;
+       return (0);
+    }
+    return (ESRCH);
 }
 
 static void
@@ -3257,6 +3450,7 @@ unload_object(Obj_Entry *root)
            LD_UTRACE(UTRACE_UNLOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0,
                obj->path);
            dbg("unloading \"%s\"", obj->path);
+           unload_filtees(root);
            munmap(obj->mapbase, obj->mapsize);
            linkmap_delete(obj);
            *linkp = obj->next;
@@ -3780,7 +3974,33 @@ fetch_ventry(const Obj_Entry *obj, unsigned long symnum)
     return NULL;
 }
 
+void
+symlook_init(SymLook *dst, const char *name)
+{
+
+       bzero(dst, sizeof(*dst));
+       dst->name = name;
+       dst->hash = elf_hash(name);
+}
+
+static void
+symlook_init_from_req(SymLook *dst, const SymLook *src)
+{
+
+       dst->name = src->name;
+       dst->hash = src->hash;
+       dst->ventry = src->ventry;
+       dst->flags = src->flags;
+       dst->defobj_out = NULL;
+       dst->sym_out = NULL;
+       dst->lockstate = src->lockstate;
+}
+
 #ifdef ENABLE_OSRELDATE
+/*
+ * Overrides for libc_pic-provided functions.
+ */
+
 int
 __getosreldate(void)
 {
index 9b4eea8..9b58185 100644 (file)
@@ -184,6 +184,8 @@ typedef struct Struct_Obj_Entry {
 
     char *rpath;               /* Search path specified in object */
     Needed_Entry *needed;      /* Shared objects needed by this one (%) */
+    Needed_Entry *needed_filtees;
+    Needed_Entry *needed_aux_filtees;
 
     STAILQ_HEAD(, Struct_Name_Entry) names; /* List of names for this object we
                                               know about. */
@@ -206,10 +208,12 @@ typedef struct Struct_Obj_Entry {
     bool z_origin : 1;         /* Process rpath and soname tokens */
     bool z_nodelete : 1;       /* Do not unload the object and dependencies */
     bool z_noopen : 1;         /* Do not load on dlopen */
+    bool z_loadfltr : 1;       /* Immediately load filtees */
     bool ref_nodel : 1;                /* Refcount increased to prevent dlclose */
     bool init_scanned: 1;      /* Object is already on init list. */
     bool on_fini_list: 1;      /* Object is already on fini list. */
     bool dag_inited : 1;       /* Object has its DAG initialized. */
+    bool filtees_loaded : 1;   /* Filtees loaded */
 
     struct link_map linkmap;   /* For GDB and dlinfo() */
     Objlist dldags;            /* Object belongs to these dlopened DAGs (%) */
@@ -222,6 +226,8 @@ typedef struct Struct_Obj_Entry {
 #define RTLD_MAGIC     0xd550b87a
 #define RTLD_VERSION   1
 
+#define RTLD_FUNCTRACE "_rtld_functrace"
+
 /* Flags to be passed into symlook_ family of functions. */
 #define SYMLOOK_IN_PLT 0x01    /* Lookup for PLT symbol */
 #define SYMLOOK_DLSYM  0x02    /* Return newest versioned symbol. Used by
@@ -230,8 +236,9 @@ typedef struct Struct_Obj_Entry {
 /* Flags for load_object(). */
 #define        RTLD_LO_NOLOAD  0x01    /* dlopen() specified RTLD_NOLOAD. */
 #define        RTLD_LO_DLOPEN  0x02    /* Load_object() called from dlopen(). */
-#define        RTLD_LO_TRACE           0x04    /* Only tracing. */
-#define        RTLD_LO_NODELETE        0x08    /* Loaded object cannot be closed. */
+#define        RTLD_LO_TRACE   0x04    /* Only tracing. */
+#define        RTLD_LO_NODELETE 0x08   /* Loaded object cannot be closed. */
+#define        RTLD_LO_FILTEES 0x10    /* Loading filtee. */
 
 /*
  * Symbol cache entry used during relocation to avoid multiple lookups
@@ -242,11 +249,34 @@ typedef struct Struct_SymCache {
     const Obj_Entry *obj;      /* Shared object which defines it */
 } SymCache;
 
+/*
+ * This structure provides a reentrant way to keep a list of objects and
+ * check which ones have already been processed in some way.
+ */
+typedef struct Struct_DoneList {
+    const Obj_Entry **objs;            /* Array of object pointers */
+    unsigned int num_alloc;            /* Allocated size of the array */
+    unsigned int num_used;             /* Number of array slots used */
+} DoneList;
+
 struct Struct_RtldLockState {
        int lockstate;
        sigjmp_buf env;
 };
 
+/*
+ * The pack of arguments and results for the symbol lookup functions.
+ */
+typedef struct Struct_SymLook {
+    const char *name;
+    unsigned long hash;
+    const Ver_Entry *ventry;
+    int flags;
+    const Obj_Entry *defobj_out;
+    const Elf_Sym *sym_out;
+    struct Struct_RtldLockState *lockstate;
+} SymLook;
+
 void _rtld_error(const char *, ...) __printflike(1, 2);
 Obj_Entry *map_object(int, const char *, const struct stat *);
 void *xcalloc(size_t);
@@ -265,14 +295,14 @@ void dump_Elf_Rela(Obj_Entry *, const Elf_Rela *, u_long);
 const char *basename(const char *);
 unsigned long elf_hash(const char *);
 const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *,
-  const Obj_Entry **, int, SymCache *);
+  const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *);
 void init_pltgot(Obj_Entry *);
 void lockdflt_init(void);
 void obj_free(Obj_Entry *);
 Obj_Entry *obj_new(void);
 void _rtld_bind_start(void);
-const Elf_Sym  *symlook_obj(const char *, unsigned long, const Obj_Entry *,
-  const Ver_Entry *, int);
+void symlook_init(SymLook *, const char *);
+int symlook_obj(SymLook *, const Obj_Entry *);
 void *tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset);
 struct tls_tcb *allocate_tls(Obj_Entry *);
 void free_tls(struct tls_tcb *);
@@ -285,9 +315,9 @@ const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long);
  * MD function declarations.
  */
 int do_copy_relocations(Obj_Entry *);
-int reloc_non_plt(Obj_Entry *, Obj_Entry *);
+int reloc_non_plt(Obj_Entry *, Obj_Entry *, struct Struct_RtldLockState *);
 int reloc_plt(Obj_Entry *);
-int reloc_jmpslots(Obj_Entry *);
+int reloc_jmpslots(Obj_Entry *, struct Struct_RtldLockState *);
 void allocate_initial_tls(Obj_Entry *);
 
 #endif /* } */
index 4bd3c33..d82e0d6 100644 (file)
@@ -72,23 +72,28 @@ do_copy_relocations(Obj_Entry *dstobj)
            void *dstaddr;
            const Elf_Sym *dstsym;
            const char *name;
-           unsigned long hash;
            size_t size;
            const void *srcaddr;
            const Elf_Sym *srcsym;
-           Obj_Entry *srcobj;
-           const Ver_Entry *ve;
+           const Obj_Entry *srcobj, *defobj;
+           SymLook req;
+           int res;
 
            dstaddr = (void *) (dstobj->relocbase + rela->r_offset);
            dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);
            name = dstobj->strtab + dstsym->st_name;
-           hash = elf_hash(name);
            size = dstsym->st_size;
-           ve = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
-
-           for (srcobj = dstobj->next;  srcobj != NULL;  srcobj = srcobj->next)
-               if ((srcsym = symlook_obj(name, hash, srcobj, ve, 0)) != NULL)
+           symlook_init(&req, name);
+           req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));
+
+           for (srcobj = dstobj->next;  srcobj != NULL;  srcobj = srcobj->next) {
+               res = symlook_obj(&req, srcobj);
+               if (res == 0) {
+                   srcsym = req.sym_out;
+                   defobj = req.defobj_out;
                    break;
+               }
+           }
 
            if (srcobj == NULL) {
                _rtld_error("Undefined symbol \"%s\" referenced from COPY"
@@ -96,7 +101,7 @@ do_copy_relocations(Obj_Entry *dstobj)
                return -1;
            }
 
-           srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value);
+           srcaddr = (const void *) (defobj->relocbase + srcsym->st_value);
            memcpy(dstaddr, srcaddr, size);
        }
     }
@@ -116,7 +121,7 @@ init_pltgot(Obj_Entry *obj)
 
 /* Process the non-PLT relocations. */
 int
-reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
+reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, RtldLockState *lockstate)
 {
        const Elf_Rela *relalim;
        const Elf_Rela *rela;
@@ -149,7 +154,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -168,7 +173,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -198,7 +203,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -212,7 +217,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -243,7 +248,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -275,7 +280,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -289,7 +294,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -303,7 +308,7 @@ reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld)
                    const Obj_Entry *defobj;
 
                    def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,
-                     false, cache);
+                     false, cache, lockstate);
                    if (def == NULL)
                        goto done;
 
@@ -360,7 +365,7 @@ reloc_plt(Obj_Entry *obj)
 
 /* Relocate the jump slots in an object. */
 int
-reloc_jmpslots(Obj_Entry *obj)
+reloc_jmpslots(Obj_Entry *obj, RtldLockState *lockstate)
 {
     const Elf_Rela *relalim;
     const Elf_Rela *rela;
@@ -376,7 +381,8 @@ reloc_jmpslots(Obj_Entry *obj)
        switch (ELF_R_TYPE(rela->r_info)) {
        case R_X86_64_JMP_SLOT:
          where = (Elf_Addr *)(obj->relocbase + rela->r_offset);
-         def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL);
+         def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, true, NULL,
+             lockstate);
          if (def == NULL)
              return (-1);
          target = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend);