执行跟踪
执行跟踪是一个程序监视另一个程序执行的一种技术。被跟踪的程序一步一步地执行,直到接收到一个信号或调用一个系统调用。执行跟踪由调试程序(debugger)广泛使用,当然还使用其他技术(包括在被调试程序中插入断点及运行时访问它的变量)。与往常一样,我们将集中讨论内核怎样支持执行跟踪,而不讨论调试程序怎样工作。
在Linux中,通过ptrace()系统调用进行执行跟踪,这个系统调用能处理各类跟踪的命令。设置了CAP_SYS_PTRACE权能的进程可以跟踪系统中的任何进程(除了init)。相反,没有CAP_SYS_PTRACE权能的进程P只能跟踪与P有相同属主的进程。此外,两个进程不能同时跟踪一个进程。ptrace()系统调用修改被跟踪进程描述符的parent字段以使它指向跟踪进程,因此,跟踪进程变为被跟踪进程的有效父进程。当执行跟踪终止时,也就是当以PTRACE_DETACH命令调用ptrace()时,这个系统调用把p_pptr设置为real_parent的值,恢复被跟踪进程原来的父进程。与被跟踪程序相关的几个监控事件为:
-
- 一条单独汇编指令执行的结束
- 进入系统调用
- 退出系统调用
- 接收到一个信号
当一个监控的事件发生时,被跟踪的程序停止,并且将SIGCHID信号发送给它的父进程。当父进程希望恢复子进程的执行时,就使用PTRACE_CONT、PTRACE_SINGLESTEP和PTRACE_SYSCALL命令中的一条命令,这取决于父进程要监控哪种事件:
-
- PTRACE_CONT命令只继续执行,子进程将一直执行到收到另一个信号。这种跟踪通过进程描述符ptrace字段中的PF_PTRACED标志实现的,这个标志的检查是由do_signal()函数进行的。
- PTRACE_SINGLESTEP命令强迫子进程执行下一条汇编语言指令,然后再次停止它。这种跟踪是基于80x86机器的eflags寄存器的TF陷阱标志而实现的。当这个标志为1时,在任一条汇编语言指令之后正好产生一个“Debug”异常。相应的异常处理程序只是清掉这个标志,强迫当前进程停止,并发送SIGCHLD信号给父进程。设置TF标志并不是特权操作,因此用户态进程即使在没有ptrace()系统调用的情况下,也能强迫单步执行。内核检查进程描述符中的PT_DTRACE标志,以跟踪子进程是否通过ptrace()进行单步执行。
- PTRACE_SYSCALL命令使被跟踪的进程重新恢复执行,直到一个系统调用被调用。进程停止两次,第一次是在系统调用开始时,第二次是在系统调用终止时。这种跟踪是利用进程描述符中的TIF_SYSCALL_TRACE标志实现的。这个标志是在进程thread_info结构的flags字段中,并在system_call()汇编语言的函数中被检查。
也可以利用Intel处理器的一些调试特点来跟踪进程。例如,父进程使用PTRACE_POKEUSR命令为子进程设置dr0 - dr7调试寄存器的值。当由某调试寄存器监控的事情发生时,CPU产生“Debug”异常,异常处理程序然后挂起被调试的进程并给父进程发送SIGCHLD信号。
可执行格式
Linux标淮的可执行格式是ELF(Executable and Linking Format),它由Unix系统实验室开发并在Unix世界相当流行。著名的Unix操作系统都把ELF作为它们的主要可执行格式。Linux的旧版支持另一种名叫Assembler OUTput Format (a.out)的格式。因为现在ELF非常实用,因此已经很少用a.out格式。
Linux支持很多其他不同格式的可执行文件。在这种方式下,Linux能运行为其他操作系统所编译的程序,如MS-DOS的EXE程序。有几种可执行格式,如Java或bash脚本,是与平台无关的。由类型为linux_binfmt的对象所描述的可执行格式实质上提供以下三种方法:
-
- load_binary 通过读存放在可执行文件中的信息为当前进程建立一个新的执行环境。
- load_shlib 用于动