[英]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.