diff options
Diffstat (limited to 'sysdeps/mach/hurd/i386/tls.h')
-rw-r--r-- | sysdeps/mach/hurd/i386/tls.h | 147 |
1 files changed, 128 insertions, 19 deletions
diff --git a/sysdeps/mach/hurd/i386/tls.h b/sysdeps/mach/hurd/i386/tls.h index 3f84300206..00b611e903 100644 --- a/sysdeps/mach/hurd/i386/tls.h +++ b/sysdeps/mach/hurd/i386/tls.h @@ -23,6 +23,44 @@ /* Some things really need not be machine-dependent. */ #include <sysdeps/mach/hurd/tls.h> + +#ifndef __ASSEMBLER__ +/* Type for the dtv. */ +typedef union dtv +{ + size_t counter; + struct + { + void *val; + bool is_static; + } pointer; +} dtv_t; + + +/* Type of the TCB. */ +typedef struct +{ + void *tcb; /* Points to this structure. */ + dtv_t *dtv; /* Vector of pointers to TLS data. */ + thread_t self; /* This thread's control port. */ + int multiple_threads; + uintptr_t sysinfo; + uintptr_t stack_guard; + uintptr_t pointer_guard; + int gscope_flag; + int private_futex; + /* Reservation of some values for the TM ABI. */ + void *__private_tm[4]; + /* GCC split stack support. */ + void *__private_ss; + + /* Keep this field last */ + mach_port_t reply_port; /* This thread's reply port. */ + struct hurd_sigstate *_hurd_sigstate; +} tcbhead_t; +#endif + + /* The TCB can have any size and the memory following the address the thread pointer points to is unspecified. Allocate the TCB there. */ #define TLS_TCB_AT_TP 1 @@ -37,6 +75,8 @@ # define __i386_set_gdt(thr, sel, desc) ((void) (thr), (void) (sel), (void) (desc), MIG_BAD_ID) # endif +#define __i386_selector_is_ldt(sel) (!!((sel) & 4)) + # include <errno.h> # include <assert.h> @@ -53,11 +93,24 @@ | (((unsigned int) (tcb)) & 0xff000000) /* base 24..31 */ \ } +# define HURD_DESC_TLS(desc) \ + ({ \ + (tcbhead_t *) ( (desc->low_word >> 16) \ + | ((desc->high_word & 0xff) << 16) \ + | (desc->high_word & 0xff000000) \ + );}) + +#define __LIBC_NO_TLS() \ + ({ unsigned short ds, gs; \ + asm ("movw %%ds,%w0; movw %%gs,%w1" : "=q" (ds), "=q" (gs)); \ + ds == gs; }) static inline const char * __attribute__ ((unused)) _hurd_tls_init (tcbhead_t *tcb, int secondcall) { HURD_TLS_DESC_DECL (desc, tcb); + thread_t self = __mach_thread_self (); + const char *msg = NULL; if (!secondcall) { @@ -65,25 +118,26 @@ _hurd_tls_init (tcbhead_t *tcb, int secondcall) from the TLS point of view. */ tcb->tcb = tcb; - /* Cache our thread port. */ - tcb->self = __mach_thread_self (); - /* 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 (self, &sel, desc); if (err == MIG_BAD_ID) { /* Old kernel, use a per-thread LDT. */ sel = 0x27; - err = __i386_set_ldt (tcb->self, sel, &desc, 1); + err = __i386_set_ldt (self, sel, &desc, 1); assert_perror (err); if (err) - return "i386_set_ldt failed"; + { + msg = "i386_set_ldt failed"; + goto out; + } } else if (err) { assert_perror (err); /* Separate from above with different line #. */ - return "i386_set_gdt failed"; + msg = "i386_set_gdt failed"; + goto out; } /* Now install the new selector. */ @@ -94,23 +148,31 @@ _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 (__glibc_unlikely (__i386_selector_is_ldt(sel))) { - error_t err = __i386_set_ldt (tcb->self, sel, &desc, 1); + kern_return_t err = __i386_set_ldt (self, sel, &desc, 1); assert_perror (err); if (err) - return "i386_set_ldt failed"; + { + msg = "i386_set_ldt failed"; + goto out; + } } else { - error_t err = __i386_set_gdt (tcb->self, &sel, desc); + kern_return_t err = __i386_set_gdt (self, &sel, desc); assert_perror (err); if (err) - return "i386_set_gdt failed"; + { + msg = "i386_set_gdt failed"; + goto out; + } } } - return 0; +out: + __mach_port_deallocate (__mach_task_self (), self); + return msg; } /* Code to initially initialize the thread pointer. This might need @@ -126,6 +188,20 @@ _hurd_tls_init (tcbhead_t *tcb, int secondcall) : "i" (offsetof (tcbhead_t, tcb))); \ __tcb;}) +/* Return the TCB address of a thread given its state. */ +# define THREAD_TCB(thread, thread_state) \ + ({ int __sel = (thread_state)->basic.gs; \ + struct descriptor __desc, *___desc = &__desc; \ + unsigned int __count = 1; \ + kern_return_t __err; \ + if (__builtin_expect (__sel, 0x48) & 4) /* LDT selector */ \ + __err = __i386_get_ldt ((thread), __sel, 1, &___desc, &__count); \ + else \ + __err = __i386_get_gdt ((thread), __sel, &__desc); \ + assert_perror (__err); \ + assert (__count == 1); \ + HURD_DESC_TLS(___desc);}) + /* Install new dtv for current thread. */ # define INSTALL_NEW_DTV(dtvp) \ ({ asm volatile ("movl %0,%%gs:%P1" \ @@ -139,9 +215,40 @@ _hurd_tls_init (tcbhead_t *tcb, int secondcall) # include <mach/machine/thread_status.h> -/* 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; + kern_return_t err; + unsigned int count = 1; + + if (__glibc_unlikely (__i386_selector_is_ldt(sel))) + 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 (__glibc_unlikely (__i386_selector_is_ldt(sel))) + 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; @@ -149,11 +256,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 (__glibc_unlikely (__i386_selector_is_ldt(sel))) err = __i386_set_ldt (child, sel, &desc, 1); else err = __i386_set_gdt (child, &sel, desc); |