Home | History | Annotate | Download | only in bionic
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in
     12  *    the documentation and/or other materials provided with the
     13  *    distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 #include <pthread.h>
     29 
     30 
     31 struct user_desc {
     32     unsigned int    entry_number;
     33     unsigned long   base_addr;
     34     unsigned int    limit;
     35     unsigned int    seg_32bit:1;
     36     unsigned int    contents:2;
     37     unsigned int    read_exec_only:1;
     38     unsigned int    limit_in_pages:1;
     39     unsigned int    seg_not_present:1;
     40     unsigned int    useable:1;
     41     unsigned int    empty:25;
     42 };
     43 
     44 extern int __set_thread_area(struct user_desc *u_info);
     45 
     46 /* the following can't be const, since the first call will
     47  * update the 'entry_number' field
     48  */
     49 static struct user_desc  _tls_desc =
     50 {
     51     -1,
     52     0,
     53     0x1000,
     54     1,
     55     0,
     56     0,
     57     1,
     58     0,
     59     1,
     60     0
     61 };
     62 
     63 static pthread_mutex_t  _tls_desc_lock = PTHREAD_MUTEX_INITIALIZER;
     64 
     65 struct _thread_area_head {
     66     void *self;
     67 };
     68 
     69 /* we implement thread local storage through the gs: segment descriptor
     70  * we create a segment descriptor for the tls
     71  */
     72 int __set_tls(void *ptr)
     73 {
     74     int   rc, segment;
     75 
     76     pthread_mutex_lock(&_tls_desc_lock);
     77     _tls_desc.base_addr = (unsigned long)ptr;
     78 
     79     /* We also need to write the location of the tls to ptr[0] */
     80     ((struct _thread_area_head *)ptr)->self = ptr;
     81 
     82     rc = __set_thread_area( &_tls_desc );
     83     if (rc != 0)
     84     {
     85         /* could not set thread local area */
     86         pthread_mutex_unlock(&_tls_desc_lock);
     87         return -1;
     88     }
     89 
     90     /* this weird computation comes from GLibc */
     91     segment = _tls_desc.entry_number*8 + 3;
     92     asm __volatile__ (
     93         "   movw %w0, %%gs" :: "q"(segment)
     94     );
     95     pthread_mutex_unlock(&_tls_desc_lock);
     96 
     97     return 0;
     98 }
     99 
    100 
    101 
    102