[PATCH v3] Add support for Linux/no-mmu with vfork

Mike Frysinger vapier at gentoo.org
Wed Oct 7 08:10:53 UTC 2009


Systems that lack a MMU cannot use fork() to create the child process.
First we detect if the toolchain has the fork() symbol and if it does not,
we just always use vfork().  If it does, then we try that first.  If it
fails due to ENOSYS, we fall back to using vfork().

Since fork() gets used in a few places, create a strace_fork() macro.
It cannot be a function due to the fun "children of a vfork share the
stack of the parent", so returning from the function the child was
spawned from would clobber the stack when the parent tried to return.

* configure.ac (AC_CHECK_FUNCS): Add fork.
* strace.c (strace_vforked, strace_fork): Define.
(strace_popen, startup_attach, trace, startup_child): Call strace_fork().
(startup_child): Do not SIGSTOP if vforked.
(trace): Skip first exec when starting up after vforked.
* syscall.c (get_scno): Drop Blackfin waitexec checks.

Signed-off-by: Mike Frysinger <vapier at gentoo.org>
---
v3
	- update to latest git which has reworked startup code

 configure.ac |    1 +
 strace.c     |   38 ++++++++++++++++++++++++++++++++------
 syscall.c    |    3 ---
 3 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/configure.ac b/configure.ac
index 95f769e..7b1a8c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -209,6 +209,7 @@ AC_CHECK_LIB(nsl, main)
 fi
 
 AC_CHECK_FUNCS([ \
+	fork \
 	getdents \
 	if_indextoname \
 	inet_ntop \
diff --git a/strace.c b/strace.c
index da8cc4a..241470b 100644
--- a/strace.c
+++ b/strace.c
@@ -212,6 +212,27 @@ foobar()
 #endif /* MIPS */
 #endif /* SVR4 */
 
+/*
+ * Glue for systems without a MMU that cannot provide fork().  Cannot
+ * be a real function as vfork()-ed children may not return from the
+ * function in which they were created (due to shared stack w/parent).
+ */
+#ifdef HAVE_FORK
+static bool strace_vforked = false;
+#define strace_fork() \
+({ \
+	pid_t __child_pid = fork(); \
+	if (__child_pid == -1 && errno == ENOSYS) { \
+		strace_vforked = true; \
+		__child_pid = vfork(); \
+	} \
+	__child_pid; \
+})
+#else
+# define strace_vforked true
+# define strace_fork()  vfork()
+#endif
+
 static int
 set_cloexec_flag(int fd)
 {
@@ -314,7 +335,7 @@ strace_popen(const char *command)
 		return NULL;
 	}
 
-	if ((popen_pid = fork()) == -1)
+	if ((popen_pid = strace_fork()) == -1)
 	{
 		fprintf(stderr, "%s: fork: %s\n",
 			progname, strerror(errno));
@@ -378,7 +399,7 @@ startup_attach(void)
 		sigprocmask(SIG_BLOCK, &blocked_set, NULL);
 
 	if (daemonized_tracer) {
-		pid_t pid = fork();
+		pid_t pid = strace_fork();
 		if (pid < 0) {
 			_exit(1);
 		}
@@ -563,7 +584,7 @@ startup_child (char **argv)
 			progname, filename);
 		exit(1);
 	}
-	strace_child = pid = fork();
+	strace_child = pid = strace_fork();
 	if (pid < 0) {
 		perror("strace: fork");
 		cleanup();
@@ -636,8 +657,11 @@ startup_child (char **argv)
 			 * Induce an immediate stop so that the parent
 			 * will resume us with PTRACE_SYSCALL and display
 			 * this execve call normally.
+			 * Unless of course we're on a no-MMU system where
+			 * we vfork()-ed, so we cannot stop the child.
 			 */
-			kill(getpid(), SIGSTOP);
+			if (!strace_vforked)
+				kill(getpid(), SIGSTOP);
 		} else {
 			struct sigaction sv_sigchld;
 			sigaction(SIGCHLD, NULL, &sv_sigchld);
@@ -1845,7 +1869,7 @@ int pfd;
 	struct procfs_status pfs;
 #endif /* FREEBSD */
 
-	switch (fork()) {
+	switch (strace_fork()) {
 	case -1:
 		perror("fork");
 		_exit(1);
@@ -2445,8 +2469,10 @@ Process %d attached (waiting for parent)\n",
 		 * with STOPSIG equal to some other signal
 		 * than SIGSTOP if we happend to attach
 		 * just before the process takes a signal.
+		 * A no-mmu vforked child won't send up a signal,
+		 * so skip the first (lost) execve notification.
 		 */
-		if ((tcp->flags & TCB_STARTUP) && WSTOPSIG(status) == SIGSTOP) {
+		if ((tcp->flags & TCB_STARTUP) && (WSTOPSIG(status) == SIGSTOP || strace_vforked)) {
 			/*
 			 * This flag is there to keep us in sync.
 			 * Next time this process stops it should
diff --git a/syscall.c b/syscall.c
index a2e6885..6a75c70 100644
--- a/syscall.c
+++ b/syscall.c
@@ -922,9 +922,6 @@ get_scno(struct tcb *tcp)
 # elif defined(BFIN)
 	if (upeek(tcp, PT_ORIG_P0, &scno))
 		return -1;
-	/* Check if we return from execve. */
-	if (tcp->flags & TCB_WAITEXECVE && tcp->flags & TCB_INSYSCALL)
-		tcp->flags &= ~(TCB_INSYSCALL | TCB_WAITEXECVE);
 # elif defined (I386)
 	if (upeek(tcp, 4*ORIG_EAX, &scno) < 0)
 		return -1;
-- 
1.6.5.rc2





More information about the Strace-devel mailing list