static int pthread_allocate_stack(const pthread_attr_t *attr, pthread_descr default_new_thread, int pagesize, pthread_descr * out_new_thread, char ** out_new_thread_bottom, char ** out_guardaddr, size_t * out_guardsize) { pthread_descr new_thread; char * new_thread_bottom; char * guardaddr; size_t stacksize, guardsize; if (attr != NULL && attr->__stackaddr_set) { /* The user provided a stack. */ new_thread = (pthread_descr) ((long)(attr->__stackaddr) & -sizeof(void *)) - 1; new_thread_bottom = (char *) attr->__stackaddr - attr->__stacksize; guardaddr = NULL; guardsize = 0; __pthread_nonstandard_stacks = 1; #ifndef __ARCH_USE_MMU__ /* check the initial thread stack boundaries so they don't overlap */ NOMMU_INITIAL_THREAD_BOUNDS((char *) new_thread, (char *) new_thread_bottom); PDEBUG("initial stack: bos=%p, tos=%p\n", __pthread_initial_thread_bos, __pthread_initial_thread_tos); #endif } else { #ifdef __ARCH_USE_MMU__ stacksize = STACK_SIZE - pagesize; if (attr != NULL) stacksize = MIN(stacksize, roundup(attr->__stacksize, pagesize)); /* Allocate space for stack and thread descriptor at default address */ new_thread = default_new_thread; new_thread_bottom = (char *) (new_thread + 1) - stacksize; if (mmap((caddr_t)((char *)(new_thread + 1) - INITIAL_STACK_SIZE), INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN, -1, 0) == MAP_FAILED) /* Bad luck, this segment is already mapped. */ return -1; /* We manage to get a stack. Now see whether we need a guard and allocate it if necessary. Notice that the default attributes (stack_size = STACK_SIZE - pagesize) do not need a guard page, since the RLIMIT_STACK soft limit prevents stacks from running into one another. */ if (stacksize == (size_t) (STACK_SIZE - pagesize)) { /* We don't need a guard page. */ guardaddr = NULL; guardsize = 0; } else { /* Put a bad page at the bottom of the stack */ guardsize = attr->__guardsize; guardaddr = (void *)new_thread_bottom - guardsize; if (mmap((caddr_t) guardaddr, guardsize, 0, MAP_FIXED, -1, 0) == MAP_FAILED) { /* We don't make this an error. */ guardaddr = NULL; guardsize = 0; } } #else /* We cannot mmap to this huge chunk of stack space when we don't have * an MMU. Pretend we are using a user provided stack even if there was * none provided by the user. Thus, we get around the mmap and reservation * of a huge stack segment. -StS */ stacksize = INITIAL_STACK_SIZE; /* The user may want to use a non-default stacksize */ if (attr != NULL) { stacksize = attr->__stacksize; } /* malloc a stack - memory from the bottom up */ if ((new_thread_bottom = malloc(stacksize)) == NULL) { /* bad luck, we cannot malloc any more */ return -1 ; } PDEBUG("malloced chunk: base=%p, size=0x%04x\n", new_thread_bottom, stacksize); /* Set up the pointers. new_thread marks the TOP of the stack frame and * the address of the pthread_descr struct at the same time. Therefore we * must account for its size and fit it in the malloc()'ed block. The * value of `new_thread' is then passed to clone() as the stack argument. * * ^ +------------------------+ * | | pthread_descr struct | * | +------------------------+ <- new_thread * malloc block | | | * | | thread stack | * | | | * v +------------------------+ <- new_thread_bottom * * Note: The calculated value of new_thread must be word aligned otherwise * the kernel chokes on a non-aligned stack frame. Choose the lower * available word boundary. */ new_thread = ((pthread_descr) ((int)(new_thread_bottom + stacksize) & -sizeof(void*))) - 1; guardaddr = NULL; guardsize = 0; PDEBUG("thread stack: bos=%p, tos=%p\n", new_thread_bottom, new_thread); /* check the initial thread stack boundaries so they don't overlap */ NOMMU_INITIAL_THREAD_BOUNDS((char *) new_thread, (char *) new_thread_bottom); PDEBUG("initial stack: bos=%p, tos=%p\n", __pthread_initial_thread_bos, __pthread_initial_thread_tos); /* on non-MMU systems we always have non-standard stack frames */ __pthread_nonstandard_stacks = 1; #endif /* __ARCH_USE_MMU__ */ } /* Clear the thread data structure. */ memset (new_thread, '\0', sizeof (*new_thread)); *out_new_thread = new_thread; *out_new_thread_bottom = new_thread_bottom; *out_guardaddr = guardaddr; *out_guardsize = guardsize; return 0; }
int __pthread_initialize_manager(void) { int manager_pipe[2]; int pid; int report_events; struct pthread_request request; *__libc_multiple_threads_ptr = 1; /* If basic initialization not done yet (e.g. we're called from a constructor run before our constructor), do it now */ if (__pthread_initial_thread_bos == NULL) pthread_initialize(); /* Setup stack for thread manager */ __pthread_manager_thread_bos = malloc(THREAD_MANAGER_STACK_SIZE); if (__pthread_manager_thread_bos == NULL) return -1; __pthread_manager_thread_tos = __pthread_manager_thread_bos + THREAD_MANAGER_STACK_SIZE; /* On non-MMU systems we make sure that the initial thread bounds don't overlap * with the manager stack frame */ NOMMU_INITIAL_THREAD_BOUNDS(__pthread_manager_thread_tos,__pthread_manager_thread_bos); PDEBUG("manager stack: size=%d, bos=%p, tos=%p\n", THREAD_MANAGER_STACK_SIZE, __pthread_manager_thread_bos, __pthread_manager_thread_tos); #if 0 PDEBUG("initial stack: estimate bos=%p, tos=%p\n", __pthread_initial_thread_bos, __pthread_initial_thread_tos); #endif /* Setup pipe to communicate with thread manager */ if (pipe(manager_pipe) == -1) { free(__pthread_manager_thread_bos); return -1; } /* Start the thread manager */ pid = 0; #if defined(USE_TLS) && USE_TLS if (__linuxthreads_initial_report_events != 0) THREAD_SETMEM (((pthread_descr) NULL), p_report_events, __linuxthreads_initial_report_events); report_events = THREAD_GETMEM (((pthread_descr) NULL), p_report_events); #else if (__linuxthreads_initial_report_events != 0) __pthread_initial_thread.p_report_events = __linuxthreads_initial_report_events; report_events = __pthread_initial_thread.p_report_events; #endif if (__builtin_expect (report_events, 0)) { /* It's a bit more complicated. We have to report the creation of the manager thread. */ int idx = __td_eventword (TD_CREATE); uint32_t mask = __td_eventmask (TD_CREATE); if ((mask & (__pthread_threads_events.event_bits[idx] | __pthread_initial_thread.p_eventbuf.eventmask.event_bits[idx])) != 0) { __pthread_lock(__pthread_manager_thread.p_lock, NULL); #ifdef __ia64__ pid = __clone2(__pthread_manager_event, (void **) __pthread_manager_thread_tos, THREAD_MANAGER_STACK_SIZE, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, (void *)(long)manager_pipe[0]); #else pid = clone(__pthread_manager_event, (void **) __pthread_manager_thread_tos, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, (void *)(long)manager_pipe[0]); #endif if (pid != -1) { /* Now fill in the information about the new thread in the newly created thread's data structure. We cannot let the new thread do this since we don't know whether it was already scheduled when we send the event. */ __pthread_manager_thread.p_eventbuf.eventdata = &__pthread_manager_thread; __pthread_manager_thread.p_eventbuf.eventnum = TD_CREATE; __pthread_last_event = &__pthread_manager_thread; __pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1; __pthread_manager_thread.p_pid = pid; /* Now call the function which signals the event. */ __linuxthreads_create_event (); } /* Now restart the thread. */ __pthread_unlock(__pthread_manager_thread.p_lock); } } if (pid == 0) { #ifdef __ia64__ pid = __clone2(__pthread_manager, (void **) __pthread_manager_thread_tos, THREAD_MANAGER_STACK_SIZE, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, (void *)(long)manager_pipe[0]); #else pid = clone(__pthread_manager, (void **) __pthread_manager_thread_tos, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, (void *)(long)manager_pipe[0]); #endif } if (pid == -1) { free(__pthread_manager_thread_bos); close(manager_pipe[0]); close(manager_pipe[1]); return -1; } __pthread_manager_request = manager_pipe[1]; /* writing end */ __pthread_manager_reader = manager_pipe[0]; /* reading end */ __pthread_manager_thread.p_tid = 2* PTHREAD_THREADS_MAX + 1; __pthread_manager_thread.p_pid = pid; /* Make gdb aware of new thread manager */ if (__pthread_threads_debug && __pthread_sig_debug > 0) { raise(__pthread_sig_debug); /* We suspend ourself and gdb will wake us up when it is ready to handle us. */ __pthread_wait_for_restart_signal(thread_self()); } /* Synchronize debugging of the thread manager */ PDEBUG("send REQ_DEBUG to manager thread\n"); request.req_kind = REQ_DEBUG; TEMP_FAILURE_RETRY(write(__pthread_manager_request, (char *) &request, sizeof(request))); return 0; }