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

发布于 2024-10-19 09:03:23 字数 1659 浏览 2 评论 0原文

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

我知道 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 is NULL and __builtin_return_address > 正在命名标签之间的某个位置产生值。

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

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

I am interested in a signal handler which can identify the address of the instruction which caused the problem.

I know about siginfo_t and __builtin_return_address and neither seem to work:

#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";
}

This outputs something like:

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

So siginfo_t is NULL and __builtin_return_address is yielding values somewhere in between the named labels.

I was expecting both of these to return the value of &&before. Am I using these functions correctly?

Tested on Linux 2.6.9-89.0.9.Elsmp and SunOS.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

维持三分热 2024-10-26 09:03:23

使用 SA_SIGINFO 安装的处理程序的第三个参数(声明为 void *)是指向 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";
}

: ,GCC 有这种令人讨厌的习惯,即移动其地址已被占用但未在控制传输操作中使用的标签(据其所知)。比较:

$ 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

看看优化版本中所有标签如何位于同一地址?这将导致任何尝试(例如通过调整电脑从故障中恢复)的尝试失败。 IIRC 有一种方法可以让 GCC 不这样做,但我不知道它是什么,也无法在手册中找到它。

The third argument to a handler installed with SA_SIGINFO (the one declared as void *) is a pointer to a ucontext_t structure. The contents of this structure are architecture- and OS-specific and not part of any standard, but they include the information you need. Here's a version of your program adapted to use it (Linux/x86-64 specific; you will need #ifdefs for every architecture and OS of interest):

#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";
}

By the way, GCC has this nasty habit of moving labels whose address is taken but not used in a control transfer operation (as far as it can tell). Compare:

$ 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

See how all the labels are at the same address in the optimized version? That's going to trip up any attempt to, say, recover from the fault by adjusting the PC. IIRC there is a way to make GCC not do that, but I don't know what it is and wasn't able to find it in the manual.

金橙橙 2024-10-26 09:03:23

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 在我的例子中毫无意义。

The siginfo_t isn't going to work because it contains the memory address which was accessed, not the address of the instruction that did it.

Now, the __builtin_return_address is interesting. On my machine it returns some nonsense:

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

I have no idea why. But then I examined the core dump:

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

As you can see, just like in your case, the offending address is somewhere in between label locations. This is easily explained, though. Just look at the disassembly of 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

The labelled statement consists of several instructions. The first one loads the address into the RAX register. It completes successfully because there is nothing wrong with it. It's the second one that accesses the address and breaks. This explains why the address in your trace is a bit different from the address of the label, although the code will probably be different from my example. This all doesn't explain why the __builtin_return_address gives nonsense in my case, though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文