2012-01-27 18:37:13 +04:00
/*
* Create NUM_THREADS threads which print " 1 " and sleep in pause ( ) .
* Then create another thread which prints " 2 " , and re - execs the program .
* The leader then either sleeps in pause ( ) , or exits if $ LEADER_EXIT is set .
* This triggers " execve'ed thread replaces thread leader " case .
*
* gcc - Wall - Os - o threaded_execve threaded_execve . c
*
* Try running it under strace like this :
*
* # Should not be confused by traced execve - ing thread
* # replacing traced leader :
2012-03-17 04:24:25 +04:00
* strace - oLOG - f . / threaded_execve
2012-01-27 18:37:13 +04:00
*
* # Same , but different output mode . Output after execve
* # should go into leader ' s LOG . < pid > file , not into execve ' ed
* # thread ' s log file :
2012-03-17 04:24:25 +04:00
* strace - oLOG - ff . / threaded_execve
2012-01-27 18:37:13 +04:00
*
* # Should not be confused by non - traced execve - ing thread
* # replacing traced leader :
2012-03-17 04:24:25 +04:00
* strace - oLOG . / threaded_execve
* ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
2012-01-27 18:37:13 +04:00
* In Linux 3.2 , non - traced execve - ing thread does not
* become traced after execve , even though it has pid = = leader ' s pid
2012-01-28 04:25:03 +04:00
* after execve . And yet , strace ' s waitpid doesn ' t return ECHILD .
2012-01-27 18:37:13 +04:00
*
* # Run for NUM seconds , not just one second .
* # Watch top to check for memory leaks in strace :
2012-03-17 04:24:25 +04:00
* strace - oLOG - f . / threaded_execve < NUM >
2012-01-27 18:37:13 +04:00
*
*/
# define NUM_THREADS 1
# define _GNU_SOURCE 1
# include <assert.h>
# include <limits.h>
# include <stddef.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <errno.h>
# include <stdio.h>
# include <sched.h>
# include <signal.h>
# include <dirent.h>
# include <fcntl.h>
# include <sys/types.h>
# include <sys/wait.h>
2016-08-09 17:38:29 +03:00
# include <asm/unistd.h>
2012-01-27 18:37:13 +04:00
/* Define clone2 for all arches */
# ifdef __ia64__
extern int __clone2 ( int ( * fn ) ( void * ) , void * child_stack_base ,
size_t stack_size , int flags , void * arg , . . . ) ;
2018-12-30 18:35:21 +03:00
# define clone2 __clone2
2013-05-01 16:16:50 +04:00
# elif defined(__metag__)
2018-12-30 18:35:21 +03:00
# define clone2(func, stack_base, size, flags, arg...) \
2017-06-18 01:23:09 +03:00
clone ( func , stack_base , flags , arg )
2012-01-27 18:37:13 +04:00
# else
2018-12-30 18:35:21 +03:00
# define clone2(func, stack_base, size, flags, arg...) \
2017-06-18 01:23:09 +03:00
clone ( func , ( stack_base ) + ( size ) , flags , arg )
2012-01-27 18:37:13 +04:00
# endif
/* Direct calls to syscalls, avoiding libc wrappers */
# define syscall_tgkill(pid, tid, sig) syscall(__NR_tgkill, (pid), (tid), (sig))
# define syscall_getpid() syscall(__NR_getpid)
# define syscall_gettid() syscall(__NR_gettid)
2017-06-18 00:47:57 +03:00
# define syscall_exit(v) syscall(__NR_exit, (v))
2012-01-27 18:37:13 +04:00
static char my_name [ PATH_MAX ] ;
2012-03-17 04:24:25 +04:00
static int leader_final_action ;
2012-01-27 18:37:13 +04:00
static int
thread1 ( void * unused )
{
write ( 1 , " 1 " , 1 ) ;
2017-06-18 01:23:09 +03:00
for ( ; ; )
pause ( ) ;
2012-01-27 18:37:13 +04:00
return 0 ;
}
static int
thread2 ( void * unused )
{
2012-03-17 04:24:25 +04:00
char buf [ 64 ] ;
sprintf ( buf , " %d " , leader_final_action ) ;
2012-01-27 18:37:13 +04:00
write ( 1 , " 2 " , 1 ) ;
usleep ( 20 * 1000 ) ;
/* This fails with ENOENT if leader has exited by now! :) */
2012-03-17 04:24:25 +04:00
execl ( " /proc/self/exe " , " exe " , " exe " , buf , NULL ) ;
2012-01-27 18:37:13 +04:00
/* So fall back to resolved name */
2012-03-17 04:24:25 +04:00
execl ( my_name , " exe " , " exe " , buf , NULL ) ;
2017-06-18 01:23:09 +03:00
for ( ; ; )
pause ( ) ;
2012-01-27 18:37:13 +04:00
return 0 ;
}
static void
2012-03-17 04:24:25 +04:00
thread_leader ( void )
2012-01-27 18:37:13 +04:00
{
/* malloc gives sufficiently aligned buffer.
* long buf [ ] does not ! ( on ia64 ) .
*/
int cnt = NUM_THREADS ;
while ( - - cnt > = 0 ) {
/* As seen in pthread_create(): */
clone2 ( thread1 , malloc ( 16 * 1024 ) , 16 * 1024 , 0
| CLONE_VM
| CLONE_FS
| CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
| 0 /* no signal to send on death */
, NULL ) ;
usleep ( 20 * 1000 ) ;
}
clone2 ( thread2 , malloc ( 16 * 1024 ) , 16 * 1024 , 0
| CLONE_VM
| CLONE_FS
| CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM
| 0 /* no signal to send on death */
, NULL ) ;
2012-03-17 04:24:25 +04:00
/* Various states leader can be while other thread execve's: */
switch ( leader_final_action % 3 ) {
2017-06-18 01:23:09 +03:00
case 0 :
syscall_exit ( 42 ) ; /* leader is dead */
case 1 :
for ( ; ; )
pause ( ) ; /* leader is in syscall */
default :
for ( ; ; )
continue ; /* leader is in userspace */
2012-03-17 04:24:25 +04:00
}
2012-01-27 18:37:13 +04:00
}
int
main ( int argc , char * * argv )
{
if ( readlink ( " /proc/self/exe " , my_name , sizeof ( my_name ) - 1 ) < = 0 )
return 1 ;
setbuf ( stdout , NULL ) ;
2012-03-17 04:24:25 +04:00
if ( argv [ 1 ] & & strcmp ( argv [ 1 ] , " exe " ) = = 0 ) {
leader_final_action = atoi ( argv [ 2 ] ) + 1 ;
thread_leader ( ) ;
}
2012-01-27 18:37:13 +04:00
printf ( " %d: thread leader \n " , getpid ( ) ) ;
alarm ( argv [ 1 ] ? atoi ( argv [ 1 ] ) : 1 ) ;
2012-03-17 04:24:25 +04:00
thread_leader ( ) ;
2012-01-27 18:37:13 +04:00
2017-06-18 01:23:09 +03:00
return 0 ;
2012-01-27 18:37:13 +04:00
}