除了我正在运行execl("/bin/ls", "ls", NULL);
之外,它与this one相同。
结果显然是错误的,因为每个系统调用都返回-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
...
execve
返回0
两次,为什么?
答案 0 :(得分:11)
代码不考虑来自子进程的exec
的通知,因此最终将syscall条目作为syscall exit处理,并且syscall退出作为syscall条目。这就是为什么你在“syscall 12 returned
”等之前看到“syscall 12 called
”的原因。(-38
是ENOSYS
,它作为默认回报放入RAX内核的系统调用条目代码的值。)
正如ptrace(2)
man page所述:
PTRACE_TRACEME
表示此进程将由其父进程跟踪。传递给此进程的任何信号(SIGKILL除外)都将使其停止,并通过wait()通知其父级。 此外,此过程对exec()的所有后续调用都将导致向其发送SIGTRAP ,使父级有机会在新程序开始执行之前获得控制权。 [...]
您说您运行的原始代码“与this one相同,只是我正在运行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
系统调用进入/退出,您将看到您观察到的新行为。父级将被唤醒三次次:一次用于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;
}
给出这样的东西(这是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很复杂并且有微妙的怪癖”....
答案 1 :(得分:0)
您可以使用perror或strerror打印上次系统错误的可读描述。此错误说明将为您提供更多帮助。
答案 2 :(得分:0)
在一个平底船上,我会说你正在检查eax
或其64位等效物(大概是rax
)作为系统调用的返回码。还有一个用于保存名为orig_eax
的寄存器的插槽,用于重新启动系统调用。
我把这些东西捅了很多但是在我的生活中找不到我的发现。以下是一些相关问题:
再次四处寻找似乎我的记忆是正确的。你会发现你需要的所有东西here in the kernel source(主站点已关闭,幸运的是torvalds现在在github镜像linux)。