2 * Create NUM_THREADS threads which print "1" and sleep in pause().
3 * Then create another thread which prints "2", and re-execs the program.
4 * The leader then either sleeps in pause(), or exits if $LEADER_EXIT is set.
5 * This triggers "execve'ed thread replaces thread leader" case.
7 * gcc -Wall -Os -o threaded_execve threaded_execve.c
9 * Try running it under strace like this:
11 * # Should not be confused by traced execve-ing thread
12 * # replacing traced leader:
13 * strace -oLOG -f ./threaded_execve
15 * # Same, but different output mode. Output after execve
16 * # should go into leader's LOG.<pid> file, not into execve'ed
17 * # thread's log file:
18 * strace -oLOG -ff ./threaded_execve
20 * # Should not be confused by non-traced execve-ing thread
21 * # replacing traced leader:
22 * strace -oLOG ./threaded_execve
23 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
24 * In Linux 3.2, non-traced execve-ing thread does not
25 * become traced after execve, even though it has pid == leader's pid
26 * after execve. And yet, strace's waitpid doesn't return ECHILD.
28 * # Run for NUM seconds, not just one second.
29 * # Watch top to check for memory leaks in strace:
30 * strace -oLOG -f ./threaded_execve <NUM>
48 #include <sys/types.h>
50 #include <sys/syscall.h>
52 /* Define clone2 for all arches */
54 extern int __clone2(int (*fn) (void *), void *child_stack_base,
55 size_t stack_size, int flags, void *arg, ...);
56 #define clone2 __clone2
57 #elif defined(__metag__)
58 #define clone2(func, stack_base, size, flags, arg...) \
59 clone(func, stack_base, flags, arg)
61 #define clone2(func, stack_base, size, flags, arg...) \
62 clone(func, (stack_base) + (size), flags, arg)
64 /* Direct calls to syscalls, avoiding libc wrappers */
65 #define syscall_tgkill(pid, tid, sig) syscall(__NR_tgkill, (pid), (tid), (sig))
66 #define syscall_getpid() syscall(__NR_getpid)
67 #define syscall_gettid() syscall(__NR_gettid)
68 #define syscall_exit(v) syscall(__NR_exit, (v));
70 static char my_name[PATH_MAX];
71 static int leader_final_action;
85 sprintf(buf, "%d", leader_final_action);
88 /* This fails with ENOENT if leader has exited by now! :) */
89 execl("/proc/self/exe", "exe", "exe", buf, NULL);
90 /* So fall back to resolved name */
91 execl(my_name, "exe", "exe", buf, NULL);
99 /* malloc gives sufficiently aligned buffer.
100 * long buf[] does not! (on ia64).
102 int cnt = NUM_THREADS;
104 /* As seen in pthread_create(): */
105 clone2(thread1, malloc(16 * 1024), 16 * 1024, 0
108 | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
109 | 0 /* no signal to send on death */
113 clone2(thread2, malloc(16 * 1024), 16 * 1024, 0
116 | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
117 | 0 /* no signal to send on death */
120 /* Various states leader can be while other thread execve's: */
121 switch (leader_final_action % 3) {
122 case 0: syscall_exit(42); /* leader is dead */
123 case 1: for(;;) pause(); /* leader is in syscall */
124 default: for(;;) continue; /* leader is in userspace */
129 main(int argc, char **argv)
131 if (readlink("/proc/self/exe", my_name, sizeof(my_name)-1) <= 0)
134 setbuf(stdout, NULL);
136 if (argv[1] && strcmp(argv[1], "exe") == 0) {
137 leader_final_action = atoi(argv[2]) + 1;
141 printf("%d: thread leader\n", getpid());
143 alarm(argv[1] ? atoi(argv[1]) : 1);