为什么 strace 不使用测试系统调用来找出它们在系统调用表中的索引?

为什么 strace 不使用测试系统调用来找出它们在系统调用表中的索引?

我理解该strace命令用于ptrace(PTRACE_PEEKUSER, child, __builtin_offsetof(struct user, regs.orig_eax))查找被跟踪子进程所捕获的系统调用的索引。然后,为了将索引转换为系统调用函数名称,它通过 grepping 安装中存在的 Linux 源代码头文件建立了表。

这种方法肯定没有记录,而且容易失败,因为源代码声明的位置和语法没有记录,必须通过 grepping 找到,而且可能会以未知的方式发生变化。我这样说对吗?

如果是这样,那么为什么strace不使用下面的方法,在我看来,这种方法更简单,只依赖于文档,因此是万无一失的。

在重启后第一次运行时,strace发送一个测试系统调用(每个系统调用函数一个),捕获它,并观察子进程使用的系统调用索引。这将提供一个完整且正确的自定义表,该表可以存储在已知的文件中,以供进一步调用strace

我确信这种方法一定被考虑过,因为它并不是什么特别巧妙的方法。所以它一定有问题。问题是什么?

答案1

因为在如此低的层次上没有系统调用名称的概念。不可能strace说“嘿,让我们打个电话看看号码是多少!”。它只能根据系统调用号本身进行调用。这是因为当系统调用号保存到或寄存器中,并且进程调用或时,fcntl()系统调用才会进行。eaxraxint 0x80syscall

尽管它可以从 C 库调用包装器函数,但不能保证系统调用与包装器同名。例如,如果它试图open()通过调用同名的 libc 包装器并检查所使用的系统调用号来找出系统调用号,它会错误地得出结论,认为它是系统调用 257,而实际上它是系统调用 2。这是因为该包装器函数实际上调用的是openat(),而不是open()。一个简单的演示 shell 日志:

$ cat open.c
#include <fcntl.h>

int main(void)
{
    open("/dev/null", O_RDONLY);
}
$ gcc open.c
$ strace -e trace=%file -P "/dev/null" ./a.out
openat(AT_FDCWD, "/dev/null", O_RDONLY) = 3
+++ exited with 0 +++

现在轮到你而是使用 来执行此操作syscall(SYS_open, "/dev/null", O_RDONLY),但这样一来您又要依赖标头中定义的常量,那么为什么不直接绕过中间人,直接strace使用 C 标头中的系统调用列表进行构建呢?这就是它的strace作用。

相关内容