我尝试使用
strace
在 C 中重现 ptrace
行为。
我想检查系统调用是否返回错误(以及什么错误1),但我不知道该怎么做?
这是我尝试重现的
strace
的输出示例:
execve("./exit", ["./exit"], 0x7ffff059f250 /* 62 vars */) = 0
read(42, NULL, 0) = -1 EBADF (Bad file descriptor)
exit(0) = ?
+++ exited with 0 +++
我尝试将系统调用2的返回值与-1进行比较,但它们不相等。
我在GLIBC系统调用的实现(
sysdeps/unix/sysv/linux/syscall.c
)中看到使用了INTERNAL_SYSCALL_ERROR_P
宏,这似乎是GLIBC内部使用的宏。我发现了这个宏的两个实现:
sysdeps/unix/sysv/linux/sysdep.h
:#define INTERNAL_SYSCALL_ERROR_P(val) ((unsigned long int)(val) > -4096UL)
sysdeps/unix/sysv/linux/x86_64/x32/times.c
:#define INTERNAL_SYSCALL_ERROR_P(val) ((unsigned long long int)(val) >= -4095LL)
由于第一个位于与系统直接相关的标头中(与第二个不同),我倾向于说这是
syscall
使用的标头。但我不知道它们是否是仿制药,所以我是否可以使用它。
1要恢复 errno 代码,反转系统调用返回值 (
-rax
) 似乎可行。
2使用
ptrace(PTRACE_GETREGS, ...)
,我们可以获得子寄存器,并将它们存储在struct user_regs_struct
中(在sys/user.h
中定义)。访问该结构的 rax
成员,我们得到了系统调用的返回值。
我想我找到了解决方案。
syscall
好像没有设置进位标志。syscall
手册上看到的那样,如果rax
为负数(低于0),则表明存在错误。rax
中的 struct user_regs_struct
是无符号的,我们只需将其转换为有符号的,我们就可以知道系统调用是否返回并出错,以及什么错误(-rax
获取 errno 代码)。
int main(int argc, const char *argv[])
{
struct user_regs_struct regs;
// strace logic ...
if ((long long)regs.rax < 0)
printf(" = -1 %s (%s)\n", strerrorname_np((int)-regs.rax), strerror((int)-regs.rax));
else
printf(" = %llu\n", regs.rax);
// ...
}