| /* ******************************************************************************* |
| * Copyright (c) 2010-2014 Google, Inc. All rights reserved. |
| * Copyright (c) 2011 Massachusetts Institute of Technology All rights reserved. |
| * Copyright (c) 2000-2010 VMware, Inc. All rights reserved. |
| * *******************************************************************************/ |
| |
| /* |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * * Neither the name of VMware, Inc. nor the names of its contributors may be |
| * used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| /* Copyright (c) 2003-2007 Determina Corp. */ |
| /* Copyright (c) 2001-2003 Massachusetts Institute of Technology */ |
| /* Copyright (c) 2000-2001 Hewlett-Packard Company */ |
| |
| /* |
| * tls_linux_x86.c - TLS support via x86 segments |
| */ |
| |
| #include "../globals.h" |
| #include "tls.h" |
| #include "include/syscall.h" /* our own local copy */ |
| |
| #ifdef X86 |
| # include "instr.h" /* for SEG_ constants */ |
| #endif |
| |
| #ifndef LINUX |
| # error Linux-only |
| #endif |
| |
| #include <asm/ldt.h> |
| |
| #ifdef X64 |
| /* Linux GDT layout in x86_64: |
| * #define GDT_ENTRY_TLS_MIN 12 |
| * #define GDT_ENTRY_TLS_MAX 14 |
| * #define GDT_ENTRY_TLS 1 |
| * TLS indexes for 64-bit, hardcode in arch_prctl |
| * #define FS_TLS 0 |
| * #define GS_TLS 1 |
| * #define GS_TLS_SEL ((GDT_ENTRY_TLS_MIN+GS_TLS)*8 + 3) |
| * #define FS_TLS_SEL ((GDT_ENTRY_TLS_MIN+FS_TLS)*8 + 3) |
| */ |
| # define FS_TLS 0 /* used in arch_prctl handling */ |
| # define GS_TLS 1 /* used in arch_prctl handling */ |
| #else |
| /* Linux GDT layout in x86_32 |
| * 6 - TLS segment #1 0x33 [ glibc's TLS segment ] |
| * 7 - TLS segment #2 0x3b [ Wine's %fs Win32 segment ] |
| * 8 - TLS segment #3 0x43 |
| * FS and GS is not hardcode. |
| */ |
| #endif |
| #define GDT_ENTRY_TLS_MIN_32 6 |
| #define GDT_ENTRY_TLS_MIN_64 12 |
| /* when x86-64 emulate i386, it still use 12-14, so using ifdef x64 |
| * cannot detect the right value. |
| * The actual value will be updated later in os_tls_app_seg_init. |
| */ |
| static uint gdt_entry_tls_min = IF_X64_ELSE(GDT_ENTRY_TLS_MIN_64, |
| GDT_ENTRY_TLS_MIN_32); |
| |
| static bool tls_global_init = false; |
| |
| /* GDT slot we use for set_thread_area. |
| * This depends on the kernel, not on the app! |
| */ |
| static int tls_gdt_index = -1; |
| /* GDT slot we use for private library TLS. */ |
| static int lib_tls_gdt_index = -1; |
| |
| #ifdef X64 |
| static bool tls_using_msr; |
| #endif |
| |
| tls_type_t tls_global_type; |
| |
| /* Indicates that on the next request for a GDT entry, we should return the GDT |
| * entry we stole for private library TLS. The entry index is in |
| * lib_tls_gdt_index. |
| * FIXME i#107: For total segment transparency, we can use the same approach |
| * with tls_gdt_index. |
| */ |
| bool return_stolen_lib_tls_gdt; |
| |
| #ifdef DEBUG |
| # define GDT_32BIT 8 /* 6=NPTL, 7=wine */ |
| # define GDT_64BIT 14 /* 12=NPTL, 13=wine */ |
| #endif |
| |
| static int |
| modify_ldt_syscall(int func, void *ptr, unsigned long bytecount) |
| { |
| return dynamorio_syscall(SYS_modify_ldt, 3, func, ptr, bytecount); |
| } |
| |
| /* reading ldt entries gives us the raw format, not struct modify_ldt_ldt_s */ |
| typedef struct { |
| unsigned int limit1500:16; |
| unsigned int base1500:16; |
| unsigned int base2316:8; |
| unsigned int type:4; |
| unsigned int not_system:1; |
| unsigned int privilege_level:2; |
| unsigned int seg_present:1; |
| unsigned int limit1916:4; |
| unsigned int custom:1; |
| unsigned int zero:1; |
| unsigned int seg_32bit:1; |
| unsigned int limit_in_pages:1; |
| unsigned int base3124:8; |
| } raw_ldt_entry_t; |
| |
| enum { |
| LDT_TYPE_CODE = 0x8, |
| LDT_TYPE_DOWN = 0x4, |
| LDT_TYPE_WRITE = 0x2, |
| LDT_TYPE_ACCESSED = 0x1, |
| }; |
| |
| #define LDT_BASE(ldt) (((ldt)->base3124<<24) | ((ldt)->base2316<<16) | (ldt)->base1500) |
| |
| #ifdef DEBUG |
| # if 0 /* not used */ |
| # ifdef X64 |
| /* Intel docs confusingly say that app descriptors are 8 bytes while |
| * system descriptors are 16; more likely all are 16, as linux kernel |
| * docs seem to assume. */ |
| # error NYI |
| # endif |
| static void |
| print_raw_ldt(raw_ldt_entry_t *ldt) |
| { |
| LOG(GLOBAL, LOG_ALL, 1, |
| "ldt @"PFX":\n", ldt); |
| LOG(GLOBAL, LOG_ALL, 1, |
| "\traw: "PFX" "PFX"\n", *((unsigned int *)ldt), *(1+(unsigned int *)ldt)); |
| LOG(GLOBAL, LOG_ALL, 1, |
| "\tbase: 0x%x\n", LDT_BASE(ldt)); |
| LOG(GLOBAL, LOG_ALL, 1, |
| "\tlimit: 0x%x\n", (ldt->limit1916<<16) | ldt->limit1500); |
| LOG(GLOBAL, LOG_ALL, 1, |
| "\tlimit_in_pages: 0x%x\n", ldt->limit_in_pages); |
| LOG(GLOBAL, LOG_ALL, 1, |
| "\tseg_32bit: 0x%x\n", ldt->seg_32bit); |
| LOG(GLOBAL, LOG_ALL, 1, |
| "\tseg_present: 0x%x\n", ldt->seg_present); |
| LOG(GLOBAL, LOG_ALL, 1, |
| "\tprivilege_level: 0x%x\n", ldt->privilege_level); |
| LOG(GLOBAL, LOG_ALL, 1, |
| "\tnot_system: 0x%x\n", ldt->not_system); |
| LOG(GLOBAL, LOG_ALL, 1, |
| "\ttype: 0x%x == %s%s%s%s\n", ldt->type, |
| (ldt->type & LDT_TYPE_CODE) ? "code":"data", |
| (ldt->type & LDT_TYPE_DOWN) ? " top-down":"", |
| (ldt->type & LDT_TYPE_WRITE) ? " W":" ", |
| (ldt->type & LDT_TYPE_ACCESSED) ? " accessed":""); |
| } |
| |
| static void |
| print_all_ldt(void) |
| { |
| int i, bytes; |
| /* can't fit 64K on our stack */ |
| raw_ldt_entry_t *ldt = global_heap_alloc(sizeof(raw_ldt_entry_t) * LDT_ENTRIES |
| HEAPACCT(ACCT_OTHER)); |
| /* make sure our struct size jives w/ ldt.h */ |
| ASSERT(sizeof(raw_ldt_entry_t) == LDT_ENTRY_SIZE); |
| memset(ldt, 0, sizeof(*ldt)); |
| bytes = modify_ldt_syscall(0, (void *)ldt, sizeof(raw_ldt_entry_t) * LDT_ENTRIES); |
| LOG(GLOBAL, LOG_ALL, 3, "read %d bytes, should == %d * %d\n", |
| bytes, sizeof(raw_ldt_entry_t), LDT_ENTRIES); |
| ASSERT(bytes == 0 /* no ldt entries */ || |
| bytes == sizeof(raw_ldt_entry_t) * LDT_ENTRIES); |
| for (i = 0; i < bytes/sizeof(raw_ldt_entry_t); i++) { |
| if (((ldt[i].base3124<<24) | (ldt[i].base2316<<16) | ldt[i].base1500) != 0) { |
| LOG(GLOBAL, LOG_ALL, 1, "ldt at index %d:\n", i); |
| print_raw_ldt(&ldt[i]); |
| } |
| } |
| global_heap_free(ldt, sizeof(raw_ldt_entry_t) * LDT_ENTRIES HEAPACCT(ACCT_OTHER)); |
| } |
| # endif /* #if 0 */ |
| #endif /* DEBUG */ |
| |
| #define LDT_ENTRIES_TO_CHECK 128 |
| |
| /* returns -1 if all indices are in use */ |
| static int |
| find_unused_ldt_index() |
| { |
| int i, bytes; |
| /* N.B.: we don't have 64K of stack for the full LDT_ENTRIES |
| * array, and I don't want to allocate a big array on the heap |
| * when it's very doubtful any more than a handful of these |
| * descriptors are actually in use |
| */ |
| raw_ldt_entry_t ldt[LDT_ENTRIES_TO_CHECK]; |
| ASSERT(LDT_ENTRIES_TO_CHECK < LDT_ENTRIES); |
| /* make sure our struct size jives w/ ldt.h */ |
| ASSERT(sizeof(raw_ldt_entry_t) == LDT_ENTRY_SIZE); |
| memset(ldt, 0, sizeof(*ldt)); |
| bytes = modify_ldt_syscall(0, (void *)ldt, sizeof(ldt)); |
| if (bytes == 0) { |
| /* no indices are taken yet */ |
| return 0; |
| } |
| ASSERT(bytes == sizeof(ldt)); |
| for (i = 0; i < bytes/sizeof(raw_ldt_entry_t); i++) { |
| if (((ldt[i].base3124<<24) | (ldt[i].base2316<<16) | ldt[i].base1500) == 0) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| static void |
| initialize_ldt_struct(our_modify_ldt_t *ldt, void *base, size_t size, uint index) |
| { |
| ASSERT(ldt != NULL); |
| ldt->entry_number = index; |
| IF_X64(ASSERT(CHECK_TRUNCATE_TYPE_uint((ptr_uint_t)base))); |
| ldt->base_addr = (int)(ptr_int_t) base; |
| IF_X64(ASSERT(CHECK_TRUNCATE_TYPE_uint(size))); |
| ldt->limit = size; |
| ldt->seg_32bit = IF_X64_ELSE(0, 1); |
| ldt->contents = MODIFY_LDT_CONTENTS_DATA; |
| ldt->read_exec_only = 0; |
| ldt->limit_in_pages = (size == GDT_NO_SIZE_LIMIT) ? 1 : 0; |
| ldt->seg_not_present = 0; |
| /* While linux kernel doesn't care if we set this, vmkernel requires it */ |
| ldt->useable = 1; /* becomes custom AVL bit */ |
| } |
| |
| static void |
| clear_ldt_struct(our_modify_ldt_t *ldt, uint index) |
| { |
| /* set fields to match LDT_empty() macro from linux kernel */ |
| memset(ldt, 0, sizeof(*ldt)); |
| ldt->seg_not_present = 1; |
| ldt->read_exec_only = 1; |
| ldt->entry_number = index; |
| } |
| |
| static void |
| create_ldt_entry(void *base, size_t size, uint index) |
| { |
| our_modify_ldt_t array; |
| int ret; |
| initialize_ldt_struct(&array, base, size, index); |
| ret = modify_ldt_syscall(1, (void *)&array, sizeof(array)); |
| ASSERT(ret >= 0); |
| } |
| |
| static void |
| clear_ldt_entry(uint index) |
| { |
| our_modify_ldt_t array; |
| int ret; |
| clear_ldt_struct(&array, index); |
| ret = modify_ldt_syscall(1, (void *)&array, sizeof(array)); |
| ASSERT(ret >= 0); |
| } |
| |
| /* Queries the set of available GDT slots, and initializes: |
| * - tls_gdt_index |
| * - gdt_entry_tls_min on ia32 |
| * - lib_tls_gdt_index if using private loader |
| * GDT slots are initialized with a base and limit of zero. The caller is |
| * responsible for setting them to a real base. |
| */ |
| static void |
| choose_gdt_slots(os_local_state_t *os_tls) |
| { |
| our_modify_ldt_t desc; |
| int i; |
| int avail_index[GDT_NUM_TLS_SLOTS]; |
| our_modify_ldt_t clear_desc; |
| int res; |
| |
| /* using local static b/c dynamo_initialized is not set for a client thread |
| * when created in client's dr_init routine |
| */ |
| /* FIXME: Could be racy if we have multiple threads initializing during |
| * startup. |
| */ |
| if (tls_global_init) |
| return; |
| tls_global_init = true; |
| |
| /* We don't want to break the assumptions of pthreads or wine, |
| * so we try to take the last slot. We don't want to hardcode |
| * the index b/c the kernel will let us clobber entries so we want |
| * to only pass in -1. |
| */ |
| ASSERT(!dynamo_initialized); |
| ASSERT(tls_gdt_index == -1); |
| for (i = 0; i < GDT_NUM_TLS_SLOTS; i++) |
| avail_index[i] = -1; |
| for (i = 0; i < GDT_NUM_TLS_SLOTS; i++) { |
| /* We use a base and limit of 0 for testing what's available. */ |
| initialize_ldt_struct(&desc, NULL, 0, -1); |
| res = dynamorio_syscall(SYS_set_thread_area, 1, &desc); |
| LOG(GLOBAL, LOG_THREADS, 4, |
| "%s: set_thread_area -1 => %d res, %d index\n", |
| __FUNCTION__, res, desc.entry_number); |
| if (res >= 0) { |
| /* We assume monotonic increases */ |
| avail_index[i] = desc.entry_number; |
| ASSERT(avail_index[i] > tls_gdt_index); |
| tls_gdt_index = desc.entry_number; |
| } else |
| break; |
| } |
| |
| #ifndef X64 |
| /* In x86-64's ia32 emulation, |
| * set_thread_area(6 <= entry_number && entry_number <= 8) fails |
| * with EINVAL (22) because x86-64 only accepts GDT indices 12 to 14 |
| * for TLS entries. |
| */ |
| if (tls_gdt_index > (gdt_entry_tls_min + GDT_NUM_TLS_SLOTS)) |
| gdt_entry_tls_min = GDT_ENTRY_TLS_MIN_64; /* The kernel is x64. */ |
| #endif |
| |
| /* Now give up the earlier slots */ |
| for (i = 0; i < GDT_NUM_TLS_SLOTS; i++) { |
| if (avail_index[i] > -1 && |
| avail_index[i] != tls_gdt_index) { |
| LOG(GLOBAL, LOG_THREADS, 4, |
| "clearing set_thread_area index %d\n", avail_index[i]); |
| clear_ldt_struct(&clear_desc, avail_index[i]); |
| res = dynamorio_syscall(SYS_set_thread_area, 1, &clear_desc); |
| ASSERT(res >= 0); |
| } |
| } |
| |
| #ifndef VMX86_SERVER |
| ASSERT_CURIOSITY(tls_gdt_index == |
| (kernel_is_64bit() ? GDT_64BIT : GDT_32BIT)); |
| #endif |
| |
| #ifdef CLIENT_INTERFACE |
| if (INTERNAL_OPTION(private_loader) && tls_gdt_index != -1) { |
| /* Use the app's selector with our own TLS base for libraries. app_fs |
| * and app_gs are initialized by the caller in os_tls_app_seg_init(). |
| */ |
| int index = SELECTOR_INDEX(IF_X64_ELSE(os_tls->app_fs, |
| os_tls->app_gs)); |
| if (index == 0) { |
| /* An index of zero means the app has no TLS (yet), and happens |
| * during early injection. We use -1 to grab a new entry. When the |
| * app asks for its first table entry with set_thread_area, we give |
| * it this one and emulate its usage of the segment. |
| */ |
| ASSERT_CURIOSITY(DYNAMO_OPTION(early_inject) && "app has " |
| "no TLS, but we used non-early injection"); |
| initialize_ldt_struct(&desc, NULL, 0, -1); |
| res = dynamorio_syscall(SYS_set_thread_area, 1, &desc); |
| LOG(GLOBAL, LOG_THREADS, 4, |
| "%s: set_thread_area -1 => %d res, %d index\n", |
| __FUNCTION__, res, desc.entry_number); |
| ASSERT(res >= 0); |
| if (res >= 0) { |
| return_stolen_lib_tls_gdt = true; |
| index = desc.entry_number; |
| } |
| } |
| lib_tls_gdt_index = index; |
| } |
| #endif |
| } |
| |
| void |
| tls_thread_init(os_local_state_t *os_tls, byte *segment) |
| { |
| /* We have four different ways to obtain TLS, each with its own limitations: |
| * |
| * 1) Piggyback on the threading system (like we do on Windows): here that would |
| * be pthreads, which uses a segment since at least RH9, and uses gdt-based |
| * segments for NPTL. The advantage is we won't run out of ldt or gdt entries |
| * (except when the app itself would). The disadvantage is we're stealing |
| * application slots and we rely on user mode interfaces. |
| * |
| * 2) Steal an ldt entry via SYS_modify_ldt. This suffers from the 8K ldt entry |
| * limit and requires that we update manually on a new thread. For 64-bit |
| * we're limited here to a 32-bit base. (Strangely, the kernel's |
| * include/asm-x86_64/ldt.h implies that the base is ignored: but it doesn't |
| * seem to be.) |
| * |
| * 3) Steal a gdt entry via SYS_set_thread_area. There is a 3rd unused entry |
| * (after pthreads and wine) we could use. The kernel swaps for us, and with |
| * CLONE_TLS the kernel will set up the entry for a new thread for us. Xref |
| * PR 192231 and PR 285898. This system call is disabled on 64-bit 2.6 |
| * kernels (though the man page for arch_prctl implies it isn't for 2.5 |
| * kernels?!?) |
| * |
| * 4) Use SYS_arch_prctl. This is only implemented on 64-bit kernels, and can |
| * only be used to set the gdt entries that fs and gs select for. Faster to |
| * use <4GB base (obtain with mmap MAP_32BIT) since can use gdt; else have to |
| * use wrmsr. The man pages say "ARCH_SET_GS is disabled in some kernels". |
| */ |
| uint selector; |
| int index = -1; |
| int res; |
| #ifdef X64 |
| /* First choice is gdt, which means arch_prctl. Since this may fail |
| * on some kernels, we require -heap_in_lower_4GB so we can fall back |
| * on modify_ldt. |
| */ |
| byte *cur_gs; |
| res = dynamorio_syscall(SYS_arch_prctl, 2, ARCH_GET_GS, &cur_gs); |
| if (res >= 0) { |
| LOG(GLOBAL, LOG_THREADS, 1, "os_tls_init: cur gs base is "PFX"\n", cur_gs); |
| /* If we're a non-initial thread, gs will be set to the parent thread's value */ |
| if (cur_gs == NULL || is_dynamo_address(cur_gs) || |
| /* By resolving i#107, we can handle gs conflicts between app and dr. */ |
| INTERNAL_OPTION(mangle_app_seg)) { |
| res = dynamorio_syscall(SYS_arch_prctl, 2, ARCH_SET_GS, segment); |
| if (res >= 0) { |
| os_tls->tls_type = TLS_TYPE_ARCH_PRCTL; |
| LOG(GLOBAL, LOG_THREADS, 1, |
| "os_tls_init: arch_prctl successful for base "PFX"\n", segment); |
| /* Kernel should have written %gs for us if using GDT */ |
| if (!dynamo_initialized && read_thread_register(SEG_TLS) == 0) { |
| LOG(GLOBAL, LOG_THREADS, 1, "os_tls_init: using MSR\n"); |
| tls_using_msr = true; |
| } |
| if (IF_CLIENT_INTERFACE_ELSE(INTERNAL_OPTION(private_loader), false)) { |
| res = dynamorio_syscall(SYS_arch_prctl, 2, ARCH_SET_FS, |
| os_tls->os_seg_info.dr_fs_base); |
| /* Assuming set fs must be successful if set gs succeeded. */ |
| ASSERT(res >= 0); |
| } |
| } else { |
| /* we've found a kernel where ARCH_SET_GS is disabled */ |
| ASSERT_CURIOSITY(false && "arch_prctl failed on set but not get"); |
| LOG(GLOBAL, LOG_THREADS, 1, |
| "os_tls_init: arch_prctl failed: error %d\n", res); |
| } |
| } else { |
| /* FIXME PR 205276: we don't currently handle it: fall back on ldt, but |
| * we'll have the same conflict w/ the selector... |
| */ |
| ASSERT_BUG_NUM(205276, cur_gs == NULL); |
| } |
| } |
| #endif |
| |
| if (os_tls->tls_type == TLS_TYPE_NONE) { |
| /* Second choice is set_thread_area */ |
| /* PR 285898: if we added CLONE_SETTLS to all clone calls (and emulated vfork |
| * with clone) we could avoid having to set tls up for each thread (as well |
| * as solve race PR 207903), at least for kernel 2.5.32+. For now we stick |
| * w/ manual setup. |
| */ |
| our_modify_ldt_t desc; |
| |
| /* Pick which GDT slots we'll use for DR TLS and for library TLS if |
| * using the private loader. |
| */ |
| choose_gdt_slots(os_tls); |
| |
| if (tls_gdt_index > -1) { |
| /* Now that we know which GDT slot to use, install the per-thread base |
| * into it. |
| */ |
| /* Base here must be 32-bit */ |
| IF_X64(ASSERT(DYNAMO_OPTION(heap_in_lower_4GB) && |
| segment <= (byte*)UINT_MAX)); |
| initialize_ldt_struct(&desc, segment, PAGE_SIZE, tls_gdt_index); |
| res = dynamorio_syscall(SYS_set_thread_area, 1, &desc); |
| LOG(GLOBAL, LOG_THREADS, 3, |
| "%s: set_thread_area %d => %d res, %d index\n", |
| __FUNCTION__, tls_gdt_index, res, desc.entry_number); |
| ASSERT(res < 0 || desc.entry_number == tls_gdt_index); |
| } else { |
| res = -1; /* fall back on LDT */ |
| } |
| |
| if (res >= 0) { |
| LOG(GLOBAL, LOG_THREADS, 1, |
| "os_tls_init: set_thread_area successful for base "PFX" @index %d\n", |
| segment, tls_gdt_index); |
| os_tls->tls_type = TLS_TYPE_GDT; |
| index = tls_gdt_index; |
| selector = GDT_SELECTOR(index); |
| WRITE_DR_SEG(selector); /* macro needs lvalue! */ |
| } else { |
| IF_VMX86(ASSERT_NOT_REACHED()); /* since no modify_ldt */ |
| LOG(GLOBAL, LOG_THREADS, 1, |
| "os_tls_init: set_thread_area failed: error %d\n", res); |
| } |
| |
| #ifdef CLIENT_INTERFACE |
| /* Install the library TLS base. */ |
| if (INTERNAL_OPTION(private_loader) && res >= 0) { |
| app_pc base = IF_X64_ELSE(os_tls->os_seg_info.dr_fs_base, |
| os_tls->os_seg_info.dr_gs_base); |
| /* lib_tls_gdt_index is picked in choose_gdt_slots. */ |
| ASSERT(lib_tls_gdt_index >= gdt_entry_tls_min); |
| initialize_ldt_struct(&desc, base, GDT_NO_SIZE_LIMIT, |
| lib_tls_gdt_index); |
| res = dynamorio_syscall(SYS_set_thread_area, 1, &desc); |
| LOG(GLOBAL, LOG_THREADS, 3, |
| "%s: set_thread_area %d => %d res, %d index\n", |
| __FUNCTION__, lib_tls_gdt_index, res, desc.entry_number); |
| if (res >= 0) { |
| /* i558 update lib seg reg to enforce the segment changes */ |
| selector = GDT_SELECTOR(lib_tls_gdt_index); |
| LOG(GLOBAL, LOG_THREADS, 2, "%s: setting %s to selector 0x%x\n", |
| __FUNCTION__, reg_names[LIB_SEG_TLS], selector); |
| WRITE_LIB_SEG(selector); |
| } |
| } |
| #endif |
| } |
| |
| if (os_tls->tls_type == TLS_TYPE_NONE) { |
| /* Third choice: modify_ldt, which should be available on kernel 2.3.99+ */ |
| /* Base here must be 32-bit */ |
| IF_X64(ASSERT(DYNAMO_OPTION(heap_in_lower_4GB) && segment <= (byte*)UINT_MAX)); |
| /* we have the thread_initexit_lock so no race here */ |
| index = find_unused_ldt_index(); |
| selector = LDT_SELECTOR(index); |
| ASSERT(index != -1); |
| create_ldt_entry((void *)segment, PAGE_SIZE, index); |
| os_tls->tls_type = TLS_TYPE_LDT; |
| WRITE_DR_SEG(selector); /* macro needs lvalue! */ |
| LOG(GLOBAL, LOG_THREADS, 1, |
| "os_tls_init: modify_ldt successful for base "PFX" w/ index %d\n", |
| segment, index); |
| } |
| |
| os_tls->ldt_index = index; |
| } |
| |
| void |
| tls_thread_free(tls_type_t tls_type, int index) |
| { |
| if (tls_type == TLS_TYPE_LDT) |
| clear_ldt_entry(index); |
| else if (tls_type == TLS_TYPE_GDT) { |
| our_modify_ldt_t desc; |
| clear_ldt_struct(&desc, index); |
| DEBUG_DECLARE(int res = ) |
| dynamorio_syscall(SYS_set_thread_area, 1, &desc); |
| ASSERT(res >= 0); |
| } |
| #ifdef X64 |
| else if (tls_type == TLS_TYPE_ARCH_PRCTL) { |
| DEBUG_DECLARE(int res = ) |
| dynamorio_syscall(SYS_arch_prctl, 2, ARCH_SET_GS, NULL); |
| ASSERT(res >= 0); |
| /* syscall re-sets gs register so caller must re-clear it */ |
| } |
| #endif |
| } |
| |
| /* Assumes it's passed either SEG_FS or SEG_GS. |
| * Returns POINTER_MAX on failure. |
| */ |
| byte * |
| tls_get_fs_gs_segment_base(uint seg) |
| { |
| #ifdef X86 |
| uint selector = read_thread_register(seg); |
| uint index = SELECTOR_INDEX(selector); |
| LOG(THREAD_GET, LOG_THREADS, 4, "%s selector %x index %d ldt %d\n", |
| __func__, selector, index, TEST(SELECTOR_IS_LDT, selector)); |
| |
| if (seg != SEG_FS && seg != SEG_GS) |
| return (byte *) POINTER_MAX; |
| |
| if (TEST(SELECTOR_IS_LDT, selector)) { |
| LOG(THREAD_GET, LOG_THREADS, 4, "selector is LDT\n"); |
| /* we have to read the entire ldt from 0 to the index */ |
| size_t sz = sizeof(raw_ldt_entry_t) * (index + 1); |
| raw_ldt_entry_t *ldt = global_heap_alloc(sz HEAPACCT(ACCT_OTHER)); |
| int bytes; |
| byte *base; |
| memset(ldt, 0, sizeof(*ldt)); |
| bytes = modify_ldt_syscall(0, (void *)ldt, sz); |
| base = (byte *)(ptr_uint_t) LDT_BASE(&ldt[index]); |
| global_heap_free(ldt, sz HEAPACCT(ACCT_OTHER)); |
| if (bytes == sz) { |
| LOG(THREAD_GET, LOG_THREADS, 4, |
| "modify_ldt %d => %x\n", index, base); |
| return base; |
| } |
| } else { |
| our_modify_ldt_t desc; |
| int res; |
| # ifdef X64 |
| byte *base; |
| res = dynamorio_syscall(SYS_arch_prctl, 2, |
| (seg == SEG_FS ? ARCH_GET_FS : ARCH_GET_GS), &base); |
| if (res >= 0) { |
| LOG(THREAD_GET, LOG_THREADS, 4, |
| "arch_prctl %s => "PFX"\n", reg_names[seg], base); |
| return base; |
| } |
| /* else fall back on get_thread_area */ |
| # endif /* X64 */ |
| if (selector == 0) |
| return NULL; |
| DOCHECKINT(1, { |
| uint max_idx = |
| IF_VMX86_ELSE(tls_gdt_index, |
| (kernel_is_64bit() ? GDT_64BIT : GDT_32BIT)); |
| ASSERT_CURIOSITY(index <= max_idx && index >= (max_idx - 2)); |
| }); |
| initialize_ldt_struct(&desc, NULL, 0, index); |
| res = dynamorio_syscall(SYS_get_thread_area, 1, &desc); |
| if (res >= 0) { |
| LOG(THREAD_GET, LOG_THREADS, 4, |
| "get_thread_area %d => %x\n", index, desc.base_addr); |
| return (byte *)(ptr_uint_t) desc.base_addr; |
| } |
| } |
| #elif defined(ARM) |
| /* FIXME i#1551: NYI on ARM */ |
| ASSERT_NOT_REACHED(); |
| #endif /* X86/ARM */ |
| return (byte *) POINTER_MAX; |
| } |
| |
| /* Assumes it's passed either SEG_FS or SEG_GS. |
| * Sets only the base: does not change the segment selector register. |
| */ |
| bool |
| tls_set_fs_gs_segment_base(tls_type_t tls_type, uint seg, |
| /* For x64 and TLS_TYPE_ARCH_PRCTL, base is used: |
| * else, desc is used. |
| */ |
| byte *base, our_modify_ldt_t *desc) |
| { |
| int res = -1; |
| #ifdef X86 |
| if (seg != SEG_FS && seg != SEG_GS) |
| return false; |
| switch (tls_type) { |
| # ifdef X64 |
| case TLS_TYPE_ARCH_PRCTL: { |
| int prctl_code = (seg == SEG_FS ? ARCH_SET_FS : ARCH_SET_GS); |
| res = dynamorio_syscall(SYS_arch_prctl, 2, prctl_code, base); |
| ASSERT(res >= 0); |
| break; |
| } |
| # endif |
| case TLS_TYPE_GDT: { |
| res = dynamorio_syscall(SYS_set_thread_area, 1, desc); |
| ASSERT(res >= 0); |
| break; |
| } |
| default: { |
| ASSERT_NOT_IMPLEMENTED(false); |
| return false; |
| } |
| } |
| #elif defined(ARM) |
| /* FIXME i#1551: NYI on ARM */ |
| ASSERT_NOT_REACHED(); |
| #endif /* X86/ARM */ |
| return (res >= 0); |
| } |
| |
| void |
| tls_init_descriptor(our_modify_ldt_t *desc OUT, void *base, size_t size, uint index) |
| { |
| initialize_ldt_struct(desc, base, size, index); |
| } |
| |
| bool |
| tls_get_descriptor(int index, our_modify_ldt_t *desc OUT) |
| { |
| int res; |
| /* No support for LDT here */ |
| ASSERT(tls_global_type != TLS_TYPE_LDT); |
| initialize_ldt_struct(desc, NULL, 0, index); |
| res = dynamorio_syscall(SYS_get_thread_area, 1, desc); |
| if (res < 0) { |
| clear_ldt_struct(desc, index); |
| return false; |
| } |
| return true; |
| } |
| |
| bool |
| tls_clear_descriptor(int index) |
| { |
| int res; |
| our_modify_ldt_t desc; |
| /* No support for LDT here */ |
| ASSERT(tls_global_type != TLS_TYPE_LDT); |
| clear_ldt_struct(&desc, index); |
| res = dynamorio_syscall(SYS_set_thread_area, 1, &desc); |
| return(res >= 0); |
| } |
| |
| int |
| tls_dr_index(void) |
| { |
| /* No support for LDT here */ |
| ASSERT(tls_global_type != TLS_TYPE_LDT); |
| return tls_gdt_index; |
| } |
| |
| int |
| tls_priv_lib_index(void) |
| { |
| /* No support for LDT here */ |
| ASSERT(tls_global_type != TLS_TYPE_LDT); |
| return lib_tls_gdt_index; |
| } |
| |
| bool |
| tls_dr_using_msr(void) |
| { |
| #ifdef X64 |
| return tls_using_msr; |
| #else |
| return false; |
| #endif |
| } |
| |
| void |
| tls_initialize_indices(os_local_state_t *os_tls) |
| { |
| #ifndef X64 |
| /* Initialize gdt_entry_tls_min on ia32. On x64, the initial value is |
| * correct. |
| */ |
| choose_gdt_slots(os_tls); |
| #endif |
| } |
| |
| int |
| tls_min_index(void) |
| { |
| /* No support for LDT here */ |
| ASSERT(tls_global_type != TLS_TYPE_LDT); |
| #ifndef X64 |
| /* On x64, the initial value is correct */ |
| ASSERT(tls_global_init); |
| #endif |
| return gdt_entry_tls_min; |
| } |
| |
| #ifdef X64 |
| static void |
| os_set_dr_seg(dcontext_t *dcontext, reg_id_t seg) |
| { |
| int res; |
| os_thread_data_t *ostd = dcontext->os_field; |
| res = dynamorio_syscall(SYS_arch_prctl, 2, |
| seg == SEG_GS ? ARCH_SET_GS : ARCH_SET_FS, |
| seg == SEG_GS ? |
| ostd->dr_gs_base : ostd->dr_fs_base); |
| ASSERT(res >= 0); |
| } |
| |
| void |
| tls_handle_post_arch_prctl(dcontext_t *dcontext, int code, reg_t base) |
| { |
| /* XXX: we can move it to pre_system_call to avoid system call. */ |
| /* i#107 syscalls that might change/query app's segment */ |
| os_local_state_t *os_tls = get_os_tls(); |
| switch (code) { |
| case ARCH_SET_FS: { |
| if (IF_CLIENT_INTERFACE_ELSE(INTERNAL_OPTION(private_loader), false)) { |
| os_thread_data_t *ostd; |
| our_modify_ldt_t *desc; |
| /* update new value set by app */ |
| os_tls->app_fs = read_thread_register(SEG_FS); |
| os_tls->app_fs_base = (void *) base; |
| /* update the app_thread_areas */ |
| ostd = (os_thread_data_t *)dcontext->os_field; |
| desc = (our_modify_ldt_t *)ostd->app_thread_areas; |
| desc[FS_TLS].entry_number = tls_min_index() + FS_TLS; |
| dynamorio_syscall(SYS_get_thread_area, 1, &desc[FS_TLS]); |
| /* set it back to the value we are actually using. */ |
| os_set_dr_seg(dcontext, SEG_FS); |
| } |
| break; |
| } |
| case ARCH_GET_FS: { |
| if (IF_CLIENT_INTERFACE_ELSE(INTERNAL_OPTION(private_loader), false)) |
| safe_write_ex((void *)base, sizeof(void *), &os_tls->app_fs_base, NULL); |
| break; |
| } |
| case ARCH_SET_GS: { |
| os_thread_data_t *ostd; |
| our_modify_ldt_t *desc; |
| /* update new value set by app */ |
| os_tls->app_gs = read_thread_register(SEG_GS); |
| os_tls->app_gs_base = (void *) base; |
| /* update the app_thread_areas */ |
| ostd = (os_thread_data_t *)dcontext->os_field; |
| desc = ostd->app_thread_areas; |
| desc[GS_TLS].entry_number = tls_min_index() + GS_TLS; |
| dynamorio_syscall(SYS_get_thread_area, 1, &desc[GS_TLS]); |
| /* set the value back to the value we are actually using */ |
| os_set_dr_seg(dcontext, SEG_GS); |
| break; |
| } |
| case ARCH_GET_GS: { |
| safe_write_ex((void*)base, sizeof(void *), &os_tls->app_gs_base, NULL); |
| break; |
| } |
| default: { |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| } /* switch (dcontext->sys_param0) */ |
| LOG(THREAD_GET, LOG_THREADS, 2, |
| "thread "TIDFMT" segment change => app fs: "PFX", gs: "PFX"\n", |
| get_thread_id(), os_tls->app_fs_base, os_tls->app_gs_base); |
| } |
| #endif /* X64 */ |