以编程方式检测信号发生位置的便携方式

时间:2015-02-28 09:58:58

标签: c signals

假设以下代码(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

是否有便携式方式获取0x00007ffff7afd4100x00000000004005e0

2 个答案:

答案 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*来实现。