假设以下代码(main.c):
#include <unistd.h>
#include <signal.h>
void handler(int sig)
{
pause(); /* line 7 */
}
int main(void)
{
signal(SIGALRM, handler);
alarm(1);
pause();
}
当我在gbd中运行它并在handler()
内设置一个断点时,运行代码并等待一秒我可以执行以下操作:
(gdb) b 7
Breakpoint 1 at 0x4005b7: file main.c, line 7.
(gdb) r
Starting program: a.out
Breakpoint 1, handler (sig=14) at main.c:7
7 pause();
(gdb) bt
#0 handler (sig=14) at main.c:7
#1 <signal handler called>
#2 0x00007ffff7afd410 in __pause_nocancel () at ../sysdeps/unix/syscall-template.S:82
#3 0x00000000004005e0 in main () at main.c:14
是否有便携式方式获取0x00007ffff7afd410
或0x00000000004005e0
?
答案 0 :(得分:2)
使用sigaction
代替signal
,处理程序将使用信号发生位置的ucontext
进行调用:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <ucontext.h>
static void handler(int sig, siginfo_t *siginfo, void *context)
{
ucontext_t *ucontext = context;
printf("rip %p\n", (void *)ucontext->uc_mcontext.gregs[REG_RIP]);
pause();
}
int main(void)
{
struct sigaction sact;
memset(&sact, 0, sizeof sact);
sact.sa_sigaction = handler;
sact.sa_flags = SA_SIGINFO;
if (sigaction(SIGALRM, &sact, NULL) < 0) {
perror("sigaction");
return 1;
}
alarm(1);
pause();
return 0;
}
rip output和gdb bt输出:
(gdb) b 13
Breakpoint 1 at 0x4006de: file main.c, line 13.
(gdb) r
Starting program: /home/osboxes/a.out
rip 0x7ffff7ae28a0
Breakpoint 1, handler (sig=14, siginfo=0x7fffffffdf70, context=0x7fffffffde40)
at main.c:13
13 pause();
(gdb) bt
#0 handler (sig=14, siginfo=0x7fffffffdf70, context=0x7fffffffde40)
at main.c:13
#1 <signal handler called>
#2 0x00007ffff7ae28a0 in __pause_nocancel () from /lib64/libc.so.6
#3 0x0000000000400758 in main () at main.c:28
答案 1 :(得分:1)
我猜不是非常便携,但glibc和其他一些libc中有backtrace(3)
:
backtrace()返回数组中调用程序的回溯 缓冲区指出。回溯是当前活跃的系列 函数调用该程序。
您必须检查堆栈中需要查看的条目数。它至少应该与Linux保持一致。
如果要将回溯转换为类似于gdb显示的内容,可以使用binutils中的addr2line(1)
。有点像
popen("addr2line -Cfip -e ./myprog", "w")
你甚至可以在运行时通过将地址(作为字符串)写入你回来的FILE*
来实现。