通过与程序陷阱标志和陷阱信号处理程序,在崩溃试图vsyscall单步执行 [英] Trying to single step through program with trap-flag and trap-signal-handler, crash on vsyscall
问题描述
我想创建一个程序的执行完整的指令跟踪,收集一些统计数据等。我第一次使用Linux的ptrace的功能步程序(使用教程的这里)。这产生两个过程,跟踪的一个和调试器,它们通过信号通信。我只得到大约每秒16K指令(在1.6GHz的凌动),所以这是太慢了什么不平凡的。
I'd like to create a complete instruction trace of the execution of a program, to collect some stats etc. I first tried using linux' ptrace functionality to step through a program (using the tutorial here). This creates two processes, the traced one and the debugger, and they communicate via signals. I only got around 16K instructions per second (on 1.6GHz Atom), so this is too slow for anything non-trivial.
我认为是通过信号的进程间通信速度太慢,所以我试图在同一个进程中执行设置调试:设置陷阱标志,并创建一个信号处理程序。当一个软件中断是用来做系统调用,陷阱标志应保存,内核会用它自己的标志 - 所以我想。但是我的程序以某种方式得到由信号SIGTRAP杀害。
I thought the interprocess communication via signals is too slow, so I tried setting up the debugging in the same process as the execution: Set the trap flag, and create a signal handler. When a software interrupt is used to make a syscall, the trap flag should be saved, the kernel would use it's own flags - so I thought. But my program somehow gets killed by signal SIGTRAP.
这是我设置:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int cycle = 0;
void trapHandler(int signum) {
if (cycle % 262144 == 0) {
write(STDOUT_FILENO," trap\n",6);
}
cycle += 1;
}
void startTrace() {
// set up signal handler
signal(SIGTRAP, trapHandler);
// set trap flag
asm volatile("pushfl\n"
"orl $0x100, (%esp)\n"
"popfl\n"
);
}
void printRock() {
char* s = "Rock\n";
asm(
"movl $5, %%edx\n" // message length
"movl %0, %%ecx\n" // message to write
"movl $1, %%ebx\n" // file descriptor (stdout)
"movl $4, %%eax\n" // system call number (sys_write)
"int $0x80\n" // sycall
: // no output regs
: "r"(s) // input text
: "edx","ecx","ebx","eax"
);
}
int main() {
startTrace();
// some computation
int x = 0;
int i;
for (i = 0; i < 100000; i++) {
x += i*2;
}
printRock();
write(STDOUT_FILENO,"Paper\n",6);
write(STDOUT_FILENO,"Scissors\n",9);
}
在运行时,这给:
When running, this gives:
trap
trap
trap
Rock
Paper
trap
Trace/breakpoint trap (core dumped)
所以,现在我们得到约每秒250K的指令,仍然缓慢,但不平凡的执行是可能的。但是,出现的两个写调用之间发生的核心转储。在GDB中,我们看到它发生:
So now we get about 250K instructions per second, still slow but non-trivial executions are possible. But there is that core dump that appears to happen between the two write calls. In GDB, we see where it happens:
Dump of assembler code for function __kernel_vsyscall:
0xb76f3414 <+0>: push %ecx
0xb76f3415 <+1>: push %edx
0xb76f3416 <+2>: push %ebp
0xb76f3417 <+3>: mov %esp,%ebp
0xb76f3419 <+5>: sysenter
0xb76f341b <+7>: nop
0xb76f341c <+8>: nop
0xb76f341d <+9>: nop
0xb76f341e <+10>: nop
0xb76f341f <+11>: nop
0xb76f3420 <+12>: nop
0xb76f3421 <+13>: nop
0xb76f3422 <+14>: int $0x80
=> 0xb76f3424 <+16>: pop %ebp
0xb76f3425 <+17>: pop %edx
0xb76f3426 <+18>: pop %ecx
0xb76f3427 <+19>: ret
和回溯:
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
#0 0xb77c5424 in __kernel_vsyscall ()
#1 0xb76d0553 in __write_nocancel () at ../sysdeps/unix/syscall-template.S:81
#2 0x0804847d in trapHandler (signum=5) at count.c:8
#3 <signal handler called>
#4 0xb77c5424 in __kernel_vsyscall ()
#5 0xb76d0553 in __write_nocancel () at ../sysdeps/unix/syscall-template.S:81
#6 0x08048537 in main () at count.c:49
这似乎是通过 INT 80
所发生的罚款系统调用,但写入呼叫使用内核的VIDSO / vsyscall打破以某种方式(我不知道这个功能,更贴近这里描述 )。这可能与使用 SYSENTER
,而不是 80 INT
,也许步入内核的时候陷阱标志生存。我不太得到什么用递归去 __ kernel_vsyscall
来电。我也不明白为什么还有的 __ kernel_vsyscall
函数中的 80 INT
电话。
It appears syscalls that happen via int 80
are fine, but the write calls use the kernel's VIDSO/vsyscall break somehow (I didn't know about this functionality, closer described here). It may be related to using sysenter
rather than int 80
, maybe the trap flag survives when stepping into the kernel. I don't quite get what's going with the recursive __kernel_vsyscall
calls. I also don't get why there's an int 80
call inside the __kernel_vsyscall
function.
没有任何人有一个建议,这是怎么回事,如何解决这一问题?也许这是可以禁用VDSO / vsysicall?或者是有可能与一个使用覆盖 __ kernel_vsyscall
函数 INT 80
,而不是 SYSENTER
?
Does anybody have a suggestion what's going on, and how to fix this? Maybe it's possible to disable the VDSO/vsysicall? Or is it possible to override the __kernel_vsyscall
function with one that uses int 80
rather than sysenter
?
推荐答案
回答自己的问题。
我没弄明白发生了什么事,或详细解释,但我找到了一个解决办法:禁用VDSO。这可以通过
Answering own question. I didn't figure out what was happening or explain it in detail, but I found a workaround: disable VDSO. That can be done via
sudo sysctl vm.vdso_enabled=0
通过这一点,通过程序这整个单步工作,包括跨系统调用步进。免责声明:如果事情坏就不要怪我。
With this, this whole single stepping through a program works, including stepping across system calls. Disclaimer: don't blame me if things go bad.
这篇关于通过与程序陷阱标志和陷阱信号处理程序,在崩溃试图vsyscall单步执行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!