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.c94
1 files changed, 72 insertions, 22 deletions
diff --git a/src/ldso/dynlink.c b/src/ldso/dynlink.c
index 1447e205..4e0b9f4e 100644
--- a/src/ldso/dynlink.c
+++ b/src/ldso/dynlink.c
@@ -67,6 +67,8 @@ struct dso {
 	char relocated;
 	char constructed;
 	struct dso **deps;
+	void *tls_image;
+	size_t tls_len, tls_size, tls_align, tls_id;
 	char *shortname;
 	char buf[];
 };
@@ -74,6 +76,7 @@ struct dso {
 #include "reloc.h"
 
 void __init_ssp(size_t *);
+void *__install_initial_tls(void *);
 
 static struct dso *head, *tail, *libc;
 static char *env_path, *sys_path, *r_path;
@@ -86,6 +89,7 @@ static jmp_buf rtld_fail;
 static pthread_rwlock_t lock;
 static struct debug debug;
 static size_t *auxv;
+static size_t tls_cnt, tls_size;
 
 struct debug *_dl_debug_addr = &debug;
 
@@ -278,7 +282,7 @@ static void reclaim_gaps(unsigned char *base, Phdr *ph, size_t phent, size_t phc
 	}
 }
 
-static void *map_library(int fd, size_t *lenp, unsigned char **basep, size_t *dynp)
+static void *map_library(int fd, struct dso *dso)
 {
 	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
 	size_t phsize;
@@ -290,6 +294,7 @@ static void *map_library(int fd, size_t *lenp, unsigned char **basep, size_t *dy
 	unsigned prot;
 	unsigned char *map, *base;
 	size_t dyn;
+	size_t tls_image=0;
 	size_t i;
 
 	ssize_t l = read(fd, buf, sizeof buf);
@@ -306,6 +311,12 @@ static void *map_library(int fd, size_t *lenp, unsigned char **basep, size_t *dy
 	for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
 		if (ph->p_type == PT_DYNAMIC)
 			dyn = ph->p_vaddr;
+		if (ph->p_type == PT_TLS) {
+			tls_image = ph->p_vaddr;
+			dso->tls_align = ph->p_align;
+			dso->tls_len = ph->p_filesz;
+			dso->tls_size = ph->p_memsz;
+		}
 		if (ph->p_type != PT_LOAD) continue;
 		if (ph->p_vaddr < addr_min) {
 			addr_min = ph->p_vaddr;
@@ -360,9 +371,11 @@ static void *map_library(int fd, size_t *lenp, unsigned char **basep, size_t *dy
 		}
 	if (!runtime) reclaim_gaps(base, (void *)((char *)buf + eh->e_phoff),
 		eh->e_phentsize, eh->e_phnum);
-	*lenp = map_len;
-	*basep = base;
-	*dynp = dyn;
+	dso->map = map;
+	dso->map_len = map_len;
+	dso->base = base;
+	dso->dynv = (void *)(base+dyn);
+	if (dso->tls_size) dso->tls_image = (void *)(base+tls_image);
 	return map;
 error:
 	munmap(map, map_len);
@@ -402,7 +415,7 @@ static struct dso *load_library(const char *name)
 	const char *pathname;
 	unsigned char *base, *map;
 	size_t dyno, map_len;
-	struct dso *p;
+	struct dso *p, temp_dso = {0};
 	int fd;
 	struct stat st;
 
@@ -469,21 +482,21 @@ static struct dso *load_library(const char *name)
 			return p;
 		}
 	}
-	map = map_library(fd, &map_len, &base, &dyno);
+	map = map_library(fd, &temp_dso);
 	close(fd);
 	if (!map) return 0;
-	p = calloc(1, sizeof *p + strlen(pathname) + 1);
+	p = malloc(sizeof *p + strlen(pathname) + 1);
 	if (!p) {
 		munmap(map, map_len);
 		return 0;
 	}
-
-	p->map = map;
-	p->map_len = map_len;
-	p->base = base;
-	p->dynv = (void *)(base + dyno);
+	memcpy(p, &temp_dso, sizeof temp_dso);
 	decode_dyn(p);
-
+	if (p->tls_image) {
+		p->tls_id = ++tls_cnt;
+		tls_size += p->tls_size + p->tls_align + 8*sizeof(size_t) - 1
+			& -4*sizeof(size_t);
+	}
 	p->dev = st.st_dev;
 	p->ino = st.st_ino;
 	p->refcnt = 1;
@@ -622,6 +635,25 @@ void _dl_debug_state(void)
 {
 }
 
+void *__copy_tls(unsigned char *mem, size_t cnt)
+{
+	struct dso *p;
+	void **dtv = (void *)mem;
+	dtv[0] = (void *)cnt;
+	mem = (void *)(dtv + cnt + 1);
+	for (p=tail; p; p=p->prev) {
+		if (p->tls_id-1 >= cnt) continue;
+		mem += -p->tls_len & (4*sizeof(size_t)-1);
+		mem += ((uintptr_t)p->tls_image - (uintptr_t)mem)
+			& (p->tls_align-1);
+		dtv[p->tls_id] = mem;
+		memcpy(mem, p->tls_image, p->tls_len);
+		mem += p->tls_size;
+	}
+	((pthread_t)mem)->dtv = dtv;
+	return mem;
+}
+
 void *__dynlink(int argc, char **argv)
 {
 	size_t aux[AUX_CNT] = {0};
@@ -676,6 +708,7 @@ void *__dynlink(int argc, char **argv)
 
 	if (aux[AT_PHDR]) {
 		size_t interp_off = 0;
+		size_t tls_image = 0;
 		/* Find load address of the main program, via AT_PHDR vs PT_PHDR. */
 		phdr = (void *)aux[AT_PHDR];
 		for (i=aux[AT_PHNUM]; i; i--, phdr=(void *)((char *)phdr + aux[AT_PHENT])) {
@@ -683,7 +716,14 @@ void *__dynlink(int argc, char **argv)
 				app->base = (void *)(aux[AT_PHDR] - phdr->p_vaddr);
 			else if (phdr->p_type == PT_INTERP)
 				interp_off = (size_t)phdr->p_vaddr;
+			else if (phdr->p_type == PT_TLS) {
+				tls_image = phdr->p_vaddr;
+				app->tls_len = phdr->p_filesz;
+				app->tls_size = phdr->p_memsz;
+				app->tls_align = phdr->p_align;
+			}
 		}
+		if (app->tls_size) app->tls_image = (char *)app->base + tls_image;
 		if (interp_off) lib->name = (char *)app->base + interp_off;
 		app->name = argv[0];
 		app->dynv = (void *)(app->base + find_dyn(
@@ -709,7 +749,7 @@ void *__dynlink(int argc, char **argv)
 			_exit(1);
 		}
 		runtime = 1;
-		ehdr = (void *)map_library(fd, &app->map_len, &app->base, &dyno);
+		ehdr = (void *)map_library(fd, app);
 		if (!ehdr) {
 			dprintf(2, "%s: %s: Not a valid dynamic program\n", ldname, argv[0]);
 			_exit(1);
@@ -718,9 +758,13 @@ void *__dynlink(int argc, char **argv)
 		close(fd);
 		lib->name = ldname;
 		app->name = argv[0];
-		app->dynv = (void *)(app->base + dyno);
 		aux[AT_ENTRY] = ehdr->e_entry;
 	}
+	if (app->tls_size) {
+		app->tls_id = ++tls_cnt;
+		tls_size += app->tls_size+app->tls_align + 8*sizeof(size_t)-1
+			& -4*sizeof(size_t);
+	}
 	app->global = 1;
 	app->constructed = 1;
 	decode_dyn(app);
@@ -791,8 +835,19 @@ void *__dynlink(int argc, char **argv)
 	debug.state = 0;
 	_dl_debug_state();
 
-	/* Stand-in until real TLS support is added to dynamic linker */
-	__libc.tls_size = sizeof(struct pthread) + 4*sizeof(size_t);
+	tls_size += sizeof(struct pthread) + 4*sizeof(size_t);
+	__libc.tls_size = tls_size;
+	__libc.tls_cnt = tls_cnt;
+	if (tls_cnt) {
+		void *mem = mmap(0, __libc.tls_size, PROT_READ|PROT_WRITE,
+			MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+		if (mem==MAP_FAILED ||
+		    !__install_initial_tls(__copy_tls(mem, tls_cnt))) {
+			dprintf(2, "%s: Error getting %zu bytes thread-local storage: %m\n",
+				argv[0], tls_size);
+			_exit(127);
+		}
+	}
 	if (ssp_used) __init_ssp(auxv);
 
 	do_init_fini(tail);
@@ -807,11 +862,6 @@ void *__dynlink(int argc, char **argv)
 	return (void *)aux[AT_ENTRY];
 }
 
-void *__copy_tls(unsigned char *mem, size_t cnt)
-{
-	return mem;
-}
-
 void *dlopen(const char *file, int mode)
 {
 	struct dso *volatile p, *orig_tail = tail, *next;