diff options
author | Elliott Hughes <enh@google.com> | 2014-05-09 14:42:16 -0700 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2014-05-09 17:06:51 -0700 |
commit | 0d236aa3f1e6d31b0c729448ae9d3ed1cad23fb4 (patch) | |
tree | 1370d62648bdbe2be69259ed593274d23851c8c6 /libc/arch-x86/bionic/__set_tls.c | |
parent | 8fa5c2528c04df00f9f9a3dc4047ae88d3f13524 (diff) | |
download | android_bionic-0d236aa3f1e6d31b0c729448ae9d3ed1cad23fb4.tar.gz android_bionic-0d236aa3f1e6d31b0c729448ae9d3ed1cad23fb4.tar.bz2 android_bionic-0d236aa3f1e6d31b0c729448ae9d3ed1cad23fb4.zip |
Align the child stack in clone(2).
Also let clone(2) set the TLS for x86.
Also ensure we initialize the TLS before we clone(2) for all architectures.
Change-Id: Ie5fa4466e1c9ee116a281dfedef574c5ba60c0b5
Diffstat (limited to 'libc/arch-x86/bionic/__set_tls.c')
-rw-r--r-- | libc/arch-x86/bionic/__set_tls.c | 99 |
1 files changed, 36 insertions, 63 deletions
diff --git a/libc/arch-x86/bionic/__set_tls.c b/libc/arch-x86/bionic/__set_tls.c index 7ed4b0152..722ec6f6c 100644 --- a/libc/arch-x86/bionic/__set_tls.c +++ b/libc/arch-x86/bionic/__set_tls.c @@ -25,77 +25,50 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include <pthread.h> - - -struct user_desc { - unsigned int entry_number; - unsigned long base_addr; - unsigned int limit; - unsigned int seg_32bit:1; - unsigned int contents:2; - unsigned int read_exec_only:1; - unsigned int limit_in_pages:1; - unsigned int seg_not_present:1; - unsigned int useable:1; - unsigned int empty:25; -}; - -extern int __set_thread_area(struct user_desc *u_info); - -/* the following can't be const, since the first call will - * update the 'entry_number' field - */ -static struct user_desc _tls_desc = -{ - -1, - 0, - 0x1000, - 1, - 0, - 0, - 1, - 0, - 1, - 0 -}; -static pthread_mutex_t _tls_desc_lock = PTHREAD_MUTEX_INITIALIZER; +#include <limits.h> +#include <pthread.h> +#include <stdbool.h> -struct _thread_area_head { - void *self; -}; +#include <asm/ldt.h> -/* we implement thread local storage through the gs: segment descriptor - * we create a segment descriptor for the tls - */ -int __set_tls(void *ptr) -{ - int rc, segment; +extern int __set_thread_area(struct user_desc*); - pthread_mutex_lock(&_tls_desc_lock); - _tls_desc.base_addr = (unsigned long)ptr; +__LIBC_HIDDEN__ void __init_user_desc(struct user_desc* result, bool allocate, void* base_addr) { + if (allocate) { + // Let the kernel choose. + result->entry_number = -1; + } else { + // Get the existing entry number from %gs. + uint32_t gs; + __asm__ __volatile__("movw %%gs, %w0" : "=q"(gs) /*output*/); + result->entry_number = (gs & 0xffff) >> 3; + } - /* We also need to write the location of the tls to ptr[0] */ - ((struct _thread_area_head *)ptr)->self = ptr; + result->base_addr = (uintptr_t) base_addr; - rc = __set_thread_area( &_tls_desc ); - if (rc != 0) - { - /* could not set thread local area */ - pthread_mutex_unlock(&_tls_desc_lock); - return -1; - } + result->limit = PAGE_SIZE; - /* this weird computation comes from GLibc */ - segment = _tls_desc.entry_number*8 + 3; - asm __volatile__ ( - " movw %w0, %%gs" :: "q"(segment) - ); - pthread_mutex_unlock(&_tls_desc_lock); - - return 0; + result->seg_32bit = 1; + result->contents = MODIFY_LDT_CONTENTS_DATA; + result->read_exec_only = 0; + result->limit_in_pages = 1; + result->seg_not_present = 0; + result->useable = 1; } +int __set_tls(void* ptr) { + struct user_desc tls_descriptor; + __init_user_desc(&tls_descriptor, true, ptr); + int rc = __set_thread_area(&tls_descriptor); + if (rc != -1) { + // Change %gs to be new GDT entry. + uint16_t table_indicator = 0; // GDT + uint16_t rpl = 3; // Requested privilege level + uint16_t selector = (tls_descriptor.entry_number << 3) | table_indicator | rpl; + __asm__ __volatile__("movw %w0, %%gs" : /*output*/ : "q"(selector) /*input*/ : /*clobber*/); + } + return rc; +} |