diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c index 3f9bc145..e07db33a 100644 --- a/src/ldso/dynlink.c +++ b/src/ldso/dynlink.c @@ -72,6 +72,7 @@ struct dso { void **new_dtv; unsigned char *new_tls; int new_dtv_idx, new_tls_idx; + struct dso *fini_next; char *shortname; char buf[]; }; @@ -86,7 +87,7 @@ struct symdef { void __init_ssp(size_t *); void *__install_initial_tls(void *); -static struct dso *head, *tail, *libc; +static struct dso *head, *tail, *libc, *fini_head; static char *env_path, *sys_path, *r_path; static int ssp_used; static int runtime; @@ -97,6 +98,7 @@ static pthread_rwlock_t lock; static struct debug debug; static size_t *auxv; static size_t tls_cnt, tls_size; +static pthread_mutex_t init_fini_lock = { ._m_type = PTHREAD_MUTEX_RECURSIVE }; struct debug *_dl_debug_addr = &debug; @@ -642,18 +644,37 @@ static void find_map_range(Phdr *ph, size_t cnt, size_t stride, struct dso *p) p->map_len = max_addr - min_addr; } +static void do_fini() +{ + struct dso *p; + size_t dyn[DYN_CNT] = {0}; + for (p=fini_head; p; p=p->fini_next) { + if (!p->constructed) continue; + decode_vec(p->dynv, dyn, DYN_CNT); + ((void (*)(void))(p->base + dyn[DT_FINI]))(); + } +} + static void do_init_fini(struct dso *p) { size_t dyn[DYN_CNT] = {0}; + int need_locking = __libc.threads_minus_1; + /* Allow recursive calls that arise when a library calls + * dlopen from one of its constructors, but block any + * other threads until all ctors have finished. */ + if (need_locking) pthread_mutex_lock(&init_fini_lock); for (; p; p=p->prev) { - if (p->constructed) return; + if (p->constructed) continue; + p->constructed = 1; decode_vec(p->dynv, dyn, DYN_CNT); - if (dyn[0] & (1<base + dyn[DT_FINI])); + if (dyn[0] & (1<fini_next = fini_head; + fini_head = p; + } if (dyn[0] & (1<base + dyn[DT_INIT]))(); - p->constructed = 1; } + if (need_locking) pthread_mutex_unlock(&init_fini_lock); } void _dl_debug_state(void) @@ -932,6 +953,7 @@ void *__dynlink(int argc, char **argv) if (ssp_used) __init_ssp(auxv); + atexit(do_fini); do_init_fini(tail); errno = 0; @@ -1007,11 +1029,11 @@ void *dlopen(const char *file, int mode) if (ssp_used) __init_ssp(auxv); _dl_debug_state(); - - do_init_fini(tail); + orig_tail = tail; end: __release_ptc(); pthread_rwlock_unlock(&lock); + if (p) do_init_fini(orig_tail); pthread_setcancelstate(cs, 0); return p; }