在分割违规后恢复生机 [英] Coming back to life after Segmentation Violation

查看:11
本文介绍了在分割违规后恢复生机的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果出现 Segmentation Fault 错误,是否可以恢复 C 程序的正常执行流程?

Is it possible to restore the normal execution flow of a C program, after the Segmentation Fault error?

struct A {
    int x;
};
A* a = 0;

a->x = 123; // this is where segmentation violation occurs

// after handling the error I want to get back here:
printf("normal execution");
// the rest of my source code....

我想要一种类似于 Java、C# 等中存在的 NullPointerException 的机制.

I want a mechanism similar to NullPointerException that is present in Java, C# etc.

注意:请不要告诉我 C++ 中有异常处理机制,因为我知道,不要告诉我应该在分配之前检查每个指针等.

Note: Please, don't tell me that there is an exception handling mechanism in C++ because I know that, dont' tell me I should check every pointer before assignment etc.

我真正想要实现的是回到上面示例中的正常执行流程.我知道可以使用 POSIX 信号执行一些操作.它应该是什么样子?其他想法?

What I really want to achieve is to get back to normal execution flow as in the example above. I know some actions can be undertaken using POSIX signals. How should it look like? Other ideas?

推荐答案

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <signal.h>
#include <stdlib.h>
#include <ucontext.h>

void safe_func(void)
{
    puts("Safe now ?");
    exit(0); //can't return to main, it's where the segfault occured.
}

void
handler (int cause, siginfo_t * info, void *uap)
{
  //For test. Never ever call stdio functions in a signal handler otherwise*/
  printf ("SIGSEGV raised at address %p
", info->si_addr);
  ucontext_t *context = uap;
  /*On my particular system, compiled with gcc -O2, the offending instruction
  generated for "*f = 16;" is 6 bytes. Lets try to set the instruction
  pointer to the next instruction (general register 14 is EIP, on linux x86) */
  context->uc_mcontext.gregs[14] += 6; 
  //alternativly, try to jump to a "safe place"
  //context->uc_mcontext.gregs[14] = (unsigned int)safe_func;
}

int
main (int argc, char *argv[])
{
  struct sigaction sa;
  sa.sa_sigaction = handler;
  int *f = NULL;
  sigemptyset (&sa.sa_mask);
  sa.sa_flags = SA_SIGINFO;
  if (sigaction (SIGSEGV, &sa, 0)) {
      perror ("sigaction");
      exit(1);
  }
  //cause a segfault
  *f = 16; 
  puts("Still Alive");
  return 0;
}

$ ./a.out
SIGSEGV raised at address (nil)
Still Alive

如果我在生产代码中看到这样的东西,我会用球棒击败某人,但这是一个丑陋的,有趣的黑客.您将不知道段错误是否损坏了您的某些数据,您将无法恢复并且知道现在一切正常,没有可移植的方式来执行此操作.您可以做的唯一稍微理智的事情是尝试记录错误(直接使用 write(),而不是任何 stdio 函数 - 它们不是信号安全的),也许重新启动程序.对于这些情况,您最好编写一个监控子进程退出、记录并启动新子进程的超级监督进程.

I would beat someone with a bat if I saw something like this in production code though, it's an ugly, for-fun hack. You'll have no idea if the segfault have corrupted some of your data, you'll have no sane way of recovering and know that everything is Ok now, there's no portable way of doing this. The only mildly sane thing you could do is try to log an error (use write() directly, not any of the stdio functions - they're not signal safe) and perhaps restart the program. For those cases you're much better off writing a superwisor process that monitors a child process exit, logs it and starts a new child process.

这篇关于在分割违规后恢复生机的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆