On Sun, 21 Nov 2004, Linus Torvalds wrote: > Ok, how about this patch? > > It does basically two things: > > - it makes the x86 version of ptrace be a lot more careful about the TF > bit in eflags, and in particular it never touches it _unless_ the > tracer has explicitly asked for it (ie we set TF only when doing a > PTRACE_SINGESTEP, and we clear it only when it has been set by us, not > if it has been set by the program itself). > > This patch also cleans up the codepaths by doing all the common stuff > in set_singlestep()/clear_singlestep(). > > - It clarifies signal handling, and makes it clear that we always push > the full eflags onto the signal stack, _except_ if the TF bit was set > by an external ptrace user, in which case we hide it so that the tracee > doesn't see it when it looks at its stack contents. > > It also adds a few comments, and makes it clear that the signal handler > itself is always set up with TF _clear_. But if we were single-stepped > into it, we will have notified the debugger, so the debugger obviously > can (and often will) decide to continue single-stepping.
Looks like a nice cleanup. What does the test program below print for you? - Davide #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <linux/user.h> #include <linux/unistd.h> int main(int ac, char **av) { int i, status, res; long start, end; pid_t cpid, pid; struct user_regs_struct ur; struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_DFL; sigaction(SIGCHLD, &sa, NULL); printf("nargs=%d\n", ac); if (ac == 1) goto tracer; printf("arg=%s\n", av[1]); loop: __asm__ volatile ("int $0x80" : "=a" (res) : "0" (__NR_getpid)); goto loop; endloop: exit(0); tracer: if ((cpid = fork()) != 0) goto parent; printf("child=%d\n", getpid()); ptrace(PTRACE_TRACEME, 0, NULL, NULL); execl(av[0], av[0], "child", NULL); exit(0); parent: start = (long) &&loop; end = (long) &&endloop; printf("pchild=%d\n", cpid); for (;;) { pid = wait(&status); if (pid != cpid) continue; res = WSTOPSIG(status); if (ptrace(PTRACE_GETREGS, pid, NULL, &ur)) { printf("[%d] error: ptrace(PTRACE_GETREGS, %d)\n", pid, pid); return 1; } if (ptrace(PTRACE_SINGLESTEP, pid, NULL, res != SIGTRAP ? res: 0)) { perror("ptrace(PTRACE_SINGLESTEP)"); return 1; } if (ur.eip >= start && ur.eip <= end) break; } for (i = 0; i < 15; i++) { printf("waiting ...\n"); pid = wait(&status); printf("done: pid=%d status=%d\n", pid, status); if (pid != cpid) continue; res = WSTOPSIG(status); printf("sig=%d\n", res); if (ptrace(PTRACE_GETREGS, pid, NULL, &ur)) { printf("[%d] error: ptrace(PTRACE_GETREGS, %d)\n", pid, pid); return 1; } printf("EIP=0x%08x\n", ur.eip); if (ptrace(PTRACE_SINGLESTEP, pid, NULL, res != SIGTRAP ? res: 0)) { perror("ptrace(PTRACE_SINGLESTEP)"); return 1; } } if (ptrace(PTRACE_CONT, cpid, NULL, SIGKILL)) { perror("ptrace(PTRACE_SINGLESTEP)"); return 1; } return 0; }