about summary refs log tree commit diff
path: root/src/ldso/dynlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ldso/dynlink.c')
-rw-r--r--src/ldso/dynlink.c36
1 files changed, 29 insertions, 7 deletions
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<<DT_FINI))
-			atexit((void (*)(void))(p->base + dyn[DT_FINI]));
+		if (dyn[0] & (1<<DT_FINI)) {
+			p->fini_next = fini_head;
+			fini_head = p;
+		}
 		if (dyn[0] & (1<<DT_INIT))
 			((void (*)(void))(p->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;
 }