繁体   English   中英

获取生成UNIX信号的故障地址

[英]Getting fault address that generated a UNIX signal

我对一个信号处理程序感兴趣,它可以识别导致问题的指令的地址。

我知道siginfo_t__builtin_return_address似乎都不起作用:

#include <iostream>
#include <signal.h>

void handler (int, siginfo_t *, void *);

int main ()
{
begin:
    std :: cerr << &&begin << " ~ " << &&before << " ~ " << &&after << "\n";

    struct sigaction s;
    s .sa_flags = SA_SIGINFO;
    sigemptyset (& s .sa_mask);
    s .sa_sigaction = handler;
    sigaction (SIGSEGV, &s, NULL);

    int * i = NULL;
before:
    *i = 0;
after:
    std :: cout << "End.\n";
}

void handler (int, siginfo_t *si, void *)
{
    std :: cerr << "si:" << si -> si_addr << "\n";
    std :: cerr << "At: " << __builtin_return_address (0) << "\n";
    std :: cerr << "At: " << __builtin_return_address (1) << "\n";
    std :: cerr << "At: " << __builtin_return_address (2) << "\n";
    std :: cerr << "At: " << __builtin_return_address (3) << "\n";
    std :: cerr << "At: " << __builtin_return_address (4) << "\n";
    std :: cerr << "At: " << __builtin_return_address (5) << "\n";
}

这输出如下:

0x10978 ~ 0x10a4c ~ 0x10a54
si:0
At: 0xfb945364
At: 0xfb939e64
At: 0x10a40
At: 0x10740
At: 0
At: Segmentation Fault

所以siginfo_t是NULL和__builtin_return_address 命名标签之间的某处产生价值。

我希望这两个都能&&before返回&&before的值。 我正确使用这些功能吗?

在Linux 2.6.9-89.0.9.Elsmp和SunOS上测试过。

使用SA_SIGINFO (声明为void *ucontext_t )安装的处理程序的第三个参数是指向ucontext_t结构的指针。 此结构的内容是体系结构和操作系统特定的,不是任何标准的一部分,但它们包含您需要的信息。 这是适合使用它的程序版本(特定于Linux / x86-64;对于每个感兴趣的架构和操作系统,您需要#ifdef ):

#define _GNU_SOURCE 1
#include <iostream>
#include <iomanip>
#include <signal.h>
#include <ucontext.h>

using std::cout;

static volatile int *causecrash;

static void handler(int, siginfo_t *si, void *ptr)
{
   ucontext_t *uc = (ucontext_t *)ptr;

   cout << "si:" << si->si_addr << '\n';
   cout << "ip:" << std::hex << uc->uc_mcontext.gregs[REG_RIP] << '\n';
}

int main()
{
begin:
    cout.setf(std::ios::unitbuf);
    cout << &&begin << " ~ " << &&before << " ~ " << &&after << '\n';

    struct sigaction s;
    s.sa_flags = SA_SIGINFO|SA_RESETHAND;
    s.sa_sigaction = handler;
    sigemptyset(&s.sa_mask);
    sigaction(SIGSEGV, &s, 0);

before:
    *causecrash = 0;
after:
    cout << "End.\n";
}

顺便说一下,海湾合作委员会有这种讨厌的习惯,就是移动地址的标签,但不用于控制转移操作(据其所知)。 相比:

$ g++ -O0 -W -Wall test.cc && ./a.out 
0x400a30 ~ 0x400acd ~ 0x400ada
si:0
ip:400ad4
Segmentation fault
$ g++ -O2 -W -Wall test.cc && ./a.out 
0x4009f0 ~ 0x4009f0 ~ 0x4009f0
si:0
ip:400ab4
Segmentation fault

了解优化版本中所有标签如何位于同一地址? 通过调整PC,这样可以避免任何尝试,例如从故障中恢复。 IIRC有一种方法可以让海湾合作委员会不这样做,但我不知道它是什么,也无法在手册中找到它。

siginfo_t不起作用,因为它包含访问的内存地址,而不是执行它的指令的地址。

现在, __builtin_return_address非常有趣。 在我的机器上它返回一些废话:

0x40089f ~ 0x400935 ~ 0x40093f
si:0
At: 0x7fe22916fc20
At: 0x7fe22915ad8e

我不知道为什么。 但后来我检查了核心转储:

(gdb) bt
#0  0x00000000004009ff in handler(int, siginfo*, void*) ()
#1  <signal handler called>
#2  0x0000000000400939 in main ()

正如您所看到的,就像您的情况一样,违规地址位于标签位置之间。 不过,这很容易解释。 只需看看main()的反汇编:

(gdb) disas
Dump of assembler code for function main:
   ...
   ; the label is here:
   0x0000000000400935 <+161>:   mov    -0x8(%rbp),%rax
=> 0x0000000000400939 <+165>:   movl   $0x0,(%rax)
   0x000000000040093f <+171>:   mov    $0x400c32,%esi

标签声明由几条指令组成。 第一个将地址加载到RAX寄存器中。 它成功完成,因为它没有任何问题。 这是第二个访问地址和中断的人。 这解释了为什么跟踪中的地址与标签的地址略有不同,尽管代码可能与我的示例不同。 这一切都没有解释为什么__builtin_return_address在我的情况下给出了废话。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM