]> granicus.if.org Git - strace/commitdiff
Split syscall_fixup into enter/exit pair of functions
authorDenys Vlasenko <dvlasenk@redhat.com>
Thu, 25 Aug 2011 08:23:00 +0000 (10:23 +0200)
committerDenys Vlasenko <dvlasenk@redhat.com>
Thu, 25 Aug 2011 08:23:00 +0000 (10:23 +0200)
* syscall.c: Create syscall_fixup_on_sysexit() which is a copy of
syscall_fixup().
(trace_syscall_exiting): Call syscall_fixup_on_sysexit() instead of
syscall_fixup().

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
syscall.c

index d1f0cf22efb3b71fd5cc30651a507b56a6173cdc..521ca10b1c51366783d1685c7d247013d50ac789 100644 (file)
--- a/syscall.c
+++ b/syscall.c
@@ -1237,7 +1237,7 @@ known_scno(struct tcb *tcp)
        return scno;
 }
 
-/* Called in trace_syscall() at each syscall entry and exit.
+/* Called at each syscall entry.
  * Returns:
  * 0: "ignore this ptrace stop", bail out of trace_syscall() silently.
  * 1: ok, continue in trace_syscall().
@@ -1987,6 +1987,185 @@ get_syscall_result(struct tcb *tcp)
        return 1;
 }
 
+/* Called at each syscall exit.
+ * Returns:
+ * 0: "ignore this ptrace stop", bail out of trace_syscall() silently.
+ * 1: ok, continue in trace_syscall().
+ * other: error, trace_syscall() should print error indicator
+ *    ("????" etc) and bail out.
+ */
+static int
+syscall_fixup_on_sysexit(struct tcb *tcp)
+{
+#ifdef USE_PROCFS
+       int scno = known_scno(tcp);
+
+       if (entering(tcp)) {
+               if (tcp->status.PR_WHY != PR_SYSENTRY) {
+                       if (
+                           scno == SYS_fork
+#ifdef SYS_vfork
+                           || scno == SYS_vfork
+#endif /* SYS_vfork */
+#ifdef SYS_fork1
+                           || scno == SYS_fork1
+#endif /* SYS_fork1 */
+#ifdef SYS_forkall
+                           || scno == SYS_forkall
+#endif /* SYS_forkall */
+#ifdef SYS_rfork1
+                           || scno == SYS_rfork1
+#endif /* SYS_fork1 */
+#ifdef SYS_rforkall
+                           || scno == SYS_rforkall
+#endif /* SYS_rforkall */
+                           ) {
+                               /* We are returning in the child, fake it. */
+                               tcp->status.PR_WHY = PR_SYSENTRY;
+                               trace_syscall(tcp);
+                               tcp->status.PR_WHY = PR_SYSEXIT;
+                       }
+                       else {
+                               fprintf(stderr, "syscall: missing entry\n");
+                               tcp->flags |= TCB_INSYSCALL;
+                       }
+               }
+       }
+       else {
+               if (tcp->status.PR_WHY != PR_SYSEXIT) {
+                       fprintf(stderr, "syscall: missing exit\n");
+                       tcp->flags &= ~TCB_INSYSCALL;
+               }
+       }
+#endif /* USE_PROCFS */
+
+#ifdef SUNOS4
+       if (entering(tcp)) {
+               if (scno == 0) {
+                       fprintf(stderr, "syscall: missing entry\n");
+                       tcp->flags |= TCB_INSYSCALL;
+               }
+       }
+       else {
+               if (scno != 0) {
+                       if (debug) {
+                               /*
+                                * This happens when a signal handler
+                                * for a signal which interrupted a
+                                * a system call makes another system call.
+                                */
+                               fprintf(stderr, "syscall: missing exit\n");
+                       }
+                       tcp->flags &= ~TCB_INSYSCALL;
+               }
+       }
+#endif /* SUNOS4 */
+
+#ifdef LINUX
+       /* A common case of "not a syscall entry" is post-execve SIGTRAP */
+#if defined (I386)
+       /* With PTRACE_O_TRACEEXEC, post-execve SIGTRAP is disabled.
+        * Every extra ptrace call is expensive, so check EAX
+        * on syscall entry only if PTRACE_O_TRACEEXEC is not enabled:
+        */
+       if (entering(tcp) && !(ptrace_setoptions & PTRACE_O_TRACEEXEC)) {
+               if (upeek(tcp, 4*EAX, &eax) < 0)
+                       return -1;
+               if (eax != -ENOSYS) {
+                       if (debug)
+                               fprintf(stderr, "not a syscall entry (eax = %ld)\n", eax);
+                       return 0;
+               }
+       }
+#elif defined (X86_64)
+       if (entering(tcp) && !(ptrace_setoptions & PTRACE_O_TRACEEXEC)) {
+               if (upeek(tcp, 8*RAX, &rax) < 0)
+                       return -1;
+               if (current_personality == 1)
+                       rax = (long int)(int)rax; /* sign extend from 32 bits */
+               if (rax != -ENOSYS && entering(tcp)) {
+                       if (debug)
+                               fprintf(stderr, "not a syscall entry (rax = %ld)\n", rax);
+                       return 0;
+               }
+       }
+#elif defined (S390) || defined (S390X)
+       if (upeek(tcp, PT_GPR2, &gpr2) < 0)
+               return -1;
+       if (syscall_mode != -ENOSYS)
+               syscall_mode = tcp->scno;
+       if (gpr2 != syscall_mode && entering(tcp)) {
+               if (debug)
+                       fprintf(stderr, "not a syscall entry (gpr2 = %ld)\n", gpr2);
+               return 0;
+       }
+       else if (((tcp->flags & (TCB_INSYSCALL|TCB_WAITEXECVE))
+                 == (TCB_INSYSCALL|TCB_WAITEXECVE))
+                && (gpr2 == -ENOSYS || gpr2 == tcp->scno)) {
+               /*
+                * Return from execve.
+                * Fake a return value of zero.  We leave the TCB_WAITEXECVE
+                * flag set for the post-execve SIGTRAP to see and reset.
+                */
+               gpr2 = 0;
+       }
+#elif defined (POWERPC)
+# define SO_MASK 0x10000000
+       if (upeek(tcp, sizeof(unsigned long)*PT_CCR, &flags) < 0)
+               return -1;
+       if (upeek(tcp, sizeof(unsigned long)*PT_R3, &result) < 0)
+               return -1;
+       if (flags & SO_MASK)
+               result = -result;
+#elif defined (M68K)
+       if (upeek(tcp, 4*PT_D0, &d0) < 0)
+               return -1;
+       if (d0 != -ENOSYS && entering(tcp)) {
+               if (debug)
+                       fprintf(stderr, "not a syscall entry (d0 = %ld)\n", d0);
+               return 0;
+       }
+#elif defined (ARM)
+       /*
+        * Nothing required
+        */
+#elif defined(BFIN)
+       if (upeek(tcp, PT_R0, &r0) < 0)
+               return -1;
+#elif defined (HPPA)
+       if (upeek(tcp, PT_GR28, &r28) < 0)
+               return -1;
+#elif defined(IA64)
+       if (upeek(tcp, PT_R10, &r10) < 0)
+               return -1;
+       if (upeek(tcp, PT_R8, &r8) < 0)
+               return -1;
+       if (ia32 && r8 != -ENOSYS && entering(tcp)) {
+               if (debug)
+                       fprintf(stderr, "not a syscall entry (r8 = %ld)\n", r8);
+               return 0;
+       }
+#elif defined(CRISV10) || defined(CRISV32)
+       if (upeek(tcp, 4*PT_R10, &r10) < 0)
+               return -1;
+       if (r10 != -ENOSYS && entering(tcp)) {
+               if (debug)
+                       fprintf(stderr, "not a syscall entry (r10 = %ld)\n", r10);
+               return 0;
+       }
+#elif defined(MICROBLAZE)
+       if (upeek(tcp, 3 * 4, &r3) < 0)
+               return -1;
+       if (r3 != -ENOSYS && entering(tcp)) {
+               if (debug)
+                       fprintf(stderr, "not a syscall entry (r3 = %ld)\n", r3);
+               return 0;
+       }
+#endif
+#endif /* LINUX */
+       return 1;
+}
+
 #ifdef LINUX
 /*
  * Check the syscall return value register value for whether it is
@@ -2302,7 +2481,7 @@ trace_syscall_exiting(struct tcb *tcp)
        if (res == 0)
                return res;
        if (res == 1)
-               res = syscall_fixup(tcp);
+               res = syscall_fixup_on_sysexit(tcp);
        if (res == 0)
                return res;
        if (res == 1)