From owner-svn-src-all@FreeBSD.ORG Sat Jun 20 14:16:41 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 8D8F81065670; Sat, 20 Jun 2009 14:16:41 +0000 (UTC) (envelope-from kan@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 7A8EF8FC14; Sat, 20 Jun 2009 14:16:41 +0000 (UTC) (envelope-from kan@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n5KEGf4o078156; Sat, 20 Jun 2009 14:16:41 GMT (envelope-from kan@svn.freebsd.org) Received: (from kan@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n5KEGfUl078153; Sat, 20 Jun 2009 14:16:41 GMT (envelope-from kan@svn.freebsd.org) Message-Id: <200906201416.n5KEGfUl078153@svn.freebsd.org> From: Alexander Kabaev Date: Sat, 20 Jun 2009 14:16:41 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r194531 - head/libexec/rtld-elf X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 20 Jun 2009 14:16:42 -0000 Author: kan Date: Sat Jun 20 14:16:41 2009 New Revision: 194531 URL: http://svn.freebsd.org/changeset/base/194531 Log: Allow order of initialization of loaded shared objects to be altered through their .init code. This might happen if init vector calls dlopen on its own and that dlopen causes some not yet initialized object to be initialized earlier as part of that dlopened DAG. Do not reset module reference counts to zero on final fini vector run when process is exiting. Just add an additional parameter to force fini vector invocation regardless of current reference count value if object was not destructed yet. This allows dlclose called from fini vector to proceed normally instead of failing with handle validation error. Reviewed by: kib Reported by: venki kaps Modified: head/libexec/rtld-elf/rtld.c head/libexec/rtld-elf/rtld.h Modified: head/libexec/rtld-elf/rtld.c ============================================================================== --- head/libexec/rtld-elf/rtld.c Sat Jun 20 12:02:56 2009 (r194530) +++ head/libexec/rtld-elf/rtld.c Sat Jun 20 14:16:41 2009 (r194531) @@ -107,15 +107,14 @@ static int load_needed_objects(Obj_Entry 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 unlink_object(Obj_Entry *); 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 @@ initlist_add_neededs(Needed_Entry *neede 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 @@ initlist_add_objects(Obj_Entry *obj, Obj 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 @@ obj_from_addr(const void *addr) * 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 @@ objlist_call_fini(Objlist *list, int *lo * 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 recursive invocation. */ + 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 @@ static void 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 @@ objlist_remove(Objlist *list, Obj_Entry } /* - * 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 @@ relocate_objects(Obj_Entry *first, bool 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 @@ dlclose(void *handle) * 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 @@ do_dlsym(void *handle, const char *name, &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. Modified: head/libexec/rtld-elf/rtld.h ============================================================================== --- head/libexec/rtld-elf/rtld.h Sat Jun 20 12:02:56 2009 (r194530) +++ head/libexec/rtld-elf/rtld.h Sat Jun 20 14:16:41 2009 (r194531) @@ -218,9 +218,11 @@ typedef struct Struct_Obj_Entry { 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 */