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

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

本文介绍了获取生成UNIX信号的故障地址的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我知道 siginfo_t __ builtin_return_address 似乎都不起作用:

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

输出类似:

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

所以 siginfo_t 为NULL,并且 __ builtin_return_address 产生的值在介于命名标签之间。

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?

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

Tested on Linux 2.6.9-89.0.9.Elsmp and SunOS.

推荐答案

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

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

顺便说一句,GCC有这样一个讨厌的习惯:移动标签的地址但不能用于控制转移操作(据其所知)。比较:

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

查看优化版本中所有标签的位置如何?这将阻止通过调整PC从故障中恢复的任何尝试。 IIRC有一种使GCC不能这样做的方法,但是我不知道它是什么,也无法在手册中找到它。

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.

这篇关于获取生成UNIX信号的故障地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 02:41