aboutsummaryrefslogtreecommitdiffstats
path: root/libc/arch-x86/bionic/__set_tls.c
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2014-05-09 14:42:16 -0700
committerElliott Hughes <enh@google.com>2014-05-09 17:06:51 -0700
commit0d236aa3f1e6d31b0c729448ae9d3ed1cad23fb4 (patch)
tree1370d62648bdbe2be69259ed593274d23851c8c6 /libc/arch-x86/bionic/__set_tls.c
parent8fa5c2528c04df00f9f9a3dc4047ae88d3f13524 (diff)
downloadandroid_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.c99
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;
+}