如何在syscall中停止时获得正确的orig_eax值?

我使用ptrace(PTRACE_ATTACH …)连接进程,而在系统调用(如nanosleep())中。 我可以使用PTRACE_GETREGS获取寄存器内容,并且eip位于预期位置(在__kernel_vsyscall中)。 但是,eax和orig_eax寄存器有意想不到的内容:eax通常包含-516,而orig_eax通常为0。

这是我使用的testing程序(取自http://www.linuxjournal.com/article/6210 ,稍作修改):

#include <stdlib.h> #include <stdio.h> #include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <sys/user.h> int main(int argc, char *argv[]) { pid_t traced_process; struct user_regs_struct regs; long ins; if(argc != 2) { printf("Usage: %s <pid to be traced>\n", argv[0]); exit(1); } traced_process = atoi(argv[1]); ptrace(PTRACE_ATTACH, traced_process, NULL, NULL); wait(NULL); ptrace(PTRACE_GETREGS, traced_process, NULL, &regs); printf("eax: %lx (%d); orig_eax: %lx\n", regs.eax, (int)regs.eax, regs.orig_eax); ins = ptrace(PTRACE_PEEKTEXT, traced_process, regs.eip, NULL); printf("EIP: %lx Instruction executed: %lx\n", regs.eip, ins); ptrace(PTRACE_DETACH, traced_process, NULL, NULL); return 0; } 

连接到在另一个terminal上运行的“sleep 10000”命令时输出:

  eax: fffffdfc (-516); orig_eax: 0 EIP: b7711424 Instruction executed: c3595a5d 

eax中的值是什么意思? 为什么orig_eax不包含原始的系统调用号码(如162)? 在这种情况下,我怎样才能真正获得系统呼叫号码?

另外,为什么gdb正确显示“打印$ orig_eax”的“162”?

顺便说一句。 这是在Ubuntu 12.04,内核3.2.0:

  • uname -a:“Linux edgebox 3.2.0-24-generic-pae#37 -Ubuntu SMP Wed Apr 25 10:47:59 UTC 2012 i686 athlon i386 GNU / Linux”
  • / proc / cpuinfo:“AMD Athlon(tm)II Neo K345双核处理器”
  • 文件which sleep :“/ bin / sleep:ELF 32位LSB可执行文件,英特尔80386,版本1(SYSV),dynamic链接(使用共享库),用于GNU / Linux 2.6.24,BuildID [sha1] = 0x0965431bde4d183eaa2fa3e3989098ce46b92129, ”。

所以这是一个32位的PAE内核和64位CPU上的32位Ubuntu安装。

这是在Ubuntu 12.04,内核3.2.0

这并不能唯一标识你的系统。 什么处理器?

我的水晶球告诉我,你正在调用的sleep是一个64位的程序,而你的跟踪程序不是。 或相反亦然。

当你连接到进程时,一个信号被发送到进程,这个进程中断了正在进行的任何系统调用。 大多数系统调用只是返回-EINTR,如果发生这种情况,并希望用户空间代码,如果需要重新启动它们。 还有其他的系统调用可以自动重启,但这是一个更大的话题。

在某些系统调用(其中nanosleep()是一个系统调用)的情况下,进程已经睡了一段指定的时间,所以不要从一开始就重新启动,而只想剩下的时间。 为了达到这个目的,每个线程在内核空间都有一个“重启块”。 当系统调用中断时,它将填充重启块,并返回-516(ERESTART_RESTARTBLOCK)。 内核专门处理这个返回代码:将PC重新回到系统调用之前,并将系统调用号码更改为“restart_syscall”(在您的体系结构中为0)。 当进程重新启动时,显然会调用restart_syscall(使用重启块)来找出要执行的操作。

这种重新启动对于用户来说通常是不可见的,但是ptrace是发现它的一种方法。 但是,我不知道GDB设法为orig_eax获得正确的值。

当睡眠进程被ptrace(PTRACE_ATTACH)停止时,跟踪进程可以获得寄存器(user_regs_struct)信息。 系统调用号是162,这是sys_nanosleep ,eax = -516表示这个中断的系统调用的返回值。