+static int (*pthread_join_p) (pthread_t __th, void ** __thread_return) = NULL;
+
+int real_pthread_join (pthread_t __th, void ** __thread_return) {
+ return pthread_join_p(__th, __thread_return);
+}
+
+static void (*pthread_exit_p)(void *) __attribute__((noreturn))= NULL;
+
+void real_pthread_exit (void * value_ptr) {
+ pthread_exit_p(value_ptr);
+}
+
+void real_init_all() {
+ char * error;
+ if (!real_epoll_wait_p) {
+ real_epoll_wait_p = (int (*)(int epfd, struct epoll_event *events, int maxevents, int timeout))dlsym(RTLD_NEXT, "epoll_wait");
+ if ((error = dlerror()) != NULL) {
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (!pthread_mutex_init_p) {
+ pthread_mutex_init_p = (int (*)(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr))dlsym(RTLD_NEXT, "pthread_mutex_init");
+ if ((error = dlerror()) != NULL) {
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (!pthread_mutex_lock_p) {
+ pthread_mutex_lock_p = (int (*)(pthread_mutex_t *__mutex))dlsym(RTLD_NEXT, "pthread_mutex_lock");
+ if ((error = dlerror()) != NULL) {
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (!pthread_mutex_unlock_p) {
+ pthread_mutex_unlock_p = (int (*)(pthread_mutex_t *__mutex))dlsym(RTLD_NEXT, "pthread_mutex_unlock");
+ if ((error = dlerror()) != NULL) {
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (!pthread_create_p) {
+ pthread_create_p = (int (*)(pthread_t *__restrict, const pthread_attr_t *__restrict, void *(*)(void *), void *__restrict))dlsym(RTLD_NEXT, "pthread_create");
+ if ((error = dlerror()) != NULL) {
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (!pthread_join_p) {
+ pthread_join_p = (int (*)(pthread_t __th, void ** __thread_return))dlsym(RTLD_NEXT, "pthread_join");
+ if ((error = dlerror()) != NULL) {
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (!pthread_exit_p) {
+ *((void (**)(void *)) &pthread_exit_p) = (void (*)(void *))dlsym(RTLD_NEXT, "pthread_exit");
+ if ((error = dlerror()) != NULL) {
+ fputs(error, stderr);
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+#ifdef TLS
+void finalize_helper_thread() {
+ Thread * curr_thread = thread_current();
+ real_pthread_mutex_lock(&curr_thread->mutex);
+ curr_thread->tls = (char *) get_tls_addr();
+ real_pthread_mutex_unlock(&curr_thread->mutex);
+ //Wait in the kernel until it is time for us to finish
+ real_pthread_mutex_lock(&curr_thread->mutex2);
+ real_pthread_mutex_unlock(&curr_thread->mutex2);
+ //return to helper thread function
+ setcontext(&curr_thread->context);
+}
+
+void * helper_thread(void * ptr) {
+ Thread * curr_thread = thread_current();
+
+ //build a context for this real thread so we can take it's context
+ int ret = getcontext(&curr_thread->helpercontext);
+ ASSERT(!ret);
+
+ //Setup destructor
+ if (pthread_setspecific(model->get_execution()->getPthreadKey(), (const void *)4)) {
+ printf("Destructor setup failed\n");
+ exit(-1);
+ }
+
+
+ /* Initialize new managed context */
+ curr_thread->helper_stack = stack_allocate(STACK_SIZE);
+ curr_thread->helpercontext.uc_stack.ss_sp = curr_thread->helper_stack;
+ curr_thread->helpercontext.uc_stack.ss_size = STACK_SIZE;
+ curr_thread->helpercontext.uc_stack.ss_flags = 0;
+ curr_thread->helpercontext.uc_link = NULL;
+ makecontext(&curr_thread->helpercontext, finalize_helper_thread, 0);
+
+ model_swapcontext(&curr_thread->context, &curr_thread->helpercontext);
+
+
+ //start the real thread
+ thread_startup();
+
+ return NULL;
+}
+
+#ifdef TLS
+void tlsdestructor(void *v) {
+ uintptr_t count = (uintptr_t) v;
+ if (count > 1) {
+ if (pthread_setspecific(model->get_execution()->getPthreadKey(), (const void *)(count - 1))) {
+ printf("Destructor setup failed\n");
+ exit(-1);
+ }
+ return;
+ }
+ /* Finish thread properly */
+ model->switch_thread(new ModelAction(THREAD_FINISH, std::memory_order_seq_cst, thread_current()));
+}
+#endif
+
+void setup_context() {
+ Thread * curr_thread = thread_current();
+
+ /* Add dummy "start" action, just to create a first clock vector */
+ model->switch_thread(new ModelAction(THREAD_START, std::memory_order_seq_cst, curr_thread));
+
+ real_init_all();
+
+ /* Initialize our lock */
+ real_pthread_mutex_init(&curr_thread->mutex, NULL);
+ real_pthread_mutex_init(&curr_thread->mutex2, NULL);
+ real_pthread_mutex_lock(&curr_thread->mutex2);
+
+ /* Create the real thread */
+ real_pthread_create(&curr_thread->thread, NULL, helper_thread, NULL);
+ bool notdone = true;
+ while(notdone) {
+ real_pthread_mutex_lock(&curr_thread->mutex);
+ if (curr_thread->tls != NULL)
+ notdone = false;
+ real_pthread_mutex_unlock(&curr_thread->mutex);
+ }
+
+ set_tls_addr((uintptr_t)curr_thread->tls);
+ setcontext(&curr_thread->context);
+}
+#endif
+