I try to implement functional ISA simulator: targets are RISC-V and MIPS.
It is step by step instruction interpreter.
abstract step:
while(num_steps)
{
try
{
take_interrupt();// take pending interrupts
fetch(); // fetch instruction from memory
decode(); // find handler to instruction
execute(); // perform instruction
}
catch (Trap& e)
{
take_trap(e); //configure appropriate system registers and jump to trap vector.
}
}
As you can see C++ exceptions are used to transfer the control flow.
Maybe there can be more handsome design?
Question: What is best way/practise to implement traps at functional ISA simulators. Also i interested in exceptions/trap implementation at translation simulators, like QEMU.
Note: by the word trap i mean ISA defined traps, not application error: misaligned memory access, illegal instruction, system register access fault, privilege level change, etc.
QEMU uses the C setjmp()/longjmp() mechanism for dealing with most exceptions: when we detect something like a page fault we set some flags to indicate the type of exception, and then longjmp() out to the top-level "execute code" loop. That loop then looks at the flags and sets the CPU state up for "enter exception handler" before continuing to execute guest code.
So we use the C equivalent of throwing an exception; as NonNumeric says there is no requirement to implement guest exceptions like this (the coincidence of names is just coincidence). But since a memory access triggering a page fault is the non-common case, it's more efficient to longjmp or throw a C++ exception rather than include "handle failure return" in all the memory access codepaths. Guest memory access is a particular hotspot and QEMU implements its memory access fastpath with a bit of custom inline assembly, so we care about the extra handful of instructions that would be required to exit to the top level loop on a page fault without doing the longjmp. A simulator which uses a simple "fetch/decode/execute" loop without doing JIT of guest code doesn't care so much about performance, so your choice may come down to preferences for code style and maintainability.
QEMU is written in C so it doesn't use C++ exceptions. You don't have to handle ISA traps via C++ exceptions either. Exceptions should be used when useful to you as implementer, nothing more.
Also note that the traps are not something way too special, they are still part of the emulated system's workflow. It is perfectly legal to encode division like:
if (reg[divisor] != 0)
reg[target] = reg[divident] / reg[divisor];
else
trap(TRAP_DIV0)
Where the trap() function directly updates the archutecture state so that the next instruction to emulate would be from the exception handler.
void trap(int trap_id)
{
// save relevant registers according to platform spec
...
// set instruction pointer to trap handler start
reg[IP_INDEX] = trap_table[trap_id].ip;
// update other registers according to spec
...
}
C++ exceptions can make your life easier. For example memory accesses on many platforms need to convert virtual to physical addresses. This conversion may result in a trap (due to insufficient access or wrong configuration). It may be easier to write:
void some_isa_instruction_handler()
{
int value1 = read_memory(address1);
int value2 = read_memory(address2);
int res = perform_something(value1, value2);
write_memory(address3, res);
}
where read_memory() and write_memory() would simply throw C++ exception when ISA trap is needed than manually checking if each operation has generated a trap. Then the take_trap() function would rollback whatever changes were performed by the interrupted instruction handler (if needed) and set up execution to go emulate the trap handler as trap() above did.
Emulating a CISC system may benefit more from such style.
Related
i am currently writing a small VM in C/C++. Obviously i can't let the whole VM crash if the user dereferences a null pointer so i have to check every access which is becoming cumbersome as the VM grows and more systems are implemented.
So i had an idea: write a signal handler for sigsegv and let the OS do its thing but instead of closing the program call the VM exception handler.
It seems to work (with my very simple test cases), but i didn't find anything guaranteeing a Sigsegv being thrown on null-derefs nor the handler being called for OS generated signals.
So my question is:
Can i count on signal.h on modern destkop OSes (i don't really care if it's not standard on doesn't work on something other than linux/win: it's a pet project). Are there any non trivial stuff i should be aware of (obscure limitations of signal(...) or longjmp(...) ?)
Thank you !
Here is the pseudo implementation:
/* ... */
jmp_buf env;
/* ... */
void handler(int) {
longjmp(env, VM_NULLPTR);
}
/* ... */
if(setjmp(env)) {
return vm_throw("NullPtrException");
}
switch(opcode) {
/* instructions */
case INVOKE:
*stack_top = vm_call(stack_top->obj); // don't check anything in the case where stack_top or stack_top->obj is null handler() will be called an a "NullPtrException" will be thrown
break;
/* more instructions */
}
/* ... */
Note : i only need to check nulls, garbage (dangling) pointers are handled by the GC and should not happen.
Calling longjmp() from a signal handler is only safe if the signal handler is never invoked from async signal unsafe code. So for example if you might receive SIGSEGV by passing a bad pointer to any of the printf() family of functions, you cannot longjmp() from your signal handler.
Can i count on signal.h on modern destkop OSes
You can count on it in the sense that the header, and the functions within will be available on all standard compliant systems. However, exactly what what signals are thrown and when is not consistent across OSes.
On windows, you may need to compile your program with cygwin or similar environment to get the system to raise a segmentation fault. Programs compiled with visual studio use "structured exceptions" to handle invalid storage access.
Is signal.h a reliable way to catch null pointers?
There are situations where null pointer dereference does not result in the raising a segmentation fault signal, even on a POSIX system.
One case might be that the compiler has optimized the operation away, which is typical for example in the case of dereferencing a null pointer to call a member function that does not access any data members. When there is no invalid memory access, there is no signal either. Of course, in that case there won't be a crash either.
Another case might be that address 0 is in fact valid. That's the case on AIX, which you don't care about. It is also the case on Linux, which you do care about, but not by default and you might choose to not care about the case where it is. See this answer for more details.
Then there is your implementation of the signal handler. longjmp is not async signal safe, so if the signal was raised while another non-safe operation was being performed, the interrupted operation may have left your program in an inconsistent state. See the answer by John Zwinck and the libc documentation for details.
Is there any way to convert a NULL pointer access into a C++ exception under Linux ? Something similar to the NullPointerException in Java. I hope the following program would return successfully, instead of crash (assume the compiler cannot figure out this NULL pointer access during compile time):
class NullPointerException {};
void accessNullPointer(char* ptr) {
*ptr = 0;
}
int main() {
try {
accessNullPointer(0);
} catch (NullPointerException&) {
return 1;
}
return 0;
}
I'm not expecting any standard way of doing it, since NULL pointer access under C++ is undefined-behavior, just want to know how to get it done under x86_64 Linux/GCC.
I did some very primitive research in this, it might be possible:
When a NULL pointer is access under Linux, a SIGSEGV will be generated.
Inside the SIGSEGV handler, the program's memory and register information will be available (if sigaction() is used to register the signal handler). The instruction which caused the SIGSEGV is also available if the program is disassembled.
Modify the program's memory and/or register, and create/fake an exception instance (maybe by invoking the low level unwind library functions, like _Unwind_RaiseException, etc.)
Finally return from the signal handler, hope the program would start a C++ stack unwinding process like a normal exception was thrown.
Here's a quote from GCC's man page (-fnon-call-exceptions):
Generate code that allows trapping instructions to throw exceptions. Note that this requires platform-specific runtime support that does not exist everywhere. Moreover, it only allows trapping instructions to throw exceptions, i.e. memory references or floating point instructions. It does not allow exceptions to be
thrown from arbitrary signal handlers such as "SIGALRM".
It seems this "platform-specific runtime" is exactly what I want. Anyone knows such a runtime for Linux/x86_64 ? Or give me some information on how to implement such a runtime if no such runtime already exists ?
I want the solution to work in multi-threaded program as well.
No, there's no good way to do that, and there shouldn't be. Exceptions are thrown by a throw statement in source code. That's important for reasoning about exception safety: you can look at the code and see the places where exceptions can be thrown and, perhaps more important, you can look a the code and see the places where exceptions will not be thrown. If pretty much anything you do can throw an exception it becomes very difficult to write exception-safe code without cluttering it with catch clauses. Microsoft tried this in their early C++ compilers: they piggybacked C++ exception handling on top of their OS's structured exceptions, and the result was a disaster.
Register an alternative signal stack with signalaltstack().
The 3rd argument to a signal handler handler registered with the SA_SIGINFO is a pointer to a ucontext_t which contains the saved register. The signal handler should adjust this to simulate a call to a function. That function can then throw the exception.
Potential complications include the need to preserve the value of callee saved registers, the red-zone on x86-64 (which can be disabled) and the return address register on some ISAs, such as ARM.
Assuming that there is a cost associated with setting recovery point, one could optimize a loop like this:
while (doContinue) {
try {
doSomeWork ();
}
catch (...) {}
}
Into something like this:
while (doContinue) {
try {
do {
doSomeWork ();
} while (doContinue);
break;
} catch (...) {}
}
But if platform supports zero cost exception handling, this optimization does not make any sense.
Could someone point me out how zero cost exception handling is implemented on different architectures and is there a way to figure what underlying mechanisms are available to compiler/code generator to decide in compile-time whether to optimize like this or not. And could compiler, for example, optimize it for you if it can assume doSomeWork () has no side effects related to loop?
Zero-cost approach can be used only if available for the target in use. If available, it is used by the most production-quality C++ compilers. Otherwise compiler will use setjmp/longjmp approach.
Execution speed of setjmp/longjmp is slower.
However, even with setjmp/longjmp approach in use, using exceptions mechanism can result in higher performance than checking for return code of every function, as in example with double-loop optimization in the question.
The only way to find out if target supports zero-cost approach and if it is being used by compiler is to translate C++ code into assembly and analyze it. Another solution could be to invoke gnat with --RTS=zcx and check for errors, if gnat is available. But that won't guarantee that it will be used by C++ compiler.
So in general, if program size is not a concern and zero-cost exceptions are available, using exceptions to handle unexpected situations is much better then checking for return code of every function. Otherwise, exceptions can be used to optimize code in certain cases.
Use, but don't abuse!
P.S.: I ended up writing an article on this.
I think you're over-estimating what "zero cost" means here. Here's the LLVM doc on it; the main effect of it seems to be that the exception and context handling code is built at compile time, so there is no additional cost at run time while the execution proceeds normally, becoming a space-time tradeoff. In your example I believe there would be twice as many "landing pads" generated, increasing size and slowing exception handling.
So, I have a big piece of legacy software, coded in C. It's for an embedded system, so if something goes wrong, like divide by zero, null pointer dereference, etc, there's not much to do except reboot.
I was wondering if I could implement just main() as c++ and wrap it's contents in try/catch. That way, depending on the type of exception thrown, I could log some debug info just before reboot.
Hmm, since there are multiple processes I might have to wrap each one, not just main(), but I hope that you see what I mean...
Is it worthwhile to leave the existing C code (several 100 Klocs) untouched, except for wrapping it with try/catch?
Division by zero or null pointer dereferencing don't produce exceptions (using the C++ terminology). C doesn't even have a concept of exceptions. If you are on an UNIX-like system, you might want to install signal handlers (SIGFPE, SIGSEGV, etc.).
Since it's an embedded application it probably runs only on one platform.
If so its probably much simpler and less intrusive to install a proper interrupt handler / kernel trap handler for the division by zero and other hard exceptions.
In most cases this can be done with just a few lines of code. Check if the OS supports such installable exception handlers. If you're working on an OS-less system you can directly hook the CPU interrupts that are called during exceptions.
First of all, divide by zero or null pointer dereference won't throw exceptions. Fpe exceptions are implemented in the GNU C library with signals (SIGFPE). And are part of the C99 standard, not the C++ standard.
A few hints based in my experience in embedded C and C++ development. Depending on your embedded OS:
Unless you are absolutely sure of what you are doing, don't try/catch between threads . Only inside the same thread. Each thread usually has its own stack.
Throwing in the middle of legacy C code and catching, is going to lead to problems, since for example when you have jumped to the catch block out of the normal program flow, allocated memory may not be reclaimed, thus you have leaks which is far more problematic than divide by zero or null pointer dereference in an embedded system.
If you are going to use exceptions, throw them inside your C++ code with allocation of other "C" resources in constructors and deallocation in destructors.
Catch them just above the C++ layer.
I don't think machine code compiled from C does the same thing with regard to exceptions, as machine code compiled for C++ which "knows" about exceptions. That is, I don't think it's correct to expect to be able to catch "exceptions" from C code using C++ constructs. C doesn't throw exceptions, and there's nothing that guarantees that errors that happen when C code does something bad are caught by C++'s exception mechanism.
Divide by zero, null pointer dereference are not C or C++ exceptions they are hardware exceptions.
On Windows you can get hardware exceptions like these wrapped in a normal C++ exception if _set_se_translator() is used.
__try/__catch may also help. Look up __try/__catch, GetExceptionCode(), _set_se_translator() on MSDN, you may find what you need.
I think I remember researching on how to handle SIGSEGV ( usually generated when derefing invalid pointer, or dividing by 0 ) and the general advise I found on the net was: do nothing, let the program die. The explanation, I think a correct one, is that by the time you get to a SIGSEGV trap, the stack of the offending thread is trashed beyond repair.
Can someone comment if this is true?
Only work on msvc
__try
{
int p = 0;
p=p / 0;
}
__except (1)
{
printf("Division by 0 \n");
}
Is it possible to catch a stack overflow exception in a recursive C++ function? If so, how?
so what will happen in this case
void doWork()
{
try() {
doWork();
}
catch( ... ) {
doWork();
}
}
I am not looking for an answer to specific OS. Just in general
It's not an exception per se, but if you just want to be able to limit your stack usage to a fixed amount, you could do something like this:
#include <stdio.h>
// These will be set at the top of main()
static char * _topOfStack;
static int _maxAllowedStackUsage;
int GetCurrentStackSize()
{
char localVar;
int curStackSize = (&localVar)-_topOfStack;
if (curStackSize < 0) curStackSize = -curStackSize; // in case the stack is growing down
return curStackSize;
}
void MyRecursiveFunction()
{
int curStackSize = GetCurrentStackSize();
printf("MyRecursiveFunction: curStackSize=%i\n", curStackSize);
if (curStackSize < _maxAllowedStackUsage) MyRecursiveFunction();
else
{
printf(" Can't recurse any more, the stack is too big!\n");
}
}
int main(int, char **)
{
char topOfStack;
_topOfStack = &topOfStack;
_maxAllowedStackUsage = 4096; // or whatever amount you feel comfortable allowing
MyRecursiveFunction();
return 0;
}
There's really no portable way to do it. An out of control recursive function will usually cause an invalid memory access when it tries to allocate a stack frame beyond the stack address space. This will usually just crash your program with a Segmentation Fault/Access Violation depending on the OS. In other words, it won't throw a c++ exception that can be handled in a standard way by the language.
Even if you can do this non-portably, as you can in Windows, it's still a very bad idea. The best strategy is to not overflow the stack in the first place. If you need isolation from some code you don't control, run that code in a different process and you can detect when it crashes. But you don't want to do that sort of thing in your own process, because you don't know what sort of nasty corruption of state the offending code is going to do, and that will make you unstable.
There's an interesting, somewhat related blog post by Microsoft's Raymond Chen about why you shouldn't try to check for valid pointers in a user mode application on Windows.
There isn't a portable way. However, there are a few nonportable solutions.
First, as others have mentioned, Windows provides a nonstandard __try and __except framework called Structured Exeption Handling (your specific answer is in the Knowledge Base).
Second, alloca -- if implemented correctly -- can tell you if the stack is about to overflow:
bool probe_stack(size_t needed_stack_frame_size)
{
return NULL != alloca(needed_stack_frame_size);
};
I like this approach, because at the end of probe_stack, the memory alloca allocated is released and available for your use. Unfortunately only a few operating systems implement alloca correctly. alloca never returns NULL on most operating systems, letting you discover that the stack has overflown with a spectacular crash.
Third, UNIX-like systems often have a header called ucontext.h with functions to set the size of the stack (or, actually, to chain several stacks together). You can keep track of where you are on the stack, and determine if you're about to overflow. Windows comes with similar abilities a la CreateFiber.
As of Windows 8, Windows has a function specifically for this (GetCurrentThreadStackLimits)
On what OS? Just for example, you can do it on Windows using Structured Exception Handling (or Vectored Exception Handling). Normally you can't do it with native C++ exception handling though, if that's what you're after.
Edit: Microsoft C++ can turn a structured exception into a C++ exception. That was enabled by default in VC++ 6. It doesn't happen by default with newer compilers, but I'm pretty sure with a bit of spelunking, you could turn it back on.
It's true that when this happens, you're out of stack space. That's part of why I mentioned vectored exception handling. Each thread gets its own stack, and a vectored exception handler can run in a separate thread from where the exception was thrown. Even SEH, however, you can handle a stack overflow exception -- it just has to manually spawn a thread to do most of the work.
I doubt so, when stack got overflow the program will not be able even to handle exception. Normally OS will close such program and report the error.
This happens mostly because of infinite recursions.
In Windows you can use structured exception handling (SEH), with __try and __except keywords to install your own exception handler routine that can catch stack overflows, access violation, etc etc.
It's pretty neat to avoid Windows' default crash dialog, and replace it with your own, if you need to.
This is done all the time by most modern operating systems. If you want to do it on your own, you'll have to know the maximum "safe" address for your stack (or likewise do some math to determine how many times you can safely call the function), but this can get very tricky if you aren't managing the call stack yourself, since the OS will usually (for good reason) be hiding this from you.
If you are programming in kernel space, this gets significantly easier, but still something I question why you're doing. If you have a stack overflow, it's probably because of a bad algorithmic decision or else an error in the code.
edit: just realized you want to "catch the exception" that results. I don't think my answer directly answers that at all (does this exception even exist? i would figure instead on a spectacular failure), but I'll leave it up for insight. If you want it removed, please let me know in the comments and I will do so.
You have to know always a level of your recursion and check it if greater than some threshold. Max level (threshold) is calclulated by ratio of stack size divided by the memory required one recursive call.
The memory required one recursive call is the memory for all arguments of the function plus the memory for all local variables plus the memory for return address + some bytes (about 4-8).
Of course, you could avoid the recursion problem by converting it to a loop.
Not sure if you're aware of this but any recursive solution can be translated to a loop-based solution, and vice-versa. It is usually desirable to use a loop based solution because it is easier to read and understand.
Regardless of use of recursion or loop, you need to make sure the exit-condition is well defined and will always be hit.
If you use Visual C++
Goto C/C++ , Code Generation
Choose "Both..." in "Basic Runtime Checks"
Then, run your application...