=== ./rtld.c ================================================================== --- ./rtld.c (revision 194299) +++ ./rtld.c (local) @@ -107,15 +107,14 @@ static int load_preload_objects(void); static Obj_Entry *load_object(const char *, const Obj_Entry *); static Obj_Entry *obj_from_addr(const void *); -static void objlist_call_fini(Objlist *, int *lockstate); -static void objlist_call_init(Objlist *, int *lockstate); +static void objlist_call_fini(Objlist *, bool, int *); +static void objlist_call_init(Objlist *, int *); static void objlist_clear(Objlist *); static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); static void objlist_init(Objlist *); 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 objlist_remove_unref(Objlist *); static void *path_enumerate(const char *, path_enum_proc, void *); static int relocate_objects(Obj_Entry *, bool, Obj_Entry *); static int rtld_dirname(const char *, char *); @@ -136,9 +135,9 @@ static void unload_object(Obj_Entry *); static void unref_dag(Obj_Entry *); static void ref_dag(Obj_Entry *); -static int origin_subst_one(char **res, const char *real, const char *kw, - const char *subst, char *may_free); -static char *origin_subst(const char *real, const char *origin_path); +static int origin_subst_one(char **, const char *, const char *, + const char *, char *); +static char *origin_subst(const char *, const char *); static int rtld_verify_versions(const Objlist *); static int rtld_verify_object_versions(Obj_Entry *); static void object_add_name(Obj_Entry *, const char *); @@ -1379,9 +1378,9 @@ static void initlist_add_objects(Obj_Entry *obj, Obj_Entry **tail, Objlist *list) { - if (obj->init_done) + if (obj->init_scanned || obj->init_done) return; - obj->init_done = true; + obj->init_scanned = true; /* Recursively process the successor objects. */ if (&obj->next != tail) @@ -1396,8 +1395,10 @@ objlist_push_tail(list, obj); /* Add the object to the global fini list in the reverse order. */ - if (obj->fini != (Elf_Addr)NULL) + if (obj->fini != (Elf_Addr)NULL && !obj->on_fini_list) { objlist_push_head(&list_fini, obj); + obj->on_fini_list = true; + } } #ifndef FPTR_TARGET @@ -1600,9 +1601,9 @@ * non-NULL fini functions. */ static void -objlist_call_fini(Objlist *list, int *lockstate) +objlist_call_fini(Objlist *list, bool force, int *lockstate) { - Objlist_Entry *elm; + Objlist_Entry *elm, *elm_tmp; char *saved_msg; /* @@ -1610,17 +1611,22 @@ * call into the dynamic linker and overwrite it. */ saved_msg = errmsg_save(); - wlock_release(rtld_bind_lock, *lockstate); - STAILQ_FOREACH(elm, list, link) { - if (elm->obj->refcount == 0) { + STAILQ_FOREACH_SAFE(elm, list, link, elm_tmp) { + if (elm->obj->refcount == 0 || force) { dbg("calling fini function for %s at %p", elm->obj->path, (void *)elm->obj->fini); LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)elm->obj->fini, 0, 0, elm->obj->path); + /* Remove object from fini list to prevent recirsive invication. */ + STAILQ_REMOVE(list, elm, Struct_Objlist_Entry, link); + wlock_release(rtld_bind_lock, *lockstate); call_initfini_pointer(elm->obj, elm->obj->fini); + *lockstate = wlock_acquire(rtld_bind_lock); + /* No need to free anything if process is going down. */ + if (!force) + free(elm); } } - *lockstate = wlock_acquire(rtld_bind_lock); errmsg_restore(saved_msg); } @@ -1633,22 +1639,39 @@ objlist_call_init(Objlist *list, int *lockstate) { Objlist_Entry *elm; + Obj_Entry *obj; char *saved_msg; /* + * Clean init_scanned flag so that objects can be rechecked and + * possibly initialized earlier if any of vectors called below + * cause the change by using dlopen. + */ + for (obj = obj_list; obj != NULL; obj = obj->next) + obj->init_scanned = false; + + /* * Preserve the current error message since an init function might * call into the dynamic linker and overwrite it. */ saved_msg = errmsg_save(); - wlock_release(rtld_bind_lock, *lockstate); STAILQ_FOREACH(elm, list, link) { + if (elm->obj->init_done) /* Initialized early. */ + continue; dbg("calling init function for %s at %p", elm->obj->path, (void *)elm->obj->init); LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)elm->obj->init, 0, 0, elm->obj->path); + /* + * Race: other thread might try to use this object before current + * one completes the initilization. Not much can be done here + * without better locking. + */ + elm->obj->init_done = true; + wlock_release(rtld_bind_lock, *lockstate); call_initfini_pointer(elm->obj, elm->obj->init); + *lockstate = wlock_acquire(rtld_bind_lock); } - *lockstate = wlock_acquire(rtld_bind_lock); errmsg_restore(saved_msg); } @@ -1713,27 +1736,6 @@ } /* - * Remove all of the unreferenced objects from "list". - */ -static void -objlist_remove_unref(Objlist *list) -{ - Objlist newlist; - Objlist_Entry *elm; - - STAILQ_INIT(&newlist); - while (!STAILQ_EMPTY(list)) { - elm = STAILQ_FIRST(list); - STAILQ_REMOVE_HEAD(list, link); - if (elm->obj->refcount == 0) - free(elm); - else - STAILQ_INSERT_TAIL(&newlist, elm, link); - } - *list = newlist; -} - -/* * Relocate newly-loaded shared objects. The argument is a pointer to * the Obj_Entry for the first such object. All objects from the first * to the end of the list of objects are relocated. Returns 0 on success, @@ -1808,15 +1810,11 @@ static void rtld_exit(void) { - Obj_Entry *obj; int lockstate; lockstate = wlock_acquire(rtld_bind_lock); dbg("rtld_exit()"); - /* Clear all the reference counts so the fini functions will be called. */ - for (obj = obj_list; obj != NULL; obj = obj->next) - obj->refcount = 0; - objlist_call_fini(&list_fini, &lockstate); + objlist_call_fini(&list_fini, true, &lockstate); /* No need to remove the items from the list, since we are exiting. */ if (!libmap_disable) lm_fini(); @@ -1936,8 +1934,7 @@ * The object is no longer referenced, so we must unload it. * First, call the fini functions. */ - objlist_call_fini(&list_fini, &lockstate); - objlist_remove_unref(&list_fini); + objlist_call_fini(&list_fini, false, &lockstate); /* Finish cleaning up the newly-unreferenced objects. */ GDB_STATE(RT_DELETE,&root->linkmap); @@ -2132,7 +2129,7 @@ &donelist); /* - * We do not distinguish between 'main' object an 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. === ./rtld.h ================================================================== --- ./rtld.h (revision 194299) +++ ./rtld.h (local) @@ -218,9 +218,11 @@ bool phdr_alloc : 1; /* Phdr is allocated and needs to be freed. */ bool z_origin : 1; /* Process rpath and soname tokens */ bool z_nodelete : 1; /* Do not unload the object and dependencies */ - bool ref_nodel : 1; /* refcount increased to prevent dlclose */ + 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. */ - struct link_map linkmap; /* for GDB and dlinfo() */ + struct link_map linkmap; /* For GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ Objlist dagmembers; /* DAG has these members (%) */ dev_t dev; /* Object's filesystem's device */