9

私が走っていることを除いて、これと同じです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)
...

誰も理由を知っていますか?

アップデート

今問題は次のとおりです。

execve called with rdi(4203214), rsi(140733315680464), rdx(140733315681192)
execve returned with 0
execve returned with 0
...

execve0 なぜ二度戻ったのですか?

4

3 に答える 3

14

コードは子からの通知を考慮していないため、execsyscall エントリを syscall exit として処理し、syscall exit を syscall entry として処理することになります。そのためsyscall 12 returned「_ _ _syscall 12 called-38ENOSYS

ptrace(2)マニュアルページに記載されているように:

PTRACE_TRACEME

このプロセスがその親によってトレースされることを示します。このプロセスにシグナル (SIGKILL を除く) が送信されると、プロセスが停止し、wait() を介してその親に通知されます。また、このプロセスによる exec() への後続のすべての呼び出しにより、SIGTRAP がそれに送信され、新しいプログラムが実行を開始する前に、親が制御を取得する機会が与えられます。[...]

あなたが実行していた元のコードは、「私が実行していることを除いて、これexecl("/bin/ls", "ls", NULL);と同じ」とおっしゃいました。32 ビットではなく x86_64 を使用していて、少なくともメッセージを変更しているため、明らかにそうではありません。

しかし、他にあまり変更を加えていないと仮定すると、最初に が親を起動したときwait()、それはシステムコールの開始または終了のためではなく、親はまだ実行されptrace(PTRACE_SYSCALL,...)ていません。代わりに、子が実行したというこの通知が表示されexecます (x86_64 では、syscall 59 は ですexecve)。

コードはそれを syscall エントリとして誤って解釈します。 次にを呼び出しptrace(PTRACE_SYSCALL,...)、次に親が起こされたときsyscall エントリ (syscall 12) のためですが、コードはそれを syscall exit として報告します。

この元のケースでは、システムコールの開始/終了は表示されず、追加の通知のみが表示されることに注意してください。これは、親が発生するまでexecve実行されないためです。ptrace(PTRACE_SYSCALL,...)

システムコールの入口/出口がキャッチされるようにコードを調整すると、観察した新しい動作が表示さます。execve親は3回起動されます。1 回目はexecvesyscall の開始 ( の使用による) ptrace(PTRACE_SYSCALL,...)、1 回はexecvesyscall の終了 (これも の使用によるptrace(PTRACE_SYSCALL,...))、3 回目は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;
}

次のような結果が得られます (これは 64 ビットの場合です。システム コール番号は 32 ビットでは異なります。特にexecve、59 ではなく 11 です)。

子は信号19で停車
SIGTRAP: システムコール 59、rc = -38
SIGTRAP: システムコール 59、rc = 0
SIGTRAP: システムコール 59、rc = 0
SIGTRAP: システムコール 63、rc = -38
SIGTRAP: システムコール 63、rc = 0
SIGTRAP: システムコール 12、rc = -38
SIGTRAP: システムコール 12、rc = 5324800
...

シグナル 19 は明示的SIGSTOPです。上記のように、子供は3回停止execveします。その後、他のシステム コールに対して 2 回 (入口と出口)。

のすべての悲惨な詳細に本当に興味があるなら、ptrace()私が知っている最高のドキュメントは ソースREADME-linux-ptrace内のファイルです。strace「API は複雑で、微妙なクセがあります」....

于 2011-09-23T00:23:27.573 に答える
0

パントでは、システムコールのリターンコードについてeax、またはそれに相当する64ビット(おそらく)を調べていると思います。システムコールを再開するために使用される、raxという名前のこのレジスタを保存するための追加のスロットがあります。orig_eax

私はこのようなものをかなりよく調べましたが、私の人生のために私の発見を見つけることができません。関連する質問は次のとおりです。

Update0

もう一度覗いてみると、私の記憶は正しいようです。必要なものはすべてカーネルソースにあります(メインサイトはダウンしていますが、幸いなことに、torvaldsはgithubのLinuxをミラーリングしています)。

于 2011-09-23T14:21:20.443 に答える
0

perrorまたはstrerrorを含む最後のシステムエラーの人間が読める形式の説明を印刷できます。このエラーの説明は、大幅に役立ちます。

于 2011-09-22T12:51:56.753 に答える