PTRACE_SYSCALL リクエストを使用できます。これは子プロセスを再起動します (PTRACE_CONT と同様) が、システム コールへの次のエントリまたはシステム コールからの終了で停止するように調整します。例 (x86 用にビルドされたカーネルを想定):
#include <sys/ptrace.h>
#include <signal.h>
#include <linux/user.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char **argv)
{
int status = 0, pid, r;
struct user_regs_struct uregs;
if ((pid = fork()) == 0) {
printf("pid = %d, ppid = %d\n", getpid(), getppid());
ptrace(PTRACE_TRACEME, 0, 0, 0);
kill(getpid(), SIGINT);
r = getpid();
printf("%d\n", r);
} else {
wait(&status);
ptrace(PTRACE_SYSCALL, pid, 0, 0);
wait(&status);
ptrace(PTRACE_GETREGS, pid, 0, &uregs);
/* this prints the syscall number of getpid */
printf("syscall nr: %d\n", uregs.orig_eax);
/* 64 is syscall number of getppid */
uregs.orig_eax = 64;
ptrace(PTRACE_SETREGS, pid, 0, &uregs);
ptrace(PTRACE_CONT, pid, 0, 0);
wait(&status);
if(WIFEXITED(status))
printf("we're done\n");
}
}
子はその PID を出力し、それ自体にシグナルを配信します。これに対する事前の呼び出しのため、ptrace()
停止されます。
親はこれが発生するのを待ち、PTRACE_SYSCALL で子を再起動してから待ちます。次に、子がgetpid
システム コールを呼び出し、もう一度停止します。親プロセスは、PTRACE_GETREGS 呼び出しを使用しeax
て、システム コール番号を保持する子のレジスタを調べます。親はこれを のシステム コール番号に変更し、getppid
もう一度子に続行を許可します。システム コールの呼び出しの前にシステム コール番号が変更されているため、子は のgetppid
代わりに呼び出すようになりgetpid
ました。
この目的での使用ptrace
は移植可能かもしれませんが、私はそれをテストしていません。gdb では、コマンドも使用できますcatch syscall
。