为什么这个ptrace程序说syscall返回-38?

lex*_*xer 5 c linux ptrace

除了我正在跑步之外,它和这个一样execl("/bin/ls", "ls", NULL);.

结果显然是错误的,因为每个系统调用返回-38:

[user@ test]# ./test_trace 
syscall 59 called with rdi(0), rsi(0), rdx(0)
syscall 12 returned with -38
syscall 12 called with rdi(0), rsi(0), rdx(140737288485480)
syscall 9 returned with -38
syscall 9 called with rdi(0), rsi(4096), rdx(3)
syscall 9 returned with -38
syscall 9 called with rdi(0), rsi(4096), rdx(3)
syscall 21 returned with -38
syscall 21 called with rdi(233257948048), rsi(4), rdx(233257828696)
...
Run Code Online (Sandbox Code Playgroud)

谁知道原因?

UPDATE

现在的问题是:

execve called with rdi(4203214), rsi(140733315680464), rdx(140733315681192)
execve returned with 0
execve returned with 0
...
Run Code Online (Sandbox Code Playgroud)

execve回来0 两次,为什么?

Mat*_*ery 11

代码不考虑exec来自子进程的通知,因此最终将syscall条目作为syscall exit处理,并将syscall exit作为syscall条目进行处理.这就是为什么你看到" syscall 12 returned" " syscall 12 called"等(-38ENOSYS其投入RAX作为内核的系统调用入口代码默认返回值.)

正如ptrace(2)手册页所述:

PTRACE_TRACEME

表示此进程将由其父进程跟踪.传递给此进程的任何信号(SIGKILL除外)都将使其停止,并通过wait()通知其父级.此外,此过程对exec()的所有后续调用都将导致向其发送SIGTRAP,从而使父级有机会在新程序开始执行之前获得控制权.[...]

你说你运行的原始代码" 除了我正在运行之外就是这个代码execl("/bin/ls", "ls", NULL);".好吧,它显然不是,因为你正在使用x86_64而不是32位并至少更改了消息.

但是,假设您没有改变太多其他内容,一次wait()唤醒父级,则不是系统调用进入或退出 - 父级尚未执行ptrace(PTRACE_SYSCALL,...).相反,你看到这个孩子已经执行过的通知exec(在x86_64上,系统调用59是execve).

代码错误地将其解释为syscall条目. 然后它调用ptrace(PTRACE_SYSCALL,...),下次父语句被唤醒时,它一个系统调用条目(系统调用12),但代码将其报告为系统调用退出.

请注意,在这种原始情况下,您永远不会看到execve系统调用进入/退出 - 只有附加通知 - 因为父级ptrace(PTRACE_SYSCALL,...)在它发生之后才会执行.

如果您确实安排了代码以便execve捕获系统调用进入/退出,您将看到您观察到的新行为.父将被唤醒3次:一次execve系统调用入口(由于使用的ptrace(PTRACE_SYSCALL,...),一旦execve系统调用退出(也由于使用的ptrace(PTRACE_SYSCALL,...),而对于第三次exec通知(这恰好反正).


这是一个完整的示例(对于x86或x86_64),exec它通过首先停止子进程来注意显示自身的行为:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/reg.h>

#ifdef __x86_64__
#define SC_NUMBER  (8 * ORIG_RAX)
#define SC_RETCODE (8 * RAX)
#else
#define SC_NUMBER  (4 * ORIG_EAX)
#define SC_RETCODE (4 * EAX)
#endif

static void child(void)
{
    /* Request tracing by parent: */
    ptrace(PTRACE_TRACEME, 0, NULL, NULL);

    /* Stop before doing anything, giving parent a chance to catch the exec: */
    kill(getpid(), SIGSTOP);

    /* Now exec: */
    execl("/bin/ls", "ls", NULL);
}

static void parent(pid_t child_pid)
{
    int status;
    long sc_number, sc_retcode;

    while (1)
    {
        /* Wait for child status to change: */
        wait(&status);

        if (WIFEXITED(status)) {
            printf("Child exit with status %d\n", WEXITSTATUS(status));
            exit(0);
        }
        if (WIFSIGNALED(status)) {
            printf("Child exit due to signal %d\n", WTERMSIG(status));
            exit(0);
        }
        if (!WIFSTOPPED(status)) {
            printf("wait() returned unhandled status 0x%x\n", status);
            exit(0);
        }
        if (WSTOPSIG(status) == SIGTRAP) {
            /* Note that there are *three* reasons why the child might stop
             * with SIGTRAP:
             *  1) syscall entry
             *  2) syscall exit
             *  3) child calls exec
             */
            sc_number = ptrace(PTRACE_PEEKUSER, child_pid, SC_NUMBER, NULL);
            sc_retcode = ptrace(PTRACE_PEEKUSER, child_pid, SC_RETCODE, NULL);
            printf("SIGTRAP: syscall %ld, rc = %ld\n", sc_number, sc_retcode);
        } else {
            printf("Child stopped due to signal %d\n", WSTOPSIG(status));
        }
        fflush(stdout);

        /* Resume child, requesting that it stops again on syscall enter/exit
         * (in addition to any other reason why it might stop):
         */
        ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL);
    }
}

int main(void)
{
    pid_t pid = fork();

    if (pid == 0)
        child();
    else
        parent(pid);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

给出这样的东西(这是64位的 - 系统调用号对于32位是不同的;特别execve是11,而不是59):

Child stopped due to signal 19
SIGTRAP: syscall 59, rc = -38
SIGTRAP: syscall 59, rc = 0
SIGTRAP: syscall 59, rc = 0
SIGTRAP: syscall 63, rc = -38
SIGTRAP: syscall 63, rc = 0
SIGTRAP: syscall 12, rc = -38
SIGTRAP: syscall 12, rc = 5324800
...

信号19是明确的SIGSTOP; 如上所述,孩子停三次execve; 然后两次(进入和退出)进行其他系统调用.

如果你真的对所有血腥细节感兴趣ptrace(),那么我所知道的最好的文档就是源代码中的 README-linux-ptrace文件strace.正如它所说,"API很复杂并且有微妙的怪癖"......