blob: 51b4016ec20279ccc7b482b800e61793f35d7c77 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2011-2025 Google, Inc. All rights reserved.
* Copyright (c) 2008-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.
*/
/*
* tls.h - kernel tls support shared among os-specific files, but not
* exported to the rest of the code.
*
* XXX: originally I was going to have this just be kernel tls support
* and leave os_local_state_t inside os.c, but it was a pain to refactor
* os_local_state_t access out of the routines here. We should either
* go ahead and do that, or pull all the os_local_state_t setup into here?
*/
#ifndef _OS_TLS_H_
#define _OS_TLS_H_ 1
#include "os_private.h" /* ASM_XAX */
#if defined(ARM) && defined(LINUX)
# include "include/syscall.h" /* SYS_set_tls */
#elif defined(AARCH64) && defined(MACOS)
# include "include/syscall_mach.h" /* MACHDEP_thread_set_tsd */
#endif
/* We support 3 different methods of creating a segment (see os_tls_init()) */
typedef enum {
TLS_TYPE_NONE,
TLS_TYPE_LDT,
TLS_TYPE_GDT,
#if defined(X86) && defined(X64)
TLS_TYPE_ARCH_PRCTL,
#endif
/* Used with stealing a register in code cache, we use a (app/priv) lib TLS
* slot to store DR's tls base in DR C code.
*/
TLS_TYPE_SLOT,
} tls_type_t;
extern tls_type_t tls_global_type;
/* XXX: more cleanly separate the code so we don't need this here */
extern bool return_stolen_lib_tls_gdt;
#define GDT_NO_SIZE_LIMIT 0xfffff
/* The ldt struct in Linux asm/ldt.h used to be just "struct modify_ldt_ldt_s"; then that
* was also typdef-ed as modify_ldt_t; then it was just user_desc.
* To compile on old and new we inline our own copy of the struct.
* We also use this as a cross-platform representation.
*/
typedef struct _our_modify_ldt_t {
unsigned int entry_number;
unsigned int 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;
} our_modify_ldt_t;
/* segment selector format:
* 15..............3 2 1..0
* index 0=GDT,1=LDT Requested Privilege Level
*/
#define USER_PRIVILEGE 3
#define LDT_NOT_GDT 1
#define GDT_NOT_LDT 0
#define SELECTOR_IS_LDT 0x4
#define LDT_SELECTOR(idx) ((idx) << 3 | ((LDT_NOT_GDT) << 2) | (USER_PRIVILEGE))
#define GDT_SELECTOR(idx) ((idx) << 3 | ((GDT_NOT_LDT) << 2) | (USER_PRIVILEGE))
#define SELECTOR_INDEX(sel) ((sel) >> 3)
#ifdef MACOS64
# define WRITE_DR_SEG(val) ASSERT_NOT_REACHED()
# define WRITE_LIB_SEG(val) ASSERT_NOT_REACHED()
# define TLS_SLOT_VAL_EXITED ((byte *)PTR_UINT_MINUS_1)
#elif defined(X86)
# define WRITE_DR_SEG(val) \
do { \
ASSERT(sizeof(val) == sizeof(reg_t)); \
asm volatile("mov %0,%%" ASM_XAX "; mov %%" ASM_XAX ", %" ASM_SEG ";" \
: \
: "m"((val)) \
: ASM_XAX); \
} while (0)
# define WRITE_LIB_SEG(val) \
do { \
ASSERT(sizeof(val) == sizeof(reg_t)); \
asm volatile("mov %0,%%" ASM_XAX "; mov %%" ASM_XAX ", %" LIB_ASM_SEG ";" \
: \
: "m"((val)) \
: ASM_XAX); \
} while (0)
#elif defined(AARCHXX) || defined(RISCV64)
# define WRITE_DR_SEG(val) ASSERT_NOT_REACHED()
# define WRITE_LIB_SEG(val) ASSERT_NOT_REACHED()
# define TLS_SLOT_VAL_EXITED ((byte *)PTR_UINT_MINUS_1)
#endif /* X86/ARM */
static inline ptr_uint_t
read_thread_register(reg_id_t reg)
{
#ifdef DR_HOST_NOT_TARGET
ptr_uint_t sel = 0;
ASSERT_NOT_REACHED();
#elif defined(MACOS64) && defined(X86)
ptr_uint_t sel;
if (reg == SEG_GS) {
asm volatile("mov %%gs:%1, %0" : "=r"(sel) : "m"(*(void **)0));
} else if (reg == SEG_FS) {
return 0;
} else {
ASSERT_NOT_REACHED();
return 0;
}
#elif defined(X86)
uint sel;
if (reg == SEG_FS) {
asm volatile("movl %%fs, %0" : "=r"(sel));
} else if (reg == SEG_GS) {
asm volatile("movl %%gs, %0" : "=r"(sel));
} else if (reg == SEG_SS) {
asm volatile("movl %%ss, %0" : "=r"(sel));
} else {
ASSERT_NOT_REACHED();
return 0;
}
/* Pre-P6 family leaves upper 2 bytes undefined, so we clear them. We don't
* clear and then use movw because that takes an extra clock cycle, and gcc
* can optimize this "and" into "test %?x, %?x" for calls from
* is_segment_register_initialized().
*/
sel &= 0xffff;
#elif defined(AARCHXX)
ptr_uint_t sel;
if (reg == DR_REG_TPIDRURO) {
IF_X64_ELSE({ asm volatile("mrs %0, tpidrro_el0"
: "=r"(sel)); },
{
/* read thread register from CP15 (coprocessor 15)
* c13 (software thread ID registers) with opcode 3 (user RO)
*/
asm volatile("mrc p15, 0, %0, c13, c0, 3" : "=r"(sel));
});
} else if (reg == DR_REG_TPIDRURW) {
IF_X64_ELSE({ asm volatile("mrs %0, tpidr_el0"
: "=r"(sel)); },
{
/* read thread register from CP15 (coprocessor 15)
* c13 (software thread ID registers) with opcode 2 (user RW)
*/
asm volatile("mrc p15, 0, %0, c13, c0, 2" : "=r"(sel));
});
} else {
ASSERT_NOT_REACHED();
return 0;
}
#elif defined(RISCV64)
ptr_uint_t sel;
if (reg == DR_REG_TP) {
asm volatile("mv %0, tp" : "=r"(sel));
} else if (reg == DR_REG_INVALID) {
/* XXX i#3544: SEG_TLS is not used. See os_exports.h */
return 0;
} else {
ASSERT_NOT_REACHED();
return 0;
}
#else
ASSERT_NOT_IMPLEMENTED(false);
#endif
return sel;
}
#if defined(AARCHXX) || defined(RISCV64)
static inline bool
write_thread_register(void *val)
{
# ifdef DR_HOST_NOT_TARGET
ASSERT_NOT_REACHED();
return false;
# elif defined(AARCH64)
# ifdef MACOS
dynamorio_mach_dep_syscall(MACHDEP_thread_set_tsd, 1, val);
# else
asm volatile("msr " IF_MACOS_ELSE("tpidrro_el0", "tpidr_el0") ", %0" : : "r"(val));
# endif
return true;
# elif defined(RISCV64)
asm volatile("mv tp, %0" : : "r"(val));
return true;
# else
return (dynamorio_syscall(SYS_set_tls, 1, val) == 0);
# endif
}
#endif
#if defined(LINUX) && defined(X86) && defined(X64) && !defined(ARCH_SET_GS)
# define ARCH_SET_GS 0x1001
# define ARCH_SET_FS 0x1002
# define ARCH_GET_FS 0x1003
# define ARCH_GET_GS 0x1004
#endif
#ifdef LINUX
# define GDT_NUM_TLS_SLOTS 3
#elif defined(MACOS) && defined(X86)
/* XXX: rename to APP_SAVED_TLS_SLOTS or sthg?
*
* XXX i#1405: it seems that the kernel does not swap our entries, so
* we are currently creating separate entries per thread -- but we
* only need to save the ones the app might use which we assume will
* be <= 3.
*/
# define GDT_NUM_TLS_SLOTS 3 /* index=1 and index=3 are used */
#endif
#define MAX_NUM_CLIENT_TLS 64
/* i#107: handle segment reg usage conflicts */
typedef struct _os_seg_info_t {
int tls_type;
void *priv_lib_tls_base;
void *priv_alt_tls_base;
void *dr_tls_base;
#ifdef X86
our_modify_ldt_t app_thread_areas[GDT_NUM_TLS_SLOTS];
#endif
} os_seg_info_t;
/* layout of our TLS */
typedef struct _os_local_state_t {
/* put state first to ensure that it is cache-line-aligned */
/* On Linux, we always use the extended structure. */
local_state_extended_t state;
/* Linear address of tls page. */
struct _os_local_state_t *self;
#ifdef X86
/* Magic number for is_thread_tls_initialized() (i#2089).
* XXX: keep the offset of this consistent with TLS_MAGIC_OFFSET_ASM in x86.asm.
*/
# define TLS_MAGIC_VALID 0x244f4952 /* RIO$ */
/* This value is used for os_thread_take_over() re-takeover. */
# define TLS_MAGIC_INVALID 0x2d4f4952 /* RIO- */
uint magic;
#endif
/* store what type of TLS this is so we can clean up properly */
tls_type_t tls_type;
/* For pre-SYS_set_thread_area kernels (pre-2.5.32, pre-NPTL), each
* thread needs its own ldt entry */
int ldt_index;
/* tid needed to ensure children are set up properly */
thread_id_t tid;
#ifdef X86
/* i#107 application's tls value and pointed-at base */
ushort app_lib_tls_reg; /* for mangling seg update/query */
ushort app_alt_tls_reg; /* for mangling seg update/query */
#endif
void *app_lib_tls_base; /* for mangling segmented memory ref */
void *app_alt_tls_base; /* for mangling segmented memory ref */
/* XXX i#3990: For MACOS, we use a union to save tls space. Unfortunately, this
* results in not initialising client tls slots which are allocated using
* dr_raw_tls_calloc. Figuring where to perform memset to clear os_seg_info is not
* apparently clear due to interleaved thread and instrum inits.
*/
#ifdef LINUX
os_seg_info_t os_seg_info;
void *client_tls[MAX_NUM_CLIENT_TLS];
#else
union {
/* i#107: We use space in os_tls to store thread area information
* thread init. It will not conflict with the client_tls usage,
* so we put them into a union for saving space.
*/
os_seg_info_t os_seg_info;
void *client_tls[MAX_NUM_CLIENT_TLS];
};
#endif
#if defined(LINUX) && !defined(X86)
/* Whether a custom mmap was used because no TLS was set up, which happens
* with client threads and static DR.
*/
bool custom_alloc;
/* Whether privload_tls_init() was used to set up client thread TLS. */
bool custom_privlib_tls;
#endif
} os_local_state_t;
os_local_state_t *
get_os_tls(void);
void
tls_thread_init(os_local_state_t *os_tls, byte *segment);
/* Sets a non-zero value for unknown threads on attach (see i#3356). */
bool
tls_thread_preinit();
void
tls_thread_free(tls_type_t tls_type, int index);
#if defined(AARCHXX) || defined(RISCV64)
byte **
get_dr_tls_base_addr(void);
#endif
#if defined(MACOS64) && defined(X86)
void
tls_process_init(void);
void
tls_process_exit(void);
int
tls_get_dr_offs(void);
byte *
tls_get_dr_addr(void);
byte **
get_app_tls_swap_slot_addr(void);
#endif
#ifdef X86
/* Assumes it's passed either SEG_FS or SEG_GS.
* Returns POINTER_MAX on failure.
*/
byte *
tls_get_fs_gs_segment_base(uint seg);
/* 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);
void
tls_init_descriptor(our_modify_ldt_t *desc DR_PARAM_OUT, void *base, size_t size,
uint index);
bool
tls_get_descriptor(int index, our_modify_ldt_t *desc DR_PARAM_OUT);
bool
tls_clear_descriptor(int index);
int
tls_dr_index(void);
int
tls_priv_lib_index(void);
bool
tls_dr_using_msr(void);
bool
running_on_WSL(void);
void
tls_initialize_indices(os_local_state_t *os_tls);
int
tls_min_index(void);
# if defined(LINUX) && defined(X64)
void
tls_handle_post_arch_prctl(dcontext_t *dcontext, int code, reg_t base);
# endif
# if defined(MACOS) && !defined(X64)
void
tls_reinstate_selector(uint selector);
# endif
#endif /* X86 */
#endif /* _OS_TLS_H_ */