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