From 11349f4e83c1446d5c678384a7d45e410c82c3e2 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 25 Jul 2019 12:55:24 -0700 Subject: [PATCH] Experimental TLS support. --- cmodelint.cc | 2 +- config.h | 2 + execution.cc | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ execution.h | 14 +++++ history.cc | 99 ++++++++++++++++++----------------- model.cc | 6 +++ model.h | 1 + pthread.cc | 2 +- threads-model.h | 10 ++++ threads.cc | 51 ++++++++++++++++++ 10 files changed, 270 insertions(+), 52 deletions(-) diff --git a/cmodelint.cc b/cmodelint.cc index 436de34f..b39e15b3 100644 --- a/cmodelint.cc +++ b/cmodelint.cc @@ -110,7 +110,7 @@ VOLATILELOAD(64) void cds_volatile_store ## size (void * obj, uint ## size ## _t val, const char * position) { \ ensureModel(); \ model->switch_to_master(new ModelAction(ATOMIC_WRITE, position, memory_order_relaxed, obj, (uint64_t) val)); \ - *((volatile uint ## size ## _t *)obj) = val; \ + *((volatile uint ## size ## _t *)obj) = val; \ } VOLATILESTORE(8) diff --git a/config.h b/config.h index ed0b6bac..c276bd03 100644 --- a/config.h +++ b/config.h @@ -43,6 +43,8 @@ /** Page size configuration */ #define PAGESIZE 4096 +#define TLS 1 + /** Thread parameters */ /* Size of stack to allocate for a thread. */ diff --git a/execution.cc b/execution.cc index 33a608d6..0710dd6b 100644 --- a/execution.cc +++ b/execution.cc @@ -47,6 +47,123 @@ struct model_snapshot_members { SNAPSHOTALLOC }; + +#ifdef TLS +#include + +//Code taken from LLVM and licensed under the University of Illinois Open Source +//License. +static uintptr_t thread_descriptor_size; +#if __LP64__ || defined(_WIN64) +# define SANITIZER_WORDSIZE 64 +#else +# define SANITIZER_WORDSIZE 32 +#endif + +#if SANITIZER_WORDSIZE == 64 +# define FIRST_32_SECOND_64(a, b) (b) +#else +# define FIRST_32_SECOND_64(a, b) (a) +#endif + +#if defined(__x86_64__) && !defined(_LP64) +# define SANITIZER_X32 1 +#else +# define SANITIZER_X32 0 +#endif + +#if defined(__arm__) +# define SANITIZER_ARM 1 +#else +# define SANITIZER_ARM 0 +#endif + +uintptr_t ThreadDescriptorSize() { + uintptr_t val = thread_descriptor_size; + if (val) + return val; +#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) +#ifdef _CS_GNU_LIBC_VERSION + char buf[64]; + uintptr_t len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf)); + if (len < sizeof(buf) && strncmp(buf, "glibc 2.", 8) == 0) { + char *end; + int minor = strtoll(buf + 8, &end, 10); + if (end != buf + 8 && (*end == '\0' || *end == '.' || *end == '-')) { + int patch = 0; + if (*end == '.') + // strtoll will return 0 if no valid conversion could be performed + patch = strtoll(end + 1, nullptr, 10); + + /* sizeof(struct pthread) values from various glibc versions. */ + if (SANITIZER_X32) + val = 1728;// Assume only one particular version for x32. + // For ARM sizeof(struct pthread) changed in Glibc 2.23. + else if (SANITIZER_ARM) + val = minor <= 22 ? 1120 : 1216; + else if (minor <= 3) + val = FIRST_32_SECOND_64(1104, 1696); + else if (minor == 4) + val = FIRST_32_SECOND_64(1120, 1728); + else if (minor == 5) + val = FIRST_32_SECOND_64(1136, 1728); + else if (minor <= 9) + val = FIRST_32_SECOND_64(1136, 1712); + else if (minor == 10) + val = FIRST_32_SECOND_64(1168, 1776); + else if (minor == 11 || (minor == 12 && patch == 1)) + val = FIRST_32_SECOND_64(1168, 2288); + else if (minor <= 13) + val = FIRST_32_SECOND_64(1168, 2304); + else + val = FIRST_32_SECOND_64(1216, 2304); + } + } +#endif +#elif defined(__mips__) + // TODO(sagarthakur): add more values as per different glibc versions. + val = FIRST_32_SECOND_64(1152, 1776); +#elif defined(__aarch64__) + // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22. + val = 1776; +#elif defined(__powerpc64__) + val = 1776; // from glibc.ppc64le 2.20-8.fc21 +#elif defined(__s390__) + val = FIRST_32_SECOND_64(1152, 1776); // valid for glibc 2.22 +#endif + if (val) + thread_descriptor_size = val; + return val; +} + +#ifdef __i386__ +# define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall)) +#else +# define DL_INTERNAL_FUNCTION +#endif + +intptr_t RoundUpTo(uintptr_t size, uintptr_t boundary) { + return (size + boundary - 1) & ~(boundary - 1); +} + +uintptr_t getTlsSize() { + // all current supported platforms have 16 bytes stack alignment + const size_t kStackAlign = 16; + typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; + get_tls_func get_tls; + void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); + memcpy(&get_tls, &get_tls_static_info_ptr, sizeof(get_tls_static_info_ptr)); + ASSERT(get_tls != 0); + size_t tls_size = 0; + size_t tls_align = 0; + get_tls(&tls_size, &tls_align); + if (tls_align < kStackAlign) + tls_align = kStackAlign; + return RoundUpTo(tls_size, tls_align); +} + +#endif + /** @brief Constructor */ ModelExecution::ModelExecution(ModelChecker *m, Scheduler *scheduler) : model(m), @@ -67,6 +184,12 @@ ModelExecution::ModelExecution(ModelChecker *m, Scheduler *scheduler) : fuzzer(new Fuzzer()), thrd_func_list(), thrd_func_inst_lists() +#ifdef TLS + ,tls_base(NULL), + tls_addr(0), + tls_size(0), + thd_desc_size(0) +#endif { /* Initialize a model-checker thread, for special ModelActions */ model_thread = new Thread(get_next_id()); @@ -74,6 +197,18 @@ ModelExecution::ModelExecution(ModelChecker *m, Scheduler *scheduler) : scheduler->register_engine(this); } +#ifdef TLS +void ModelExecution::initTLS() { + tls_addr = get_tls_addr(); + tls_size = getTlsSize(); + tls_addr -= tls_size; + thd_desc_size = ThreadDescriptorSize(); + tls_addr += thd_desc_size; + tls_base = (char *) snapshot_calloc(tls_size,1); + memcpy(tls_base, reinterpret_cast(tls_addr), tls_size); +} +#endif + /** @brief Destructor */ ModelExecution::~ModelExecution() { diff --git a/execution.h b/execution.h index f1d3dc56..513a92a7 100644 --- a/execution.h +++ b/execution.h @@ -212,6 +212,20 @@ private: * This data structure is handled by ModelHistory */ SnapVector< SnapList< func_inst_list_t *> *> thrd_func_inst_lists; + +#ifdef TLS +public: + char * getTLSBase() {return tls_base;} + uintptr_t getTLSAddr() {return tls_addr;} + uintptr_t getTLSSize() {if (!tls_base) initTLS();return tls_size;} + uintptr_t getThdDescSize() {return thd_desc_size;} +private: + void initTLS(); + char * tls_base; + uintptr_t tls_addr; + uintptr_t tls_size; + uintptr_t thd_desc_size; +#endif }; #endif /* __EXECUTION_H__ */ diff --git a/history.cc b/history.cc index a3398f9c..4ee34bff 100644 --- a/history.cc +++ b/history.cc @@ -18,65 +18,64 @@ ModelHistory::ModelHistory() : void ModelHistory::enter_function(const uint32_t func_id, thread_id_t tid) { - //model_print("thread %d entering func %d\n", tid, func_id); - uint32_t id = id_to_int(tid); - SnapVector * thrd_func_list = model->get_execution()->get_thrd_func_list(); - SnapVector< SnapList *> * - thrd_func_inst_lists = model->get_execution()->get_thrd_func_inst_lists(); + /* //model_print("thread %d entering func %d\n", tid, func_id); + uint32_t id = id_to_int(tid); + SnapVector * thrd_func_list = model->get_execution()->get_thrd_func_list(); + SnapVector< SnapList *> * + thrd_func_inst_lists = model->get_execution()->get_thrd_func_inst_lists(); - if ( thrd_func_list->size() <= id ) { - thrd_func_list->resize( id + 1 ); - thrd_func_inst_lists->resize( id + 1 ); - } + if ( thrd_func_list->size() <= id ) { + thrd_func_list->resize( id + 1 ); + thrd_func_inst_lists->resize( id + 1 ); + } - func_id_list_t * func_list = thrd_func_list->at(id); - SnapList * func_inst_lists = thrd_func_inst_lists->at(id); + func_id_list_t * func_list = thrd_func_list->at(id); + SnapList * func_inst_lists = thrd_func_inst_lists->at(id); - if (func_list == NULL) { - func_list = new func_id_list_t(); - thrd_func_list->at(id) = func_list; - } + if (func_list == NULL) { + func_list = new func_id_list_t(); + thrd_func_list->at(id) = func_list; + } - if (func_inst_lists == NULL) { - func_inst_lists = new SnapList< func_inst_list_t *>(); - thrd_func_inst_lists->at(id) = func_inst_lists; - } + if (func_inst_lists == NULL) { + func_inst_lists = new SnapList< func_inst_list_t *>(); + thrd_func_inst_lists->at(id) = func_inst_lists; + } - func_list->push_back(func_id); - func_inst_lists->push_back( new func_inst_list_t() ); + func_list->push_back(func_id); + func_inst_lists->push_back( new func_inst_list_t() ); - if ( func_nodes.size() <= func_id ) - resize_func_nodes( func_id + 1 ); -} + if ( func_nodes.size() <= func_id ) + resize_func_nodes( func_id + 1 ); + */} /* @param func_id a non-zero value */ void ModelHistory::exit_function(const uint32_t func_id, thread_id_t tid) { - uint32_t id = id_to_int(tid); - SnapVector * thrd_func_list = model->get_execution()->get_thrd_func_list(); - SnapVector< SnapList *> * - thrd_func_inst_lists = model->get_execution()->get_thrd_func_inst_lists(); - - func_id_list_t * func_list = thrd_func_list->at(id); - SnapList * func_inst_lists = thrd_func_inst_lists->at(id); - - uint32_t last_func_id = func_list->back(); - - if (last_func_id == func_id) { - /* clear read map upon exiting functions */ - FuncNode * func_node = func_nodes[func_id]; - func_node->clear_read_map(tid); - - func_inst_list_t * curr_inst_list = func_inst_lists->back(); - func_node->link_insts(curr_inst_list); - - func_list->pop_back(); - func_inst_lists->pop_back(); - } else { - model_print("trying to exit with a wrong function id\n"); - model_print("--- last_func: %d, func_id: %d\n", last_func_id, func_id); - } - //model_print("thread %d exiting func %d\n", tid, func_id); + /* uint32_t id = id_to_int(tid); + SnapVector * thrd_func_list = model->get_execution()->get_thrd_func_list(); + SnapVector< SnapList *> * + thrd_func_inst_lists = model->get_execution()->get_thrd_func_inst_lists(); + + func_id_list_t * func_list = thrd_func_list->at(id); + SnapList * func_inst_lists = thrd_func_inst_lists->at(id); + + uint32_t last_func_id = func_list->back(); + + if (last_func_id == func_id) { + FuncNode * func_node = func_nodes[func_id]; + func_node->clear_read_map(tid); + + func_inst_list_t * curr_inst_list = func_inst_lists->back(); + func_node->link_insts(curr_inst_list); + + func_list->pop_back(); + func_inst_lists->pop_back(); + } else { + model_print("trying to exit with a wrong function id\n"); + model_print("--- last_func: %d, func_id: %d\n", last_func_id, func_id); + } + //model_print("thread %d exiting func %d\n", tid, func_id);*/ } void ModelHistory::resize_func_nodes(uint32_t new_size) @@ -86,7 +85,7 @@ void ModelHistory::resize_func_nodes(uint32_t new_size) if ( old_size < new_size ) func_nodes.resize(new_size); - for (uint32_t id = old_size; id < new_size; id++) { + for (uint32_t id = old_size;id < new_size;id++) { const char * func_name = func_map_rev[id]; FuncNode * func_node = new FuncNode(); func_node->set_func_id(id); diff --git a/model.cc b/model.cc index 45fa1b20..e4d6b75f 100644 --- a/model.cc +++ b/model.cc @@ -24,6 +24,9 @@ ModelChecker *model = NULL; /** Wrapper to run the user's main function, with appropriate arguments */ void user_main_wrapper(void *) { +#ifdef TLS + model->get_execution()->getTLSSize(); +#endif user_main(model->params.argc, model->params.argv); } @@ -41,6 +44,9 @@ ModelChecker::ModelChecker() : { memset(&stats,0,sizeof(struct execution_stats)); init_thread = new Thread(execution->get_next_id(), (thrd_t *) model_malloc(sizeof(thrd_t)), &user_main_wrapper, NULL, NULL); // L: user_main_wrapper passes the user program +#ifdef TLS + init_thread->setTLS((char *)get_tls_addr()); +#endif execution->add_thread(init_thread); scheduler->set_current_thread(init_thread); execution->setParams(¶ms); diff --git a/model.h b/model.h index 9fb94228..2f137a3a 100644 --- a/model.h +++ b/model.h @@ -67,6 +67,7 @@ public: void set_inspect_plugin(TraceAnalysis *a) { inspect_plugin=a; } void startMainThread(); void startChecker(); + Thread * getInitThread() {return init_thread;} MEMALLOC private: /** Flag indicates whether to restart the model checker. */ diff --git a/pthread.cc b/pthread.cc index d23c4900..120d365c 100644 --- a/pthread.cc +++ b/pthread.cc @@ -108,7 +108,7 @@ int pthread_mutex_trylock(pthread_mutex_t *p_mutex) { model = new ModelChecker(); model->startChecker(); } - + ModelExecution *execution = model->get_execution(); cdsc::snapmutex *m = execution->getMutexMap()->get(p_mutex); return m->try_lock(); diff --git a/threads-model.h b/threads-model.h index 02f20b03..eebfcc7f 100644 --- a/threads-model.h +++ b/threads-model.h @@ -118,6 +118,9 @@ public: void operator delete[](void *p, size_t size) { Thread_free(p); } +#ifdef TLS + void setTLS(char *_tls) { tls = _tls;} +#endif private: int create_context(); @@ -142,6 +145,9 @@ private: void *arg; ucontext_t context; void *stack; +#ifdef TLS + char *tls; +#endif thrd_t *user_thread; thread_id_t id; thread_state state; @@ -160,6 +166,10 @@ private: const bool model_thread; }; +#ifdef TLS +uintptr_t get_tls_addr(); +#endif + Thread * thread_current(); void thread_startup(); diff --git a/threads.cc b/threads.cc index 67f681ca..6c7b8902 100644 --- a/threads.cc +++ b/threads.cc @@ -12,6 +12,25 @@ /* global "model" object */ #include "model.h" +#include "execution.h" + +#ifdef TLS +uintptr_t get_tls_addr() { + uintptr_t addr; + asm ("mov %%fs:0, %0" : "=r" (addr)); + return addr; +} + +#include +#include +extern "C" { +int arch_prctl(int code, unsigned long addr); +} +static void set_tls_addr(uintptr_t addr) { + arch_prctl(ARCH_SET_FS, addr); + asm ("mov %0, %%fs:0" : : "r" (addr) : "memory"); +} +#endif /** Allocate a stack for a new thread. */ static void * stack_allocate(size_t size) @@ -50,6 +69,17 @@ void thread_startup() /* Add dummy "start" action, just to create a first clock vector */ model->switch_to_master(new ModelAction(THREAD_START, std::memory_order_seq_cst, curr_thread)); +#ifdef TLS + if (curr_thread->tls == NULL) { + uintptr_t tlssize = model->get_execution()->getTLSSize(); + uintptr_t thddesc = model->get_execution()->getThdDescSize(); + curr_thread->tls = (char*) Thread_malloc(tlssize); + memcpy(curr_thread->tls, model->get_execution()->getTLSBase(), tlssize); + curr_thread->tls += tlssize - thddesc; + set_tls_addr((uintptr_t)curr_thread->tls); + } +#endif + /* Call the actual thread function */ if (curr_thread->start_routine != NULL) { curr_thread->start_routine(curr_thread->arg); @@ -98,6 +128,9 @@ int Thread::create_context() int Thread::swap(Thread *t, ucontext_t *ctxt) { t->set_state(THREAD_READY); +#ifdef TLS + set_tls_addr((uintptr_t)model->getInitThread()->tls); +#endif return model_swapcontext(&t->context, ctxt); } @@ -112,6 +145,10 @@ int Thread::swap(Thread *t, ucontext_t *ctxt) int Thread::swap(ucontext_t *ctxt, Thread *t) { t->set_state(THREAD_RUNNING); +#ifdef TLS + if (t->tls != NULL) + set_tls_addr((uintptr_t)t->tls); +#endif return model_swapcontext(ctxt, &t->context); } @@ -124,6 +161,11 @@ void Thread::complete() state = THREAD_COMPLETED; if (stack) stack_free(stack); +#ifdef TLS + if (tls && get_id() != 1) + tls += model->get_execution()->getTLSSize() - model->get_execution()->getThdDescSize(); + Thread_free(tls); +#endif } /** @@ -141,6 +183,9 @@ Thread::Thread(thread_id_t tid) : start_routine(NULL), arg(NULL), stack(NULL), +#ifdef TLS + tls(NULL), +#endif user_thread(NULL), id(tid), state(THREAD_READY), /* Thread is always ready? */ @@ -163,6 +208,9 @@ Thread::Thread(thread_id_t tid, thrd_t *t, void (*func)(void *), void *a, Thread start_routine(func), pstart_routine(NULL), arg(a), +#ifdef TLS + tls(NULL), +#endif user_thread(t), id(tid), state(THREAD_CREATED), @@ -192,6 +240,9 @@ Thread::Thread(thread_id_t tid, thrd_t *t, void *(*func)(void *), void *a, Threa start_routine(NULL), pstart_routine(func), arg(a), +#ifdef TLS + tls(NULL), +#endif user_thread(t), id(tid), state(THREAD_CREATED), -- 2.34.1