我是这类编程的新手,很抱歉,如果我的问题很简单。 我想要做的是在我的程序中导致分段错误而不是退出程序,我想处理信号并在分段错误后继续执行。我编写了一个似乎正常工作的代码,我只是想确保这是实现此目的的方法。所以这是我的代码。
void myhandle(int mysignal, siginfo_t *si, void* arg)
{
printf("Signal is %d\n",mysignal);
ucontext_t *context = (ucontext_t *)arg;
context->uc_mcontext.gregs[REG_RIP]++;
}
int main(int argc, char *argv[])
{
struct sigaction action;
action.sa_handler=myhandle;
sigaction(11,&action,NULL);
printf("Before segfault\n");
int *a=NULL;
int b=*a;
printf("I am still alive\n");
return 0;
}
有人可以向我解释为什么myhandle里面的printf运行两次? 这段代码也可以吗?
谢谢。
答案 0 :(得分:2)
您可能需要检查dissamembly以找到要跳转的PC(即RIP)。对于你的情况,它应该看起来像,
int *a=NULL;
400697: 48 c7 45 f8 00 00 00 movq $0x0,-0x8(%rbp)
40069e: 00
int b=*a;
40069f: 48 8b 45 f8 mov -0x8(%rbp),%rax
4006a3: 8b 00 mov (%rax),%eax
4006a5: 89 45 f4 mov %eax,-0xc(%rbp)
printf("I am still alive\n");
4006a8: bf 7c 07 40 00 mov $0x40077c,%edi
4006ad: e8 de fd ff ff callq 400490 <puts@plt>
,异常在0x4006a3上,应该设置为0x4006a8以跳转到printf()。或者+2到0x4006a5,这也是有效的。
邮件转储两次的原因是,在第一次调用时,
context->uc_mcontext.gregs[REG_RIP]++
,它将RIP设置为0x4006a4,一个无效位置,再触发一个例外。
答案 1 :(得分:2)
通过this example我已经以下面的方式修改了您的代码,现在它可以按照您的意愿运行。
#include<stdio.h>
#define __USE_GNU
#include<signal.h>
#include<ucontext.h>
void myhandle(int mysignal, siginfo_t *si, void* arg)
{
printf("Signal is %d\n",mysignal);
ucontext_t *context = (ucontext_t *)arg;
context->uc_mcontext.gregs[REG_RIP] = context->uc_mcontext.gregs[REG_RIP] + 0x04 ;
}
int main(int argc, char *argv[])
{
struct sigaction action;
action.sa_sigaction = &myhandle;
action.sa_flags = SA_SIGINFO;
sigaction(11,&action,NULL);
printf("Before segfault\n");
int *a=NULL;
int b;
b =*a;
printf("I am still alive\n");
return 0;
}
<强>输出:强>
jeegar@jeegar:~/stackoverflow$ gcc test1.c
jeegar@jeegar:~/stackoverflow$ ./a.out
Before segfault
Signal is 11
I am still alive
在进一步的问题表格OP中。 运行时为此signel删除此处理程序
void myhandle(int mysignal, siginfo_t *si, void* arg)
{
printf("Signal is %d\n",mysignal);
if(flag == 0) {
// Disable the handler
action.sa_sigaction = SIG_DFL;
sigaction(11,&action,NULL);
}
if(flag) {
ucontext_t *context = (ucontext_t *)arg;
context->uc_mcontext.gregs[REG_RIP] = context- >uc_mcontext.gregs[REG_RIP] + 0x04 ;
}
}
答案 2 :(得分:1)
有人可以向我解释为什么myhandle里面的printf会运行两次吗?
该行为似乎取决于操作系统。来自myhandle
的控件可能根本不会返回main
。
捕获信号11是很常见的,通常由操作系统处理以终止程序。
但是,可以为它编写一个信号处理程序,让该函数在exit
之前打印出来。
struct sigaction action;
struct sigaction old_action;
void myhandle( int mysignal )
{
if( 11 == mysignal )
{
printf( "Signal is %d\n", mysignal ); // <-- this should print OK.
sigaction( 11, &old_action, NULL ); // restore OS signal handler, or just exit().
return;
}
}
int main(int argc, char *argv[])
{
action.sa_handler = myhandle;
sigaction( 11, &action, &old_action );
printf("Before segfault\n");
int *a=NULL;
int b=*a;
printf( "I am still alive\n" ); // <-- this won't happen
return 0;
}
答案 3 :(得分:0)
这些信号对于继续执行来说并不是一件容易的事。原因是导致信号的指令尚未执行,因此尝试执行失败的指令将继续执行。
信号处理程序执行两次(或者甚至无限重复)的原因是返回会导致CPU重试执行导致分段错误的相同事情,并且如果没有任何改变,它将再次导致分段错误。 / p>
为了处理这样的信号(SIGSEGV
,SIGFPE
,SIGILL
等),您必须实际改变信号上下文以解决问题。为此,您需要使用专为正在使用的CPU编写的代码,并使用编译器特定的行为,因为您需要修改上下文。