You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判断系统调用是否返回错误?基于ptrace模拟strace的疑问

如何用ptrace判断Linux系统调用的错误返回及错误类型

核心问题解析

Linux内核的系统调用错误返回机制和GLIBC封装的syscall()函数逻辑一致,但不能直接用返回值和-1对比——内核会把错误码以负数偏移的方式存在返回值里,而非直接返回-1。


一、判断系统调用是否出错

Linux内核规定:当系统调用出错时,返回值(rax寄存器的值)会落在[-4096, -1]这个区间里(对应无符号数的0xFFFFFFFFFFFFF000到0xFFFFFFFFFFFFFFFF)。这就是你找到的GLIBC内部宏INTERNAL_SYSCALL_ERROR_P的核心逻辑,这个规则是x86_64 Linux的通用内核行为,不是GLIBC专属,你可以直接复用:

#define SYSCALL_ERROR_P(val) ((unsigned long)(val) > -4096UL)

注:x32架构的判断逻辑是>= -4095LL,但x86_64下用上面的宏足够覆盖所有场景。

二、获取具体错误类型

当通过上面的宏判断为错误返回时,直接对rax的值取反(-rax)就能得到对应的errno错误码,再通过strerror()函数或者错误常量(如EBADF)即可转换成可读的错误信息,和strace的输出格式对应。

三、代码实现片段

结合ptrace获取寄存器的逻辑,核心处理部分如下:

#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#define SYSCALL_ERROR_P(val) ((unsigned long)(val) > -4096UL)

// 示例:处理系统调用返回值
void handle_syscall_ret(struct user_regs_struct *regs) {
    unsigned long ret_val = regs->rax;
    if (SYSCALL_ERROR_P(ret_val)) {
        int err_num = -ret_val;
        // 模拟strace的输出格式:= -1 EBADF (Bad file descriptor)
        printf("= -1 %s (%s)\n", strerror(err_num), strerror(err_num));
    } else {
        printf("= %ld\n", ret_val);
    }
}

int main() {
    pid_t child = fork();
    if (child == 0) {
        ptrace(PTRACE_TRACEME, 0, NULL, NULL);
        execvp("./exit", (char*[]){"exit", NULL});
    } else {
        int status;
        int in_syscall = 0;
        wait(&status);
        
        while (WIFSTOPPED(status)) {
            struct user_regs_struct regs;
            ptrace(PTRACE_GETREGS, child, NULL, &regs);
            
            // 区分系统调用入口和出口:第一次停在入口,第二次停在出口
            if (in_syscall) {
                handle_syscall_ret(&regs);
                // 特殊处理不会返回的系统调用(如exit、execve)
                long syscall_num = regs.orig_rax;
                if (syscall_num == 60 /* exit */ || syscall_num == 59 /* execve */) {
                    printf("= ?\n");
                }
            }
            in_syscall = !in_syscall;
            
            ptrace(PTRACE_SYSCALL, child, NULL, NULL);
            wait(&status);
        }
        printf("+++ exited with %d +++\n", WEXITSTATUS(status));
    }
    return 0;
}

四、关键注意事项

  • 区分系统调用的入口/出口:PTRACE_SYSCALL会在系统调用的进入和退出两个节点触发停止,需要用状态标记(如in_syscall变量)只在出口时处理返回值。
  • 特殊系统调用处理:像exit、execve这类调用成功后不会返回用户态,需要单独判断并输出= ?,和strace的行为对齐。

内容的提问来源于stack exchange,提问作者Clement-Z4RM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 21:33:14