TLS support --- csu/libc-start.c | 2 + hurd/hurdfault.c | 2 + hurd/hurdsig.c | 4 +- mach/mach.h | 3 + mach/setup-thread.c | 30 +++++++++++++++- ports/sysdeps/mach/alpha/thread_state.h | 1 sysdeps/generic/thread_state.h | 1 sysdeps/mach/hurd/bits/libc-lock.h | 3 + sysdeps/mach/hurd/bits/libc-tsd.h | 35 ------------------ sysdeps/mach/hurd/fork.c | 7 +++ sysdeps/mach/hurd/i386/init-first.c | 60 +++++++++++++++++++------------- sysdeps/mach/hurd/i386/tls.h | 53 ++++++++++++++++++++++------ sysdeps/mach/hurd/i386/tlsdesc.sym | 17 +++++++++ sysdeps/mach/hurd/i386/trampoline.c | 2 - sysdeps/mach/hurd/profil.c | 2 + sysdeps/mach/hurd/setitimer.c | 3 + sysdeps/mach/hurd/tls.h | 2 + sysdeps/mach/i386/thread_state.h | 11 +++++ sysdeps/mach/powerpc/thread_state.h | 1 sysdeps/mach/thread_state.h | 3 + 20 files changed, 167 insertions(+), 75 deletions(-) --- a/csu/libc-start.c +++ b/csu/libc-start.c @@ -134,10 +134,12 @@ } # endif +#ifndef __GNU__ /* Initialize the thread library at least a bit since the libgcc functions are using thread functions if these are available and we need to setup errno. */ __pthread_initialize_minimal (); +#endif /* Set up the stack checker's canary. */ uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random); --- a/hurd/hurdfault.c +++ b/hurd/hurdfault.c @@ -206,6 +206,8 @@ /* This state will be restored when we fault. It runs the function above. */ memset (&state, 0, sizeof state); + + MACHINE_THREAD_STATE_FIX_NEW (&state); MACHINE_THREAD_STATE_SET_PC (&state, faulted); MACHINE_THREAD_STATE_SET_SP (&state, faultstack, sizeof faultstack); --- a/hurd/hurdsig.c +++ b/hurd/hurdsig.c @@ -1268,6 +1268,8 @@ (vm_address_t *) &__hurd_sigthread_stack_base, &stacksize); assert_perror (err); + err = __mach_setup_tls (_hurd_msgport_thread); + assert_perror (err); __hurd_sigthread_stack_end = __hurd_sigthread_stack_base + stacksize; __hurd_sigthread_variables = @@ -1276,8 +1278,6 @@ __libc_fatal ("hurd: Can't allocate threadvars for signal thread\n"); memset (__hurd_sigthread_variables, 0, __hurd_threadvar_max * sizeof (unsigned long int)); - __hurd_sigthread_variables[_HURD_THREADVAR_LOCALE] - = (unsigned long int) &_nl_global_locale; /* Reinitialize the MiG support routines so they will use a per-thread variable for the cached reply port. */ --- a/mach/mach.h +++ b/mach/mach.h @@ -101,5 +101,8 @@ vm_address_t *stack_base, vm_size_t *stack_size); +/* Give THREAD a TLS area. */ +kern_return_t __mach_setup_tls (thread_t thread); +kern_return_t mach_setup_tls (thread_t thread); #endif /* mach.h */ --- a/mach/setup-thread.c +++ b/mach/setup-thread.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "sysdep.h" /* Defines stack direction. */ #define STACK_SIZE (16 * 1024 * 1024) /* 16MB, arbitrary. */ @@ -73,8 +74,35 @@ if (error = __vm_protect (task, stack, __vm_page_size, 0, VM_PROT_NONE)) return error; - return __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR, + return __thread_set_state (thread, MACHINE_NEW_THREAD_STATE_FLAVOR, (natural_t *) &ts, tssize); } weak_alias (__mach_setup_thread, mach_setup_thread) + +/* Give THREAD a TLS area. */ +kern_return_t +__mach_setup_tls (thread_t thread) +{ + kern_return_t error; + struct machine_thread_state ts; + mach_msg_type_number_t tssize = MACHINE_THREAD_STATE_COUNT; + tcbhead_t *tcb; + + if (error = __thread_get_state (thread, MACHINE_THREAD_STATE_FLAVOR, + (natural_t *) &ts, &tssize)) + return error; + assert (tssize == MACHINE_THREAD_STATE_COUNT); + + tcb = _dl_allocate_tls(NULL); + if (!tcb) + return KERN_RESOURCE_SHORTAGE; + + _hurd_tls_new(thread, &ts, tcb); + + error = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR, + (natural_t *) &ts, tssize); + return error; +} + +weak_alias (__mach_setup_tls, mach_setup_tls) --- a/sysdeps/generic/thread_state.h +++ b/sysdeps/generic/thread_state.h @@ -23,6 +23,7 @@ /* Replace with "i386" or "mips" or whatever. */ +#define MACHINE_NEW_THREAD_STATE_FLAVOR _NEW_THREAD_STATE #define MACHINE_THREAD_STATE_FLAVOR _THREAD_STATE #define MACHINE_THREAD_STATE_COUNT _THREAD_STATE_COUNT --- a/ports/sysdeps/mach/alpha/thread_state.h +++ b/ports/sysdeps/mach/alpha/thread_state.h @@ -19,6 +19,7 @@ #include +#define MACHINE_NEW_THREAD_STATE_FLAVOR ALPHA_THREAD_STATE #define MACHINE_THREAD_STATE_FLAVOR ALPHA_THREAD_STATE #define MACHINE_THREAD_STATE_COUNT ALPHA_THREAD_STATE_COUNT --- a/sysdeps/mach/hurd/bits/libc-tsd.h +++ /dev/null @@ -1,35 +0,0 @@ -/* libc-internal interface for thread-specific data. Hurd version. - Copyright (C) 1998,2002,2008 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -#ifndef _BITS_LIBC_TSD_H -#define _BITS_LIBC_TSD_H 1 - -#include - -#define __libc_tsd_define(CLASS, TYPE, KEY) /* nothing, always have threadvars */ - -#define __libc_tsd_address(TYPE, KEY) \ - ((TYPE *) __hurd_threadvar_location (_HURD_THREADVAR_##KEY)) - -#define __libc_tsd_get(TYPE, KEY) \ - (*__libc_tsd_address (TYPE, KEY)) -#define __libc_tsd_set(TYPE, KEY, VALUE) \ - (*__libc_tsd_address (TYPE, KEY) = (VALUE)) - -#endif /* bits/libc-tsd.h */ --- a/sysdeps/mach/hurd/fork.c +++ b/sysdeps/mach/hurd/fork.c @@ -523,6 +523,11 @@ #endif MACHINE_THREAD_STATE_SET_PC (&state, (unsigned long int) _hurd_msgport_receive); + + /* Do special thread setup for TLS if needed. */ + if (err = _hurd_tls_fork (sigthread, _hurd_msgport_thread, &state)) + LOSE; + if (err = __thread_set_state (sigthread, MACHINE_THREAD_STATE_FLAVOR, (natural_t *) &state, statecount)) LOSE; @@ -533,7 +538,7 @@ _hurd_longjmp_thread_state (&state, env, 1); /* Do special thread setup for TLS if needed. */ - if (err = _hurd_tls_fork (thread, &state)) + if (err = _hurd_tls_fork (thread, __mach_thread_self (), &state)) LOSE; if (err = __thread_set_state (thread, MACHINE_THREAD_STATE_FLAVOR, --- a/sysdeps/mach/hurd/i386/init-first.c +++ b/sysdeps/mach/hurd/i386/init-first.c @@ -104,10 +104,6 @@ char **argv = &arg0; char **envp = &argv[argc + 1]; struct hurd_startup_data *d; -#ifndef SHARED - extern ElfW(Phdr) *_dl_phdr; - extern size_t _dl_phnum; -#endif while (*envp) ++envp; @@ -118,27 +114,9 @@ data block; the argument strings start there. */ if ((void *) d == argv[0]) { -#ifndef SHARED - /* We may need to see our own phdrs, e.g. for TLS setup. - Try the usual kludge to find the headers without help from - the exec server. */ - extern const void _start; - const ElfW(Ehdr) *const ehdr = &_start; - _dl_phdr = (ElfW(Phdr) *) ((const void *) ehdr + ehdr->e_phoff); - _dl_phnum = ehdr->e_phnum; - assert (ehdr->e_phentsize == sizeof (ElfW(Phdr))); -#endif return; } -#ifndef SHARED - __libc_enable_secure = d->flags & EXEC_SECURE; - - _dl_phdr = (ElfW(Phdr) *) d->phdr; - _dl_phnum = d->phdrsz / sizeof (ElfW(Phdr)); - assert (d->phdrsz % sizeof (ElfW(Phdr)) == 0); -#endif - _hurd_init_dtable = d->dtable; _hurd_init_dtablesize = d->dtablesize; @@ -172,13 +150,16 @@ char **envp = &argv[argc + 1]; struct hurd_startup_data *d; unsigned long int threadvars[_HURD_THREADVAR_MAX]; +#ifndef SHARED + extern ElfW(Phdr) *_dl_phdr; + extern size_t _dl_phnum; +#endif /* Provide temporary storage for thread-specific variables on the startup stack so the cthreads initialization code can use them for malloc et al, or so we can use malloc below for the real threadvars array. */ memset (threadvars, 0, sizeof threadvars); - threadvars[_HURD_THREADVAR_LOCALE] = (unsigned long int) &_nl_global_locale; __hurd_threadvar_stack_offset = (unsigned long int) threadvars; /* Since the cthreads initialization code uses malloc, and the @@ -192,6 +173,39 @@ ++envp; d = (void *) ++envp; + /* If we are the bootstrap task started by the kernel, + then after the environment pointers there is no Hurd + data block; the argument strings start there. */ + if ((void *) d == argv[0]) + { +#ifndef SHARED + /* We may need to see our own phdrs, e.g. for TLS setup. + Try the usual kludge to find the headers without help from + the exec server. */ + extern const void __executable_start; + const ElfW(Ehdr) *const ehdr = &__executable_start; + _dl_phdr = (ElfW(Phdr) *) ((const void *) ehdr + ehdr->e_phoff); + _dl_phnum = ehdr->e_phnum; + assert (ehdr->e_phentsize == sizeof (ElfW(Phdr))); +#endif + } + else + { +#ifndef SHARED + __libc_enable_secure = d->flags & EXEC_SECURE; + + _dl_phdr = (ElfW(Phdr) *) d->phdr; + _dl_phnum = d->phdrsz / sizeof (ElfW(Phdr)); + assert (d->phdrsz % sizeof (ElfW(Phdr)) == 0); +#endif + } + +#ifndef SHARED + /* We need to setup TLS before starting sigthread */ + extern void __pthread_initialize_minimal(void); + __pthread_initialize_minimal(); +#endif + /* The user might have defined a value for this, to get more variables. Otherwise it will be zero on startup. We must make sure it is set properly before before cthreads initialization, so cthreads can know --- a/sysdeps/mach/hurd/i386/tls.h +++ b/sysdeps/mach/hurd/i386/tls.h @@ -31,6 +31,8 @@ # ifndef __ASSEMBLER__ +#include + /* Use i386-specific RPCs to arrange that %gs segment register prefix addresses the TCB in each thread. */ # include @@ -72,7 +72,7 @@ _hurd_tls_init (tcbhead_t *tcb, int secondcall) /* Get the first available selector. */ int sel = -1; - error_t err = __i386_set_gdt (tcb->self, &sel, desc); + kern_return_t err = __i386_set_gdt (tcb->self, &sel, desc); if (err == MIG_BAD_ID) { /* Old kernel, use a per-thread LDT. */ @@ -96,16 +96,16 @@ _hurd_tls_init (tcbhead_t *tcb, int secondcall) /* Fetch the selector set by the first call. */ int sel; asm ("mov %%gs, %w0" : "=q" (sel) : "0" (0)); - if (__builtin_expect (sel, 0x50) & 4) /* LDT selector */ + if (__builtin_expect (sel, 0x48) & 4) /* LDT selector */ { - error_t err = __i386_set_ldt (tcb->self, sel, &desc, 1); + kern_return_t err = __i386_set_ldt (tcb->self, sel, &desc, 1); assert_perror (err); if (err) return "i386_set_ldt failed"; } else { - error_t err = __i386_set_gdt (tcb->self, &sel, desc); + kern_return_t err = __i386_set_gdt (tcb->self, &sel, desc); assert_perror (err); if (err) return "i386_set_gdt failed"; @@ -142,9 +142,40 @@ _hurd_tls_init (tcbhead_t *tcb, int secondcall) #include -/* Set up TLS in the new thread of a fork child, copying from our own. */ -static inline error_t __attribute__ ((unused)) -_hurd_tls_fork (thread_t child, struct i386_thread_state *state) +/* Set up TLS in the new thread of a fork child, copying from the original. */ +static inline kern_return_t __attribute__ ((unused)) +_hurd_tls_fork (thread_t child, thread_t orig, struct i386_thread_state *state) +{ + /* Fetch the selector set by _hurd_tls_init. */ + int sel; + asm ("mov %%gs, %w0" : "=q" (sel) : "0" (0)); + if (sel == state->ds) /* _hurd_tls_init was never called. */ + return 0; + + struct descriptor desc, *_desc = &desc; + int err; + unsigned int count; + + if (__builtin_expect (sel, 0x48) & 4) /* LDT selector */ + err = __i386_get_ldt (orig, sel, 1, &_desc, &count); + else + err = __i386_get_gdt (orig, sel, &desc); + + assert_perror (err); + if (err) + return err; + + if (__builtin_expect (sel, 0x48) & 4) /* LDT selector */ + err = __i386_set_ldt (child, sel, &desc, 1); + else + err = __i386_set_gdt (child, &sel, desc); + + state->gs = sel; + return err; +} + +static inline kern_return_t __attribute__ ((unused)) +_hurd_tls_new (thread_t child, struct i386_thread_state *state, tcbhead_t *tcb) { /* Fetch the selector set by _hurd_tls_init. */ int sel; @@ -152,11 +183,13 @@ _hurd_tls_fork (thread_t child, struct i386_thread_state *state) if (sel == state->ds) /* _hurd_tls_init was never called. */ return 0; - tcbhead_t *const tcb = THREAD_SELF; HURD_TLS_DESC_DECL (desc, tcb); - error_t err; + kern_return_t err; + + tcb->tcb = tcb; + tcb->self = child; - if (__builtin_expect (sel, 0x50) & 4) /* LDT selector */ + if (__builtin_expect (sel, 0x48) & 4) /* LDT selector */ err = __i386_set_ldt (child, sel, &desc, 1); else err = __i386_set_gdt (child, &sel, desc); --- a/sysdeps/mach/hurd/i386/trampoline.c +++ b/sysdeps/mach/hurd/i386/trampoline.c @@ -65,7 +65,7 @@ sizeof (state->basic)); memcpy (&state->fpu, &ss->context->sc_i386_float_state, sizeof (state->fpu)); - state->set |= (1 << i386_THREAD_STATE) | (1 << i386_FLOAT_STATE); + state->set |= (1 << i386_REGS_SEGS_STATE) | (1 << i386_FLOAT_STATE); } } --- a/sysdeps/mach/hurd/profil.c +++ b/sysdeps/mach/hurd/profil.c @@ -69,6 +69,8 @@ if (! err) err = __mach_setup_thread (__mach_task_self (), profile_thread, &profile_waiter, NULL, NULL); + if (! err) + err = __mach_setup_tls(profile_thread); } else err = 0; --- a/sysdeps/mach/hurd/setitimer.c +++ b/sysdeps/mach/hurd/setitimer.c @@ -223,11 +223,12 @@ return __hurd_fail (err); _hurd_itimer_thread_stack_base = 0; /* Anywhere. */ _hurd_itimer_thread_stack_size = __vm_page_size; /* Small stack. */ - if (err = __mach_setup_thread (__mach_task_self (), + if ((err = __mach_setup_thread (__mach_task_self (), _hurd_itimer_thread, &timer_thread, &_hurd_itimer_thread_stack_base, &_hurd_itimer_thread_stack_size)) + || (err = __mach_setup_tls(_hurd_itimer_thread))) { __thread_terminate (_hurd_itimer_thread); _hurd_itimer_thread = MACH_PORT_NULL; --- a/sysdeps/mach/i386/thread_state.h +++ b/sysdeps/mach/i386/thread_state.h @@ -19,7 +19,8 @@ #include -#define MACHINE_THREAD_STATE_FLAVOR i386_THREAD_STATE +#define MACHINE_NEW_THREAD_STATE_FLAVOR i386_THREAD_STATE +#define MACHINE_THREAD_STATE_FLAVOR i386_REGS_SEGS_STATE #define MACHINE_THREAD_STATE_COUNT i386_THREAD_STATE_COUNT #define machine_thread_state i386_thread_state @@ -28,6 +29,14 @@ #define SP uesp #define SYSRETURN eax +#define MACHINE_THREAD_STATE_FIX_NEW(ts) do { \ + asm ("mov %%cs, %w0" : "=q" ((ts)->cs)); \ + asm ("mov %%ds, %w0" : "=q" ((ts)->ds)); \ + asm ("mov %%es, %w0" : "=q" ((ts)->es)); \ + asm ("mov %%fs, %w0" : "=q" ((ts)->fs)); \ + asm ("mov %%gs, %w0" : "=q" ((ts)->gs)); \ +} while(0) + struct machine_thread_all_state { int set; /* Mask of bits (1 << FLAVOR). */ --- a/sysdeps/mach/powerpc/thread_state.h +++ b/sysdeps/mach/powerpc/thread_state.h @@ -19,6 +19,7 @@ #include +#define MACHINE_NEW_THREAD_STATE_FLAVOR PPC_THREAD_STATE #define MACHINE_THREAD_STATE_FLAVOR PPC_THREAD_STATE #define MACHINE_THREAD_STATE_COUNT PPC_THREAD_STATE_COUNT --- a/sysdeps/mach/thread_state.h +++ b/sysdeps/mach/thread_state.h @@ -38,6 +38,9 @@ ((ts)->SP = (unsigned long int) (stack) + (size)) #endif #endif +#ifndef MACHINE_THREAD_STATE_FIX_NEW +#define MACHINE_THREAD_STATE_FIX_NEW(ts) +#endif /* These functions are of use in machine-dependent signal trampoline implementations. */ --- a/sysdeps/mach/hurd/bits/libc-lock.h +++ b/sysdeps/mach/hurd/bits/libc-lock.h @@ -21,6 +21,9 @@ #define _BITS_LIBC_LOCK_H 1 #if (_LIBC - 0) || (_CTHREADS_ - 0) +#if (_LIBC - 0) +#include +#endif #include #include --- a/sysdeps/mach/hurd/tls.h +++ b/sysdeps/mach/hurd/tls.h @@ -23,7 +23,9 @@ #if defined HAVE_TLS_SUPPORT && !defined __ASSEMBLER__ # include +# include # include +# include # include # include --- /dev/null +++ b/sysdeps/mach/hurd/i386/tlsdesc.sym @@ -0,0 +1,17 @@ +#include +#include +#include +#include +#include + +-- + +-- Abuse tls.h macros to derive offsets relative to the thread register. + +DTV_OFFSET offsetof(tcbhead_t, dtv) + +TLSDESC_ARG offsetof(struct tlsdesc, arg) + +TLSDESC_GEN_COUNT offsetof(struct tlsdesc_dynamic_arg, gen_count) +TLSDESC_MODID offsetof(struct tlsdesc_dynamic_arg, tlsinfo.ti_module) +TLSDESC_MODOFF offsetof(struct tlsdesc_dynamic_arg, tlsinfo.ti_offset)