diff --git a/libexec/rtld-elf/alpha/lockdflt.c b/libexec/rtld-elf/alpha/lockdflt.c
index c92695001bd7..f9dbc5d66a3f 100644
--- a/libexec/rtld-elf/alpha/lockdflt.c
+++ b/libexec/rtld-elf/alpha/lockdflt.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright 1999 John D. Polstra.
+ * Copyright 1999, 2000 John D. Polstra.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -51,11 +51,8 @@ void
 lockdflt_acquire(void *lock)
 {
     LockDflt *l = (LockDflt *)lock;
-    sigset_t old_mask;
-
-    sigprocmask(SIG_BLOCK, &l->lock_mask, &old_mask);
-    if (l->depth == 0)
-	l->old_mask = old_mask;
+    sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask);
+    assert(l->depth == 0);
     l->depth++;
 }
 
@@ -84,15 +81,7 @@ void
 lockdflt_release(void *lock)
 {
     LockDflt *l = (LockDflt *)lock;
+    assert(l->depth == 1);
     l->depth--;
-    assert(l->depth >= 0);
-    if (l->depth == 0)
-	sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
-}
-
-void
-lockdflt_init(void)
-{
-    dllockinit(NULL, lockdflt_create, lockdflt_acquire, lockdflt_acquire,
-      lockdflt_release, lockdflt_destroy, NULL);
+    sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
 }
diff --git a/libexec/rtld-elf/amd64/lockdflt.c b/libexec/rtld-elf/amd64/lockdflt.c
index c92695001bd7..f9dbc5d66a3f 100644
--- a/libexec/rtld-elf/amd64/lockdflt.c
+++ b/libexec/rtld-elf/amd64/lockdflt.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright 1999 John D. Polstra.
+ * Copyright 1999, 2000 John D. Polstra.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -51,11 +51,8 @@ void
 lockdflt_acquire(void *lock)
 {
     LockDflt *l = (LockDflt *)lock;
-    sigset_t old_mask;
-
-    sigprocmask(SIG_BLOCK, &l->lock_mask, &old_mask);
-    if (l->depth == 0)
-	l->old_mask = old_mask;
+    sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask);
+    assert(l->depth == 0);
     l->depth++;
 }
 
@@ -84,15 +81,7 @@ void
 lockdflt_release(void *lock)
 {
     LockDflt *l = (LockDflt *)lock;
+    assert(l->depth == 1);
     l->depth--;
-    assert(l->depth >= 0);
-    if (l->depth == 0)
-	sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
-}
-
-void
-lockdflt_init(void)
-{
-    dllockinit(NULL, lockdflt_create, lockdflt_acquire, lockdflt_acquire,
-      lockdflt_release, lockdflt_destroy, NULL);
+    sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
 }
diff --git a/libexec/rtld-elf/i386/lockdflt.c b/libexec/rtld-elf/i386/lockdflt.c
index c92695001bd7..f9dbc5d66a3f 100644
--- a/libexec/rtld-elf/i386/lockdflt.c
+++ b/libexec/rtld-elf/i386/lockdflt.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright 1999 John D. Polstra.
+ * Copyright 1999, 2000 John D. Polstra.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -51,11 +51,8 @@ void
 lockdflt_acquire(void *lock)
 {
     LockDflt *l = (LockDflt *)lock;
-    sigset_t old_mask;
-
-    sigprocmask(SIG_BLOCK, &l->lock_mask, &old_mask);
-    if (l->depth == 0)
-	l->old_mask = old_mask;
+    sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask);
+    assert(l->depth == 0);
     l->depth++;
 }
 
@@ -84,15 +81,7 @@ void
 lockdflt_release(void *lock)
 {
     LockDflt *l = (LockDflt *)lock;
+    assert(l->depth == 1);
     l->depth--;
-    assert(l->depth >= 0);
-    if (l->depth == 0)
-	sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
-}
-
-void
-lockdflt_init(void)
-{
-    dllockinit(NULL, lockdflt_create, lockdflt_acquire, lockdflt_acquire,
-      lockdflt_release, lockdflt_destroy, NULL);
+    sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
 }
diff --git a/libexec/rtld-elf/lockdflt.c b/libexec/rtld-elf/lockdflt.c
index c92695001bd7..f9dbc5d66a3f 100644
--- a/libexec/rtld-elf/lockdflt.c
+++ b/libexec/rtld-elf/lockdflt.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright 1999 John D. Polstra.
+ * Copyright 1999, 2000 John D. Polstra.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -51,11 +51,8 @@ void
 lockdflt_acquire(void *lock)
 {
     LockDflt *l = (LockDflt *)lock;
-    sigset_t old_mask;
-
-    sigprocmask(SIG_BLOCK, &l->lock_mask, &old_mask);
-    if (l->depth == 0)
-	l->old_mask = old_mask;
+    sigprocmask(SIG_BLOCK, &l->lock_mask, &l->old_mask);
+    assert(l->depth == 0);
     l->depth++;
 }
 
@@ -84,15 +81,7 @@ void
 lockdflt_release(void *lock)
 {
     LockDflt *l = (LockDflt *)lock;
+    assert(l->depth == 1);
     l->depth--;
-    assert(l->depth >= 0);
-    if (l->depth == 0)
-	sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
-}
-
-void
-lockdflt_init(void)
-{
-    dllockinit(NULL, lockdflt_create, lockdflt_acquire, lockdflt_acquire,
-      lockdflt_release, lockdflt_destroy, NULL);
+    sigprocmask(SIG_SETMASK, &l->old_mask, NULL);
 }
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
index 5bcd390a6232..b5bba084544d 100644
--- a/libexec/rtld-elf/rtld.c
+++ b/libexec/rtld-elf/rtld.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright 1996, 1997, 1998, 1999 John D. Polstra.
+ * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -73,13 +73,16 @@ typedef struct Struct_LockInfo {
  * Function declarations.
  */
 static const char *basename(const char *);
-static void call_fini_functions(Obj_Entry *);
-static void call_init_functions(Obj_Entry *);
 static void die(void);
 static void digest_dynamic(Obj_Entry *);
 static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *);
 static Obj_Entry *dlcheck(void *);
 static char *find_library(const char *, const Obj_Entry *);
+static void funclist_call(Funclist *);
+static void funclist_clear(Funclist *);
+static void funclist_init(Funclist *);
+static void funclist_push_head(Funclist *, InitFunc);
+static void funclist_push_tail(Funclist *, InitFunc);
 static const char *gethints(void);
 static void init_dag(Obj_Entry *);
 static void init_dag1(Obj_Entry *root, Obj_Entry *obj);
@@ -102,7 +105,7 @@ static void set_program_var(const char *, const void *);
 static const Elf_Sym *symlook_list(const char *, unsigned long,
   Objlist *, const Obj_Entry **, bool in_plt);
 static void trace_loaded_objects(Obj_Entry *obj);
-static void unload_object(Obj_Entry *, bool do_fini_funcs);
+static void unload_object(Obj_Entry *);
 static void unref_dag(Obj_Entry *);
 
 void r_debug_state(void);
@@ -158,7 +161,7 @@ static func_ptr_type exports[] = {
 
 /*
  * Global declarations normally provided by crt1.  The dynamic linker is
- * not build with crt1, so we have to provide them ourselves.
+ * not built with crt1, so we have to provide them ourselves.
  */
 char *__progname;
 char **environ;
@@ -209,6 +212,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
     Elf_Auxinfo *auxp;
     const char *argv0;
     Obj_Entry *obj;
+    Funclist initlist;
 
     /*
      * On entry, the dynamic linker itself has not been relocated yet.
@@ -325,15 +329,22 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
     if (load_needed_objects(obj_main) == -1)
 	die();
 
-    for (obj = obj_list;  obj != NULL;  obj = obj->next)
+    /*
+     * Make a list of all objects loaded at startup.  Also construct
+     * the list of init functions to call, in reverse order.
+     */
+    funclist_init(&initlist);
+    for (obj = obj_list;  obj != NULL;  obj = obj->next) {
 	objlist_add(&list_main, obj);
+	if (obj->init != NULL && !obj->mainprog)
+	    funclist_push_head(&initlist, obj->init);
+    }
 
     if (ld_tracing) {		/* We're done */
 	trace_loaded_objects(obj_main);
 	exit(0);
     }
 
-    dbg("relocating objects");
     if (relocate_objects(obj_main,
 	ld_bind_now != NULL && *ld_bind_now != '\0') == -1)
 	die();
@@ -351,8 +362,10 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
 
     r_debug_state();		/* say hello to gdb! */
 
-    dbg("calling _init functions");
-    call_init_functions(obj_main->next);
+    funclist_call(&initlist);
+    wlock_acquire();
+    funclist_clear(&initlist);
+    lock_release();
 
     dbg("transferring control to program entry point = %p", obj_main->entry);
 
@@ -417,26 +430,6 @@ basename(const char *name)
     return p != NULL ? p + 1 : name;
 }
 
-static void
-call_fini_functions(Obj_Entry *first)
-{
-    Obj_Entry *obj;
-
-    for (obj = first;  obj != NULL;  obj = obj->next)
-	if (obj->fini != NULL)
-	    (*obj->fini)();
-}
-
-static void
-call_init_functions(Obj_Entry *first)
-{
-    if (first != NULL) {
-	call_init_functions(first->next);
-	if (first->init != NULL)
-	    (*first->init)();
-    }
-}
-
 static void
 die(void)
 {
@@ -565,11 +558,11 @@ digest_dynamic(Obj_Entry *obj)
 	    break;
 
 	case DT_INIT:
-	    obj->init = (void (*)(void)) (obj->relocbase + dynp->d_un.d_ptr);
+	    obj->init = (InitFunc) (obj->relocbase + dynp->d_un.d_ptr);
 	    break;
 
 	case DT_FINI:
-	    obj->fini = (void (*)(void)) (obj->relocbase + dynp->d_un.d_ptr);
+	    obj->fini = (InitFunc) (obj->relocbase + dynp->d_un.d_ptr);
 	    break;
 
 	case DT_DEBUG:
@@ -834,6 +827,55 @@ find_symdef(unsigned long symnum, Obj_Entry *refobj,
     return def;
 }
 
+static void
+funclist_call(Funclist *list)
+{
+    Funclist_Entry *elm;
+
+    STAILQ_FOREACH(elm, list, link) {
+	dbg("calling init/fini function at %p", elm->func);
+	(*elm->func)();
+    }
+}
+
+static void
+funclist_clear(Funclist *list)
+{
+    Funclist_Entry *elm;
+
+    while (!STAILQ_EMPTY(list)) {
+	elm = STAILQ_FIRST(list);
+	STAILQ_REMOVE_HEAD(list, link);
+	free(elm);
+    }
+}
+
+static void
+funclist_init(Funclist *list)
+{
+    STAILQ_INIT(list);
+}
+
+static void
+funclist_push_head(Funclist *list, InitFunc func)
+{
+    Funclist_Entry *elm;
+
+    elm = NEW(Funclist_Entry);
+    elm->func = func;
+    STAILQ_INSERT_HEAD(list, elm, link);
+}
+
+static void
+funclist_push_tail(Funclist *list, InitFunc func)
+{
+    Funclist_Entry *elm;
+
+    elm = NEW(Funclist_Entry);
+    elm->func = func;
+    STAILQ_INSERT_TAIL(list, elm, link);
+}
+
 /*
  * Return the search path from the ldconfig hints file, reading it if
  * necessary.  Returns NULL if there are problems with the hints file,
@@ -1214,8 +1256,12 @@ relocate_objects(Obj_Entry *first, bool bind_now)
 static void
 rtld_exit(void)
 {
+    Obj_Entry *obj;
+
     dbg("rtld_exit()");
-    call_fini_functions(obj_list->next);
+    for (obj = obj_list->next;  obj != NULL;  obj = obj->next)
+	if (obj->fini != NULL)
+	    (*obj->fini)();
 }
 
 static char *
@@ -1258,6 +1304,8 @@ int
 dlclose(void *handle)
 {
     Obj_Entry *root;
+    Obj_Entry *obj;
+    Funclist finilist;
 
     wlock_acquire();
     root = dlcheck(handle);
@@ -1266,11 +1314,31 @@ dlclose(void *handle)
 	return -1;
     }
 
-    GDB_STATE(RT_DELETE);
-    unload_object(root, true);
+    /* Unreference the object and its dependencies. */
     root->dl_refcount--;
-    GDB_STATE(RT_CONSISTENT);
+    unref_dag(root);
 
+    if (root->refcount == 0) {
+	/*
+	 * The object is no longer referenced, so we must unload it.
+	 * First, make a list of the fini functions and then call them
+	 * with no locks held.
+	 */
+	funclist_init(&finilist);
+	for (obj = obj_list->next;  obj != NULL;  obj = obj->next)
+	    if (obj->refcount == 0 && obj->fini != NULL)
+		funclist_push_tail(&finilist, obj->fini);
+
+	lock_release();
+	funclist_call(&finilist);
+	wlock_acquire();
+	funclist_clear(&finilist);
+
+	/* Finish cleaning up the newly-unreferenced objects. */
+	GDB_STATE(RT_DELETE);
+	unload_object(root);
+	GDB_STATE(RT_CONSISTENT);
+    }
     lock_release();
     return 0;
 }
@@ -1338,6 +1406,10 @@ dlopen(const char *name, int mode)
 {
     Obj_Entry **old_obj_tail;
     Obj_Entry *obj;
+    Obj_Entry *initobj;
+    Funclist initlist;
+
+    funclist_init(&initlist);
 
     wlock_acquire();
     GDB_STATE(RT_ADD);
@@ -1363,15 +1435,27 @@ dlopen(const char *name, int mode)
 
 	    if (load_needed_objects(obj) == -1 ||
 	      (init_dag(obj), relocate_objects(obj, mode == RTLD_NOW)) == -1) {
-		unload_object(obj, false);
 		obj->dl_refcount--;
+		unref_dag(obj);
+		if (obj->refcount == 0)
+		    unload_object(obj);
 		obj = NULL;
-	    } else
-		call_init_functions(obj);
+	    } else {
+		/* Make list of init functions to call, in reverse order */
+		for (initobj = obj; initobj != NULL; initobj = initobj->next)
+		    if (initobj->init != NULL)
+			funclist_push_head(&initlist, initobj->init);
+	    }
 	}
     }
 
     GDB_STATE(RT_CONSISTENT);
+
+    /* Call the init functions with no locks held. */
+    lock_release();
+    funclist_call(&initlist);
+    wlock_acquire();
+    funclist_clear(&initlist);
     lock_release();
     return obj;
 }
@@ -1734,44 +1818,40 @@ trace_loaded_objects(Obj_Entry *obj)
 }
 
 /*
- * Note, this is called only for objects loaded by dlopen().
+ * Unload a dlopened object and its dependencies from memory and from
+ * our data structures.  It is assumed that the DAG rooted in the
+ * object has already been unreferenced, and that the object has a
+ * reference count of 0.
  */
 static void
-unload_object(Obj_Entry *root, bool do_fini_funcs)
+unload_object(Obj_Entry *root)
 {
-    unref_dag(root);
-    if (root->refcount == 0) {	/* We are finished with some objects. */
-	Obj_Entry *obj;
-	Obj_Entry **linkp;
-	Objlist_Entry *elm;
+    Obj_Entry *obj;
+    Obj_Entry **linkp;
+    Objlist_Entry *elm;
 
-	/* Finalize objects that are about to be unmapped. */
-	if (do_fini_funcs)
-	    for (obj = obj_list->next;  obj != NULL;  obj = obj->next)
-		if (obj->refcount == 0 && obj->fini != NULL)
-		    (*obj->fini)();
+    assert(root->refcount == 0);
 
-	/* Remove the DAG from all objects' DAG lists. */
-	STAILQ_FOREACH(elm, &root->dagmembers , link)
-	    objlist_remove(&elm->obj->dldags, root);
+    /* Remove the DAG from all objects' DAG lists. */
+    STAILQ_FOREACH(elm, &root->dagmembers , link)
+	objlist_remove(&elm->obj->dldags, root);
 
-	/* Remove the DAG from the RTLD_GLOBAL list. */
-	objlist_remove(&list_global, root);
+    /* Remove the DAG from the RTLD_GLOBAL list. */
+    objlist_remove(&list_global, root);
 
-	/* Unmap all objects that are no longer referenced. */
-	linkp = &obj_list->next;
-	while ((obj = *linkp) != NULL) {
-	    if (obj->refcount == 0) {
-		dbg("unloading \"%s\"", obj->path);
-		munmap(obj->mapbase, obj->mapsize);
-		linkmap_delete(obj);
-		*linkp = obj->next;
-		obj_free(obj);
-	    } else
-		linkp = &obj->next;
-	}
-	obj_tail = linkp;
+    /* Unmap all objects that are no longer referenced. */
+    linkp = &obj_list->next;
+    while ((obj = *linkp) != NULL) {
+	if (obj->refcount == 0) {
+	    dbg("unloading \"%s\"", obj->path);
+	    munmap(obj->mapbase, obj->mapsize);
+	    linkmap_delete(obj);
+	    *linkp = obj->next;
+	    obj_free(obj);
+	} else
+	    linkp = &obj->next;
     }
+    obj_tail = linkp;
 }
 
 static void
diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
index b8fcf2c78fb2..3a88547d0298 100644
--- a/libexec/rtld-elf/rtld.h
+++ b/libexec/rtld-elf/rtld.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright 1996-1998 John D. Polstra.
+ * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -52,6 +52,7 @@ typedef unsigned char bool;
 struct stat;
 struct Struct_Obj_Entry;
 
+/* Lists of shared objects */
 typedef struct Struct_Objlist_Entry {
     STAILQ_ENTRY(Struct_Objlist_Entry) link;
     struct Struct_Obj_Entry *obj;
@@ -59,6 +60,17 @@ typedef struct Struct_Objlist_Entry {
 
 typedef STAILQ_HEAD(Struct_Objlist, Struct_Objlist_Entry) Objlist;
 
+/* Lists of init or fini functions */
+typedef void (*InitFunc)(void);
+
+typedef struct Struct_Funclist_Entry {
+    STAILQ_ENTRY(Struct_Funclist_Entry) link;
+    InitFunc func;
+} Funclist_Entry;
+
+typedef STAILQ_HEAD(Struct_Funclist, Struct_Funclist_Entry) Funclist;
+
+/* Lists of shared object dependencies */
 typedef struct Struct_Needed_Entry {
     struct Struct_Needed_Entry *next;
     struct Struct_Obj_Entry *obj;
@@ -122,8 +134,8 @@ typedef struct Struct_Obj_Entry {
     const char *rpath;		/* Search path specified in object */
     Needed_Entry *needed;	/* Shared objects needed by this one (%) */
 
-    void (*init)(void);		/* Initialization function to call */
-    void (*fini)(void);		/* Termination function to call */
+    InitFunc init;		/* Initialization function to call */
+    InitFunc fini;		/* Termination function to call */
 
     bool mainprog;		/* True if this is the main program */
     bool rtld;			/* True if this is the dynamic linker */