137 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Spawn a child and set it up for ptrace()-ing
 | |
|  *
 | |
|  * Copyright (c) 2008 Analog Devices Inc.
 | |
|  *
 | |
|  * Licensed under the GPL-2 or later
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * To use:
 | |
|  *  - add this line after your normal includes:
 | |
|  *       #include "spawn_ptrace_child.c"
 | |
|  *  - add this line to the top of your main():
 | |
|  *       make_a_baby(argc, argv);
 | |
|  *  - access the child pid via the "pid" variable
 | |
|  */
 | |
| 
 | |
| #include <errno.h>      /* errno */
 | |
| #include <signal.h>     /* signal() */
 | |
| #include <stdbool.h>    /* true */
 | |
| #include <string.h>     /* strcmp() */
 | |
| #include <unistd.h>     /* execlp() sleep() vfork() */
 | |
| #include <sys/ptrace.h> /* ptrace() */
 | |
| #include <sys/wait.h>
 | |
| 
 | |
| #include "test.h"
 | |
| 
 | |
| static pid_t pid;
 | |
| 
 | |
| #ifdef __sparc__
 | |
| /* sparce swaps addr/data for get/set regs */
 | |
| # define maybe_swap(request, addr, data) \
 | |
| do { \
 | |
| 	if (request == PTRACE_GETREGS || request == PTRACE_SETREGS) { \
 | |
| 		void *__s = addr; \
 | |
| 		addr = data; \
 | |
| 		data = __s; \
 | |
| 	} \
 | |
| } while (0)
 | |
| #else
 | |
| # define maybe_swap(...)
 | |
| #endif
 | |
| #define vptrace(request, pid, addr, data) \
 | |
| ({ \
 | |
| 	errno = 0; \
 | |
| 	long __ret; \
 | |
| 	void *__addr = (void *)(addr); \
 | |
| 	void *__data = (void *)(data); \
 | |
| 	maybe_swap(request, __addr, __data); \
 | |
| 	__ret = ptrace(request, pid, __addr, __data); \
 | |
| 	if (__ret && errno) \
 | |
| 		perror("ptrace(" #request ", " #pid ", " #addr ", " #data ")"); \
 | |
| 	__ret; \
 | |
| })
 | |
| 
 | |
| static void make_a_baby(int argc, char *argv[])
 | |
| {
 | |
| 	if (argc > 1 && !strcmp(argv[1], "child")) {
 | |
| 		/* if we're the child, just sit around doing nothing */
 | |
| 		int i = 60;
 | |
| 		while (i--) {
 | |
| 			close(-100);
 | |
| 			sleep(1);
 | |
| 		}
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	signal(SIGCHLD, SIG_IGN);
 | |
| 
 | |
| 	pid = vfork();
 | |
| 	if (pid == -1) {
 | |
| 		tst_resm(TFAIL, "vfork() failed");
 | |
| 		tst_exit();
 | |
| 	} else if (pid) {
 | |
| 		int status;
 | |
| 
 | |
| 		if (wait(&status) != pid) {
 | |
| 			tst_brkm(TBROK | TERRNO, NULL, "wait(%i) failed: %#x", pid, status);
 | |
| 			kill(pid, SIGKILL);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if (!WIFSTOPPED(status)) {
 | |
| 			tst_brkm(TBROK, NULL, "child status not stopped: %#x", status);
 | |
| 			kill(pid, SIGKILL);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	errno = 0;
 | |
| 	long ret = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
 | |
| 	if (ret && errno) {
 | |
| 		tst_resm(TFAIL, "PTRACE_TRACEME failed");
 | |
| 		tst_exit();
 | |
| 	}
 | |
| 
 | |
| 	execlp(argv[0], argv[0], "child", NULL);
 | |
| 	tst_resm(TFAIL, "execlp() failed");
 | |
| 	tst_exit();
 | |
| }
 | |
| 
 | |
| #define SPT(x) [PTRACE_##x] = #x,
 | |
| static char *strings[] = {
 | |
| 	SPT(TRACEME)
 | |
| 	SPT(PEEKTEXT)
 | |
| 	SPT(PEEKDATA)
 | |
| 	SPT(PEEKUSER)
 | |
| 	SPT(POKETEXT)
 | |
| 	SPT(POKEDATA)
 | |
| 	SPT(POKEUSER)
 | |
| #ifdef PTRACE_GETREGS
 | |
| 	SPT(GETREGS)
 | |
| #endif
 | |
| #ifdef PTRACE_SETREGS
 | |
| 	SPT(SETREGS)
 | |
| #endif
 | |
| #ifdef PTRACE_GETSIGINFO
 | |
| 	SPT(GETSIGINFO)
 | |
| #endif
 | |
| #ifdef PTRACE_SETSIGINFO
 | |
| 	SPT(SETSIGINFO)
 | |
| #endif
 | |
| #ifdef PTRACE_GETFGREGS
 | |
| 	SPT(GETFGREGS)
 | |
| #endif
 | |
| #ifdef PTRACE_SETFGREGS
 | |
| 	SPT(SETFGREGS)
 | |
| #endif
 | |
| 	SPT(KILL)
 | |
| 	SPT(SINGLESTEP)
 | |
| };
 | |
| static inline char *strptrace(int request)
 | |
| {
 | |
| 	return strings[request];
 | |
| }
 |