diff options
author | jdp <jdp@FreeBSD.org> | 2001-01-05 04:36:17 +0000 |
---|---|---|
committer | jdp <jdp@FreeBSD.org> | 2001-01-05 04:36:17 +0000 |
commit | 1f45c7f8de614b14e53b43eb44d9cefb822c3868 (patch) | |
tree | 0d8b47d80f6747c6336b81bb82498a7858cd1964 /libexec | |
parent | efb9aed41f3f45bdbee250f1a3193b60fd3fc07b (diff) | |
download | FreeBSD-src-1f45c7f8de614b14e53b43eb44d9cefb822c3868.zip FreeBSD-src-1f45c7f8de614b14e53b43eb44d9cefb822c3868.tar.gz |
Fix a bug in which a program called dlclose from a destructor and
got an assert failure in the dynamic linker.
Diffstat (limited to 'libexec')
-rw-r--r-- | libexec/rtld-elf/rtld.c | 45 |
1 files changed, 43 insertions, 2 deletions
diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 0b595df..0cffa74 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -77,6 +77,8 @@ 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 bool donelist_check(DoneList *, const Obj_Entry *); +static void errmsg_restore(char *); +static char *errmsg_save(void); static char *find_library(const char *, const Obj_Entry *); static const char *gethints(void); static void init_dag(Obj_Entry *); @@ -457,6 +459,30 @@ _rtld_error(const char *fmt, ...) va_end(ap); } +/* + * Return a dynamically-allocated copy of the current error message, if any. + */ +static char * +errmsg_save(void) +{ + return error_message == NULL ? NULL : xstrdup(error_message); +} + +/* + * Restore the current error message from a copy which was previously saved + * by errmsg_save(). The copy is freed. + */ +static void +errmsg_restore(char *saved_msg) +{ + if (saved_msg == NULL) + error_message = NULL; + else { + _rtld_error("%s", saved_msg); + free(saved_msg); + } +} + static const char * basename(const char *name) { @@ -696,7 +722,7 @@ dlcheck(void *handle) if (obj == (Obj_Entry *) handle) break; - if (obj == NULL || obj->dl_refcount == 0) { + if (obj == NULL || obj->refcount == 0 || obj->dl_refcount == 0) { _rtld_error("Invalid shared object handle %p", handle); return NULL; } @@ -1184,13 +1210,20 @@ static void objlist_call_fini(Objlist *list) { Objlist_Entry *elm; + char *saved_msg; + /* + * Preserve the current error message since a fini function might + * call into the dynamic linker and overwrite it. + */ + saved_msg = errmsg_save(); STAILQ_FOREACH(elm, list, link) { if (elm->obj->refcount == 0) { dbg("calling fini function for %s", elm->obj->path); (*elm->obj->fini)(); } } + errmsg_restore(saved_msg); } /* @@ -1202,11 +1235,18 @@ static void objlist_call_init(Objlist *list) { Objlist_Entry *elm; + char *saved_msg; + /* + * Preserve the current error message since an init function might + * call into the dynamic linker and overwrite it. + */ + saved_msg = errmsg_save(); STAILQ_FOREACH(elm, list, link) { dbg("calling init function for %s", elm->obj->path); (*elm->obj->init)(); } + errmsg_restore(saved_msg); } static void @@ -2030,7 +2070,8 @@ unref_dag(Obj_Entry *root) { const Needed_Entry *needed; - assert(root->refcount != 0); + if (root->refcount == 0) + return; root->refcount--; if (root->refcount == 0) for (needed = root->needed; needed != NULL; needed = needed->next) |