Handling CPU exceptions in C++

2019-04-26 06:52发布

问题:

is there a cross-platform way to handle the CPU exceptions like segmentation faults, or division by zero? Lets say, I need to call some potentially unsafe functions (for example from a plug-in file), that can cause a segfault, or some other problems that I cannot test before I execute it. I know, that the C standard library has signal handling functions, but I don't know how to use them to handle the problem to avoid the program termination (I guess, I can't just jump to the location before the problematic functions execution, or can I?). Under windows I could use the SEH exception handlers, but I can't do that under Linux, or any other OS. What about using my own exception handler to handle these problems, how much is that different between Windows/Linux? Would that be even possible (via assembler - lets say just on the x86 platform)?

I'm asking mostly out of curiosity, I'm not trying to solve an existing problem (yet). Thanks

回答1:

libsigsegv is a cross-platform library for handling segmentation faults and stack overflows. However, in the vast majority of cases, when you detect a segmentation fault, the right thing to do is to terminate execution as fast as possible instead of trying to recover from it. A segfault is usually indicative of a bug or corrupted memory, and once you have corrupted memory, it's virtually impossible to recover from that.



回答2:

The problem is that if the plugin seg faults, you can't guarantee what state your main program will be in anymore. Even if you could catch SIGSEGV (which I believe you can) you wouldn't have a good way to recover in your application.

What you would have to do is run the plugin in a forked process so that if it crashes your main program isn't taken down too. You could communicate between the processes with a pipe for example.



回答3:

This is not covered in Standard C++, however common desktop OSes provide facilities to do this. Windows has Structured Exception Handling (SEH) for which relevant compiler extensions are available, and POSIX provides signal handling.

Typically, I would say that you shouldn't catch CPU exceptions- they only occur if your program is bugged and at that point, it's time to crack out a debugger, not continue.

You cannot use the same approach- even in assembler. These facilities are provided by the OS- when the CPU raises an exception, it goes to the OS to decide what to do about it, not user-mode. Not just that, but I would say that SEH and signal handling are easily different enough to warrant fundamentally different approaches when using them in code, so a simple #ifdef won't cut it.

setjmp and longjmp can only work for "signals" raised by user-mode code, not OS-level.



回答4:

I don't think a true cross platform solution exists.

under windows you can use _set_se_translator to translate SEH exceptions to C++ exceptions.

See the following article C++ exception-handling tricks for Linux on how to do the same under Linux



回答5:

No, there's no standard way. In C++, such "CPU exceptions" are manifestations of Undefined Behavior, i.e. the C++ standard doesn't specify anything about their behavior, or of anything that happens afterwards. Even the notion of a "segfault" doesn't exist in standard C++. Dereferencing a NULL pointer could set your computer on fire, and obviously there's little left to catch at that point.

C didn't solve this either: SIGSEGV isn't a standard C signal; it's a POSIX extension. Windows doesn't have SIGSEGV, for instance.



回答6:

Signal handlers can fix up program execution to some point; what exactly is allowed is documented in the signal(7) manual page.

There are implementations that will

  • return to the faulting instruction from SIGSEGV handlers (this allows you to change the memory map and return), and

  • go to the instruction following the fault for SIGFPE (so your signal handler needs to look up the instruction and provide a result)

Note that this is implementation-defined at best. The manual tells you not to rely on any of this. You have been warned. :)