/* Procedure executed by sideline threads. * XXX i#500: Cannot use libc routines (printf) in the child process. */ int run(void *arg) { int threadnum = (int)(long) arg; int i = 0; /* for CLONE_CHILD_CLEARTID for signaling parent. if we used raw * clone system call we could get kernel to do this for us. */ child[threadnum] = dynamorio_syscall(SYS_gettid, 0); dynamorio_syscall(SYS_set_tid_address, 1, &child[threadnum]); child_started[threadnum] = true; nolibc_print("Sideline thread started\n"); while (true) { /* do nothing for now */ i++; if (i % 25000000 == 0) break; } while (!child_exit[threadnum]) nolibc_nanosleep(&sleeptime); nolibc_print("Sideline thread finished, exiting whole group\n"); /* We deliberately bring down the whole group. Note that this is * the default on x64 on returning for some reason which seems * like a bug in _clone() (xref i#94). */ dynamorio_syscall(SYS_exit_group, 0); return 0; }
/* Procedure executed by sideline threads * XXX i#500: Cannot use libc routines (printf) in the child process. */ int run(void *arg) { int i = 0; /* for CLONE_CHILD_CLEARTID for signaling parent. if we used raw * clone system call we could get kernel to do this for us. */ child = nolibc_syscall(SYS_gettid, 0); nolibc_syscall(SYS_set_tid_address, 1, &child); nolibc_print("Sideline thread started\n"); while (true) { /* do nothing for now */ i++; if (i % 2500000 == 0) { nolibc_print("i = "); nolibc_print_int(i); nolibc_print("\n"); } if (i % 25000000 == 0) break; } while (!child_exit) nanosleep(&sleeptime, NULL); nolibc_print("Sideline thread finished\n"); child_done = true; #ifdef X64 /* FIXME: returning here invokes SYS_exit_group and takes down the * parent...what's up with that? Xref i#94. */ nolibc_syscall(SYS_exit, 0); #endif return 0; }
/* allocate stack storage on the app's heap */ static void * stack_alloc(int size) { size_t sp; void *q = NULL; void *p; #if STACK_OVERFLOW_PROTECT /* allocate an extra page and mark it non-accessible to trap stack overflow */ q = nolibc_mmap(0, PAGE_SIZE, PROT_NONE, MAP_ANON|MAP_PRIVATE, -1, 0); if (q == NULL || q == MAP_FAILED) nolibc_print("mmap failed\n"); stack_redzone_start = (size_t) q; #endif p = nolibc_mmap(q, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); if (p == NULL || p == MAP_FAILED) nolibc_print("mmap failed\n"); #ifdef DEBUG nolibc_memset(p, 0xab, size); #endif /* stack grows from high to low addresses, so return a ptr to the top of the allocated region */ sp = (size_t)p + size; return (void*) sp; }
/* Safe print int syscall. */ void nolibc_print_int(int n) { char buf[12]; /* 2 ** 31 has 10 digits, plus sign and nul. */ int i = 0; int m; int len; /* Sign. */ if (n < 0) { buf[i++] = '-'; n = -n; } /* Go forward by the number of digits we'll need. */ m = n; while (m > 0) { m /= 10; i++; } len = i; assert(len <= sizeof(buf)); /* Go backward for each digit. */ while (n > 0) { buf[--i] = '0' + (n % 10); n /= 10; } buf[len] = '\0'; nolibc_print(buf); }
static void delete_thread(pid_t pid, void *stack) { pid_t result; /* do not print out pids to make diff easy */ nolibc_print("Waiting for child to exit\n"); result = waitpid(pid, NULL, 0); nolibc_print("Child has exited\n"); if (result == -1 || result != pid) { /* somehow getting errors: ignoring for now since works */ #if VERBOSE perror("delete_thread waitpid"); #endif } stack_free(stack, THREAD_STACK_SIZE); }
/* Create a new thread. It should be passed "fcn", a function which * takes two arguments, (the second one is a dummy, always 4). The * first argument is passed in "arg". Returns the TID of the new * thread */ static pid_t create_thread(int (*fcn)(void *), void *arg, void **stack, bool same_group) { pid_t newpid; int flags; void *my_stack; my_stack = stack_alloc(THREAD_STACK_SIZE); /* need SIGCHLD so parent will get that signal when child dies, * else have errors doing a wait */ flags = SIGCHLD | CLONE_VM | /* CLONE_THREAD => no signal to parent on termination; have to use * CLONE_CHILD_CLEARTID to get that. Since we're using library call * instead of raw system call we don't have child_tidptr argument, * so we set the location in the child itself via set_tid_address(). */ CLONE_CHILD_CLEARTID | CLONE_FS | CLONE_FILES | CLONE_SIGHAND; if (same_group) flags |= CLONE_THREAD; /* XXX: Using libc clone in the child here really worries me, but it seems * to work. My theory is that the parent has to call clone, which invokes * the loader to fill in the PLT entry, so when the child calls clone it * doesn't go into the loader and avoiding the races like we saw in i#500. */ newpid = clone(fcn, my_stack, flags, arg); /* this is really a tid if we passed CLONE_THREAD: child has same pid as us */ if (newpid == -1) { nolibc_print("smp.c: Error calling clone\n"); stack_free(my_stack, THREAD_STACK_SIZE); return -1; } *stack = my_stack; return newpid; }
/* Procedure executed by sideline threads * XXX i#500: Cannot use libc routines (printf) in the child process. */ int run(void *arg) { int i = 0; nolibc_print("Sideline thread started\n"); while (true) { /* do nothing for now */ i++; if (i % 2500000 == 0) { nolibc_print("i = "); nolibc_print_int(i); nolibc_print("\n"); } if (i % 25000000 == 0) break; } while (!child_exit) nolibc_nanosleep(&sleeptime); nolibc_print("Sideline thread finished\n"); child_done = true; return 0; }
/* Procedure executed by sideline threads * XXX i#500: Cannot use libc routines (printf) in the child process. */ int run(void *arg) { int threadnum = (int)(long) arg; int i = 0; /* for CLONE_CHILD_CLEARTID for signaling parent. if we used raw * clone system call we could get kernel to do this for us. */ child[threadnum] = nolibc_syscall(SYS_gettid, 0); nolibc_syscall(SYS_set_tid_address, 1, &child[threadnum]); if (threadnum == 0) { /* first child creates rest of group */ int j; for (j = 1; j < NUM_THREADS; j++) { child[j] = create_thread(run, (void *)(long)j, &stack[j], true); if (child[j] < 0) nolibc_print("failed to create child thread\n"); } } child_started[threadnum] = true; nolibc_print("Sideline thread started\n"); while (true) { /* do nothing for now */ i++; if (i % 25000000 == 0) break; } while (!child_exit[threadnum]) nolibc_nanosleep(&sleeptime); nolibc_print("Sideline thread finished, exiting whole group\n"); child_done[threadnum] = true; /* We deliberately bring down the whole group. Note that this is * the default on x64 on returning for some reason which seems * like a bug in _clone() (xref i#94). */ nolibc_syscall(SYS_exit_group, 0); return 0; }