问题描述
我对可以识别导致问题的指令地址的信号处理器感兴趣。
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 #ifdef
s 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信号的故障地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!