+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 (!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) {
+ 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);
+
+ /* Initialize new managed context */
+ void *helperstack = stack_allocate(STACK_SIZE);
+ curr_thread->helpercontext.uc_stack.ss_sp = helperstack;
+ curr_thread->helpercontext.uc_stack.ss_size = STACK_SIZE;
+ curr_thread->helpercontext.uc_stack.ss_flags = 0;
+ curr_thread->helpercontext.uc_link = model->get_system_context();
+ makecontext(&curr_thread->helpercontext, finalize_helper_thread, 0);
+
+ model_swapcontext(&curr_thread->context, &curr_thread->helpercontext);
+
+ //start the real thread
+ thread_startup();
+
+ //now the real thread has control again
+ stack_free(helperstack);
+
+ return NULL;
+}
+
+void setup_context() {