rtld to support __thread tls

David Xu davidxu at t2t2.com
Fri Mar 4 23:23:51 PST 2005


Matthew Dillon wrote:
    Well, lets not let this slip through the cracks.  We want __thread / TLS
    support and we have the GDT infrastructure to do it now.    Maybe you
    should push your current patch set to David Xu with a quick summary and 
    you and he can discuss architectural issues over the weekend.

					-Matt
					Matthew Dillon 
					<dillon at xxxxxxxxxxxxx>
Hi All,

Because Joerg is working on rtld, I just picked up an relative easier
task --- hack all necessary code to support __thread for static libary,
I have spent two hours to work out all patches, please see attached
file, they works on my machine (a SMP box).
I don't know what was committed into rtld by Joerg yet, since I have to
fix some broken code due to FreeBSD's swappable kernel stack issue,
not have enough time. =(
Note, this only works for static library! the patches do not affect
shared objects, however applying the patch should be safe.
you should use gcc3 to compile sample code tls_test.c. the gcc 2.95 does
not support __thread.
I use csh, so:
# setenv CCVER gcc34
# cc -o tls_test tls_test.c -static -lthread_xu
David Xu

Index: i386/crt1.c
===================================================================
RCS file: /cvs/src/lib/csu/i386/crt1.c,v
retrieving revision 1.1
diff -u -r1.1 crt1.c
--- i386/crt1.c	15 Jun 2004 08:53:09 -0000	1.1
+++ i386/crt1.c	5 Mar 2005 07:04:41 -0000
@@ -39,6 +39,7 @@
 extern void _fini(void);
 extern void _init(void);
 extern int main(int, char **, char **);
+extern void _init_tls(void);
 
 #ifdef GCRT
 extern void _mcleanup(void);
@@ -85,6 +86,8 @@
 
     if(&_DYNAMIC != NULL)
 	atexit(rtld_cleanup);
+    else
+	_init_tls();
 
 #ifdef GCRT
     atexit(_mcleanup);
Index: kern/imgact_elf.c
===================================================================
RCS file: /cvs/src/sys/kern/imgact_elf.c,v
retrieving revision 1.25
diff -u -r1.25 imgact_elf.c
--- kern/imgact_elf.c	26 Feb 2005 20:32:36 -0000	1.25
+++ kern/imgact_elf.c	5 Mar 2005 07:02:31 -0000
@@ -542,6 +542,17 @@
   						     phdr[i].p_filesz, prot)) != 0)
   				goto fail;
 
+			/*
+			 * If this segment contains the program headers,
+			 * remember their virtual address for the AT_PHDR
+			 * aux entry. Static binaries don't usually include
+			 * a PT_PHDR entry.
+			 */
+			if (phdr[i].p_offset == 0 &&
+			    hdr->e_phoff + hdr->e_phnum * hdr->e_phentsize
+				<= phdr[i].p_filesz)
+				proghdr = phdr[i].p_vaddr + hdr->e_phoff;
+
 			seg_addr = trunc_page(phdr[i].p_vaddr);
 			seg_size = round_page(phdr[i].p_memsz +
 				phdr[i].p_vaddr - seg_addr);
Index: gen/Makefile.inc
===================================================================
RCS file: /cvs/src/lib/libc/gen/Makefile.inc,v
retrieving revision 1.7
diff -u -r1.7 Makefile.inc
--- gen/Makefile.inc	20 Feb 2005 01:52:25 -0000	1.7
+++ gen/Makefile.inc	5 Mar 2005 06:57:37 -0000
@@ -28,7 +28,7 @@
 	shmat.c shmctl.c shmdt.c shmget.c siginterrupt.c siglist.c signal.c \
 	sigsetops.c sigwait.c sleep.c srand48.c stringlist.c strtofflags.c \
 	sysconf.c sysctl.c sysctlbyname.c sysctlnametomib.c \
-	syslog.c telldir.c termios.c time.c times.c timezone.c ttyname.c \
+	syslog.c telldir.c termios.c time.c times.c timezone.c tls.c ttyname.c \
 	ttyslot.c ualarm.c ulimit.c uname.c unvis.c usleep.c utime.c \
 	valloc.c vis.c \
 	wait.c wait3.c waitpid.c
Index: i386/gen/Makefile.inc
===================================================================
RCS file: /cvs/src/lib/libc/i386/gen/Makefile.inc,v
retrieving revision 1.2
diff -u -r1.2 Makefile.inc
--- i386/gen/Makefile.inc	17 Jun 2003 04:26:42 -0000	1.2
+++ i386/gen/Makefile.inc	5 Mar 2005 06:57:37 -0000
@@ -2,5 +2,5 @@
 # $FreeBSD: src/lib/libc/i386/gen/Makefile.inc,v 1.10.2.1 2001/02/07 00:12:45 peter Exp $
 # $DragonFly: src/lib/libc/i386/gen/Makefile.inc,v 1.2 2003/06/17 04:26:42 dillon Exp $
 
-SRCS+=	_setjmp.S alloca.S fabs.S frexp.c infinity.c isinf.c ldexp.c modf.S \
-	rfork_thread.S setjmp.S sigsetjmp.S
+SRCS+=	_set_tp.c _setjmp.S alloca.S fabs.S frexp.c infinity.c isinf.c \
+	ldexp.c modf.S rfork_thread.S setjmp.S sigsetjmp.S
Index: include/libc_private.h
===================================================================
RCS file: /cvs/src/lib/libc/include/libc_private.h,v
retrieving revision 1.3
diff -u -r1.3 libc_private.h
--- include/libc_private.h	31 Jan 2005 22:29:29 -0000	1.3
+++ include/libc_private.h	5 Mar 2005 06:57:37 -0000
@@ -72,4 +72,14 @@
 int _fseeko(FILE *, __off_t, int);
 #endif
 
+/*
+ * Initialise TLS static programs
+ */
+void _init_tls(void);
+
+/*
+ * Set the TLS thread pointer
+ */
+void _set_tp(void *p, int size);
+
 #endif /* _LIBC_PRIVATE_H_ */
--- /dev/null	2005-03-05 14:01:56.000000000 +0000
+++ gen/tls.c	2005-03-05 14:51:56.000000000 +0000
@@ -0,0 +1,301 @@
+/*-
+ * Copyright (c) 2004 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	$FreeBSD: src/lib/libc/gen/tls.c,v 1.7 2005/03/01 23:42:00 davidxu Exp $
+ *	$DragonFly$
+ */
+
+/*
+ * Define stubs for TLS internals so that programs and libraries can
+ * link. These functions will be replaced by functional versions at
+ * runtime from ld-elf.so.1.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <elf.h>
+#include <assert.h>
+#include "libc_private.h"
+
+/* XXX not sure what variants to use for arm. */
+
+#if defined(__ia64__) || defined(__powerpc__)
+#define TLS_VARIANT_I
+#endif
+#if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) || \
+    defined(__arm__)
+#define TLS_VARIANT_II
+#endif
+
+#ifndef PIC
+
+#define round(size, align) \
+	(((size) + (align) - 1) & ~((align) - 1))
+
+static size_t tls_static_space;
+static size_t tls_init_size;
+#ifdef TLS_VARIANT_I
+static size_t tls_init_offset;
+#endif
+static void *tls_init;
+#endif
+
+void *_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign);
+void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign);
+void *__tls_get_addr(void *);
+
+#ifdef __i386__
+
+extern void *___tls_get_addr(void *ti) __attribute__((__regparm__(1)));
+
+#pragma weak ___tls_get_addr
+__attribute__((__regparm__(1)))
+void *
+___tls_get_addr(void *ti __unused)
+{
+	return (0);
+}
+
+#endif
+
+#pragma weak __tls_get_addr
+void *
+__tls_get_addr(void *ti __unused)
+{
+	return (0);
+}
+
+#ifdef TLS_VARIANT_I
+
+#pragma weak _rtld_free_tls
+/*
+ * Free Static TLS using the Variant I method.
+ */
+void
+_rtld_free_tls(void *tls, size_t tcbsize, size_t tcbalign)
+{
+#ifndef PIC
+	Elf_Addr* dtv;
+
+	dtv = ((Elf_Addr**)tls)[0];
+	free(tls);
+	free(dtv);
+#endif
+}
+
+#pragma weak _rtld_allocate_tls
+/*
+ * Allocate Static TLS using the Variant I method.
+ */
+void *
+_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
+{
+#ifndef PIC
+	size_t size;
+	char *tls;
+	Elf_Addr *dtv;
+
+	size = tls_static_space;
+	if (size < tcbsize)
+		size = tcbsize;
+
+	tls = malloc(size);
+	dtv = malloc(3 * sizeof(Elf_Addr));
+
+	*(Elf_Addr**) tls = dtv;
+
+	dtv[0] = 1;
+	dtv[1] = 1;
+	dtv[2] = (Elf_Addr)(tls + tls_init_offset);
+	if (oldtls) {
+		/*
+		 * Copy the static TLS block over whole.
+		 */
+		memcpy(tls + tls_init_offset,
+		    (char*) oldtls + tls_init_offset,
+		    tls_static_space - tls_init_offset);
+
+		/*
+		 * We assume that this block was the one we created with
+		 * allocate_initial_tls().
+		 */
+		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
+	} else {
+		memcpy(tls + tls_init_offset, tls_init, tls_init_size);
+		memset(tls + tls_init_offset + tls_init_size,
+		    0, tls_static_space - tls_init_size);
+	}
+
+	return tls;
+#else
+	return (0);
+#endif
+}
+
+#endif
+
+#ifdef TLS_VARIANT_II
+
+/*
+ * Free Static TLS using the Variant II method.
+ */
+#pragma weak _rtld_free_tls
+void
+_rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign)
+{
+#ifndef PIC
+	size_t size;
+	Elf_Addr* dtv;
+	Elf_Addr tlsstart, tlsend;
+
+	/*
+	 * Figure out the size of the initial TLS block so that we can
+	 * find stuff which ___tls_get_addr() allocated dynamically.
+	 */
+	size = round(tls_static_space, tcbalign);
+
+	dtv = ((Elf_Addr**)tcb)[1];
+	tlsend = (Elf_Addr) tcb;
+	tlsstart = tlsend - size;
+	free((void*) tlsstart);
+	free(dtv);
+#endif
+}
+
+#pragma weak _rtld_allocate_tls
+/*
+ * Allocate Static TLS using the Variant II method.
+ */
+void *
+_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign)
+{
+#ifndef PIC
+	size_t size;
+	char *tls;
+	Elf_Addr *dtv;
+	Elf_Addr segbase, oldsegbase;
+
+	size = round(tls_static_space, tcbalign);
+
+	assert(tcbsize >= 2*sizeof(Elf_Addr));
+	tls = malloc(size + tcbsize);
+	dtv = malloc(3 * sizeof(Elf_Addr));
+
+	segbase = (Elf_Addr)(tls + size);
+	((Elf_Addr*)segbase)[0] = segbase;
+	((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv;
+
+	dtv[0] = 1;
+	dtv[1] = 1;
+	dtv[2] = segbase - tls_static_space;
+
+	if (oldtls) {
+		/*
+		 * Copy the static TLS block over whole.
+		 */
+		oldsegbase = (Elf_Addr) oldtls;
+		memcpy((void *)(segbase - tls_static_space),
+		    (const void *)(oldsegbase - tls_static_space),
+		    tls_static_space);
+
+		/*
+		 * We assume that this block was the one we created with
+		 * allocate_initial_tls().
+		 */
+		_rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr));
+	} else {
+		memcpy((void *)(segbase - tls_static_space),
+		    tls_init, tls_init_size);
+		memset((void *)(segbase - tls_static_space + tls_init_size),
+		    0, tls_static_space - tls_init_size);
+	}
+
+	return (void*) segbase;
+#else
+	return (0);
+#endif
+}
+
+#endif /* TLS_VARIANT_II */
+
+extern char **environ;
+
+void
+_init_tls()
+{
+#ifndef PIC
+	Elf_Addr *sp;
+	Elf_Auxinfo *aux, *auxp;
+	Elf_Phdr *phdr;
+	size_t phent, phnum;
+	int i;
+	void *tls;
+
+	sp = (Elf_Addr *) environ;
+	while (*sp++ != 0)
+		;
+	aux = (Elf_Auxinfo *) sp;
+	phdr = 0;
+	phent = phnum = 0;
+	for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {
+		switch (auxp->a_type) {
+		case AT_PHDR:
+			phdr = auxp->a_un.a_ptr;
+			break;
+
+		case AT_PHENT:
+			phent = auxp->a_un.a_val;
+			break;
+
+		case AT_PHNUM:
+			phnum = auxp->a_un.a_val;
+			break;
+		}
+	}
+	if (phdr == 0 || phent != sizeof(Elf_Phdr) || phnum == 0)
+		return;
+
+	for (i = 0; (unsigned)i < phnum; i++) {
+		if (phdr[i].p_type == PT_TLS) {
+#ifdef TLS_VARIANT_I
+			tls_static_space = round(2*sizeof(Elf_Addr),
+			    phdr[i].p_align) + phdr[i].p_memsz;
+			tls_init_offset = round(2*sizeof(Elf_Addr),
+			    phdr[i].p_align);
+#else			    
+			tls_static_space = round(phdr[i].p_memsz,
+			    phdr[i].p_align);
+#endif
+			tls_init_size = phdr[i].p_filesz;
+			tls_init = (void*) phdr[i].p_vaddr;
+		}
+	}
+
+	tls = _rtld_allocate_tls(NULL, 2*sizeof(Elf_Addr),
+	    sizeof(Elf_Addr));
+
+	_set_tp(tls, 2 * sizeof(Elf_Addr));
+#endif
+}
--- /dev/null	2005-03-05 14:01:56.000000000 +0000
+++ i386/gen/_set_tp.c	2005-03-05 14:51:07.000000000 +0000
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2005 David Xu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly$
+ */
+
+#include <sys/tls.h>
+
+#include "libc_private.h"
+
+void
+_set_tp(void *tp, int size)
+{
+	struct tls_info t;
+	int seg;
+
+	t.base = tp;
+	t.size = size;
+	seg = sys_set_tls_area(0, &t, sizeof(t));
+	__asm __volatile("movl %0, %%gs" : : "r" (seg));
+}
Index: arch/i386/i386/pthread_md.c
===================================================================
RCS file: /cvs/src/lib/libthread_xu/arch/i386/i386/pthread_md.c,v
retrieving revision 1.3
diff -u -r1.3 pthread_md.c
--- arch/i386/i386/pthread_md.c	22 Feb 2005 14:56:22 -0000	1.3
+++ arch/i386/i386/pthread_md.c	5 Mar 2005 07:03:14 -0000
@@ -31,19 +31,25 @@
 #include <sys/tls.h>
 #include <stdlib.h>
 
+#include "rtld_tls.h"
 #include "pthread_md.h"
 
 struct tcb *
 _tcb_ctor(struct pthread *thread, int initial)
 {
 	struct tcb *tcb;
+	void *oldtls;
 
-	if ((tcb = malloc(sizeof(struct tcb))) == NULL)
-		return (NULL);
-	tcb->tcb_self = tcb;
-	tcb->tcb_dtv = NULL;
-	tcb->tcb_thread = thread;
-	tcb->tcb_seg = -1;
+	if (initial)
+		__asm __volatile("movl %%gs:0, %0" : "=r" (oldtls));
+	else
+		oldtls = NULL;
+
+	tcb = _rtld_allocate_tls(oldtls, sizeof(struct tcb), 16);
+	if (tcb) {
+		tcb->tcb_thread = thread;
+		tcb->tcb_seg = -1;
+	}
 	return (tcb);
 }
 
@@ -62,5 +68,5 @@
 void
 _tcb_dtor(struct tcb *tcb)
 {
-	free(tcb);
+	_rtld_free_tls(tcb, sizeof(struct tcb), 16);
 }
#include <stdio.h>
#include <pthread.h>

int __thread i;

void *f(void *a)
{
	for (i = 9; i >=0; --i) {
		printf("%d\n", i);
		sleep(1);
	}
	return (0);
}

void *g(void *a)
{
	for (i = 0; i <= 9; ++i) {
		printf("%d\n", i);
		sleep(1);
	}
	return (0);
}

int main()
{
	pthread_t td1, td2;

	pthread_create(&td1, NULL, f, NULL);
	pthread_create(&td2, NULL, g, NULL);
	pthread_join(td1, NULL);
	pthread_join(td2, NULL);
	return (0);
}




More information about the Kernel mailing list