[PATCH] Use PTRACE_GETREGS on i386 and x86-64
Denys Vlasenko
dvlasenk at redhat.com
Fri Aug 26 18:31:41 UTC 2011
> This version which uses asm/ptrace.h and its struct pt_regs.
>
> Also, I decided to bite the bullet and convert x86-64 too.
> Especially that on it we also get rid of extra CS read!
>
> Not yet tested on x86-64.
x86-64 testing revealed two minor errors.
Updated patch is below.
diff -d -urpN strace.6/defs.h strace.7/defs.h
--- strace.6/defs.h 2011-08-26 18:32:46.578974479 +0200
+++ strace.7/defs.h 2011-08-26 19:33:36.196672883 +0200
@@ -193,6 +193,12 @@ extern int ptrace(int, int, char *, int,
#define PTRACE_PEEKUSER PTRACE_PEEKUSR
#define PTRACE_POKEUSER PTRACE_POKEUSR
#endif
+#if defined(X86_64) || defined(I386)
+/* For struct pt_regs. x86 strace uses PTRACE_GETREGS.
+ * PTRACE_GETREGS returns registers in the layout of this struct.
+ */
+# include <asm/ptrace.h>
+#endif
#ifdef ALPHA
# define REG_R0 0
# define REG_A0 16
diff -d -urpN strace.6/syscall.c strace.7/syscall.c
--- strace.6/syscall.c 2011-08-25 10:39:36.000000000 +0200
+++ strace.7/syscall.c 2011-08-26 19:10:19.102470614 +0200
@@ -716,7 +716,9 @@ struct tcb *tcp_last = NULL;
#ifdef LINUX
# if defined (I386)
-static long eax;
+static struct pt_regs i386_regs;
+# elif defined(X86_64)
+static struct pt_regs x86_64_regs;
# elif defined (IA64)
long r8, r10, psr; /* TODO: make static? */
long ia32 = 0; /* not static */
@@ -752,8 +754,6 @@ static long r28;
static long r0;
# elif defined(SH64)
static long r9;
-# elif defined(X86_64)
-static long rax;
# elif defined(CRISV10) || defined(CRISV32)
static long r10;
# elif defined(MICROBLAZE)
@@ -895,34 +895,26 @@ get_scno(struct tcb *tcp)
if (upeek(tcp, PT_ORIG_P0, &scno))
return -1;
# elif defined (I386)
- if (upeek(tcp, 4*ORIG_EAX, &scno) < 0)
+ if (ptrace(PTRACE_GETREGS, tcp->pid, NULL, (long) &i386_regs) < 0)
return -1;
+ scno = i386_regs.orig_eax;
# elif defined (X86_64)
- if (upeek(tcp, 8*ORIG_RAX, &scno) < 0)
- return -1;
-
- /* TODO: speed up strace by not doing this at every syscall.
- * We only need to do it after execve.
- */
int currpers;
- long val;
- int pid = tcp->pid;
+ if (ptrace(PTRACE_GETREGS, tcp->pid, NULL, (long) &x86_64_regs) < 0)
+ return -1;
+ scno = x86_64_regs.orig_rax;
/* Check CS register value. On x86-64 linux it is:
* 0x33 for long mode (64 bit)
* 0x23 for compatibility mode (32 bit)
- * It takes only one ptrace and thus doesn't need
- * to be cached.
*/
- if (upeek(tcp, 8*CS, &val) < 0)
- return -1;
- switch (val) {
+ switch (x86_64_regs.cs) {
case 0x23: currpers = 1; break;
case 0x33: currpers = 0; break;
default:
- fprintf(stderr, "Unknown value CS=0x%02X while "
+ fprintf(stderr, "Unknown value CS=0x%08X while "
"detecting personality of process "
- "PID=%d\n", (int)val, pid);
+ "PID=%d\n", (int)x86_64_regs.cs, tcp->pid);
currpers = current_personality;
break;
}
@@ -933,14 +925,13 @@ get_scno(struct tcb *tcp)
*/
unsigned long val, rip, i;
- if (upeek(tcp, 8*RIP, &rip) < 0)
- perror("upeek(RIP)");
+ rip = x86_64_regs.rip;
/* sizeof(syscall) == sizeof(int 0x80) == 2 */
rip -= 2;
errno = 0;
- call = ptrace(PTRACE_PEEKTEXT, pid, (char *)rip, (char *)0);
+ call = ptrace(PTRACE_PEEKTEXT, tcp->pid, (char *)rip, (char *)0);
if (errno)
fprintf(stderr, "ptrace_peektext failed: %s\n",
strerror(errno));
@@ -954,7 +945,7 @@ get_scno(struct tcb *tcp)
fprintf(stderr,
"Unknown syscall opcode (0x%04X) while "
"detecting personality of process "
- "PID=%d\n", (int)call, pid);
+ "PID=%d\n", (int)call, tcp->pid);
break;
}
# endif
@@ -962,14 +953,14 @@ get_scno(struct tcb *tcp)
static const char *const names[] = {"64 bit", "32 bit"};
set_personality(currpers);
fprintf(stderr, "[ Process PID=%d runs in %s mode. ]\n",
- pid, names[current_personality]);
+ tcp->pid, names[current_personality]);
}
# elif defined(IA64)
# define IA64_PSR_IS ((long)1 << 34)
if (upeek(tcp, PT_CR_IPSR, &psr) >= 0)
ia32 = (psr & IA64_PSR_IS) != 0;
if (ia32) {
- if (upeek(tcp, PT_R1, &scno) < 0) /* orig eax */
+ if (upeek(tcp, PT_R1, &scno) < 0)
return -1;
} else {
if (upeek(tcp, PT_R15, &scno) < 0)
@@ -1291,26 +1282,17 @@ syscall_fixup_on_sysenter(struct tcb *tc
#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 (!(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;
- }
+ if (i386_regs.eax != -ENOSYS) {
+ if (debug)
+ fprintf(stderr, "not a syscall entry (eax = %ld)\n", i386_regs.eax);
+ return 0;
}
#elif defined (X86_64)
- if (!(ptrace_setoptions & PTRACE_O_TRACEEXEC)) {
- if (upeek(tcp, 8*RAX, &rax) < 0)
- return -1;
+ {
+ long rax = x86_64_regs.rax;
if (current_personality == 1)
- rax = (long int)(int)rax; /* sign extend from 32 bits */
- if (rax != -ENOSYS && entering(tcp)) {
+ rax = (int)rax; /* sign extend from 32 bits */
+ if (rax != -ENOSYS) {
if (debug)
fprintf(stderr, "not a syscall entry (rax = %ld)\n", rax);
return 0;
@@ -1521,6 +1503,7 @@ syscall_enter(struct tcb *tcp)
for (i = 0; i < nargs; ++i)
tcp->u_arg[i] = regs.uregs[i];
# elif defined(AVR32)
+ /* TODO: make it faster by unrolling into 6 direct assignments */
static const unsigned long *argregp[MAX_ARGS] = { ®s.r12,
®s.r11,
®s.r10,
@@ -1553,14 +1536,26 @@ syscall_enter(struct tcb *tcp)
if (upeek(tcp, REG_GENERAL(syscall_regs[i]), &tcp->u_arg[i]) < 0)
return -1;
# elif defined(X86_64)
- static const int argreg[SUPPORTED_PERSONALITIES][MAX_ARGS] = {
- { 8 * RDI, 8 * RSI, 8 * RDX, 8 * R10, 8 * R8 , 8 * R9 }, /* x86-64 ABI */
- { 8 * RBX, 8 * RCX, 8 * RDX, 8 * RSI, 8 * RDI, 8 * RBP } /* i386 ABI */
- };
-
- for (i = 0; i < nargs; ++i)
- if (upeek(tcp, argreg[current_personality][i], &tcp->u_arg[i]) < 0)
- return -1;
+ (void)i;
+ (void)nargs;
+ if (current_personality == 0) { /* x86-64 ABI */
+ tcp->u_arg[0] = x86_64_regs.rdi;
+ tcp->u_arg[1] = x86_64_regs.rsi;
+ tcp->u_arg[2] = x86_64_regs.rdx;
+ tcp->u_arg[3] = x86_64_regs.r10;
+ tcp->u_arg[4] = x86_64_regs.r8;
+ tcp->u_arg[5] = x86_64_regs.r9;
+ } else { /* i386 ABI */
+ /* TODO: sign-extend lower 32 bit halves here,
+ * instead of doing it in every syscall handler?
+ */
+ tcp->u_arg[0] = x86_64_regs.rbx;
+ tcp->u_arg[1] = x86_64_regs.rcx;
+ tcp->u_arg[2] = x86_64_regs.rdx;
+ tcp->u_arg[3] = x86_64_regs.rsi;
+ tcp->u_arg[4] = x86_64_regs.rdi;
+ tcp->u_arg[5] = x86_64_regs.rbp;
+ }
# elif defined(MICROBLAZE)
for (i = 0; i < nargs; ++i)
if (upeek(tcp, (5 + i) * 4, &tcp->u_arg[i]) < 0)
@@ -1582,7 +1577,16 @@ syscall_enter(struct tcb *tcp)
for (i = 0; i < nargs; ++i)
if (upeek(tcp, (i < 5 ? i : i + 2)*4, &tcp->u_arg[i]) < 0)
return -1;
-# else /* Other architecture (like i386) (32bits specific) */
+# elif defined(I386)
+ (void)i;
+ (void)nargs;
+ tcp->u_arg[0] = i386_regs.ebx;
+ tcp->u_arg[1] = i386_regs.ecx;
+ tcp->u_arg[2] = i386_regs.edx;
+ tcp->u_arg[3] = i386_regs.esi;
+ tcp->u_arg[4] = i386_regs.edi;
+ tcp->u_arg[5] = i386_regs.ebp;
+# else /* Other architecture (32bits specific) */
for (i = 0; i < nargs; ++i)
if (upeek(tcp, i*4, &tcp->u_arg[i]) < 0)
return -1;
@@ -1882,10 +1886,10 @@ get_syscall_result(struct tcb *tcp)
if (upeek(tcp, PT_R0, &r0) < 0)
return -1;
# elif defined (I386)
- if (upeek(tcp, 4*EAX, &eax) < 0)
+ if (ptrace(PTRACE_GETREGS, tcp->pid, NULL, (long) &i386_regs) < 0)
return -1;
# elif defined (X86_64)
- if (upeek(tcp, 8*RAX, &rax) < 0)
+ if (ptrace(PTRACE_GETREGS, tcp->pid, NULL, (long) &x86_64_regs) < 0)
return -1;
# elif defined(IA64)
# define IA64_PSR_IS ((long)1 << 34)
@@ -2053,20 +2057,20 @@ get_error(struct tcb *tcp)
tcp->u_rval = gpr2;
}
# elif defined(I386)
- if (check_errno && is_negated_errno(eax)) {
+ if (check_errno && is_negated_errno(i386_regs.eax)) {
tcp->u_rval = -1;
- u_error = -eax;
+ u_error = -i386_regs.eax;
}
else {
- tcp->u_rval = eax;
+ tcp->u_rval = i386_regs.eax;
}
# elif defined(X86_64)
- if (check_errno && is_negated_errno(rax)) {
+ if (check_errno && is_negated_errno(x86_64_regs.rax)) {
tcp->u_rval = -1;
- u_error = -rax;
+ u_error = -x86_64_regs.rax;
}
else {
- tcp->u_rval = rax;
+ tcp->u_rval = x86_64_regs.rax;
}
# elif defined(IA64)
if (ia32) {
More information about the Strace-devel
mailing list