views:

208

answers:

3

I have some odd self modifying code, but at the root of it is a pretty simple problem: I want to be able to execute a jmp (or a call) and then from that arbitrary point throw an exception and have it caught by the try/catch block that contained the jmp/call.

But when I do this (in gcc 4.4.1 x86_64) the exception results in a terminate() as it would if the exception was thrown from outside of a try/catch. I don't really see how this is different than throwing an exception from inside of some far-flung library, yet it obviously is because it just doesn't work.

How can I execute a jmp or call but still throw an exception back to the original try/catch? Why doesn't this try/catch continue to handle these exceptions as it would if the function was called normally?

The code:

#include <iostream>
#include <stdexcept>

using namespace std;

void thrower()
{
    cout << "Inside thrower" << endl;
    throw runtime_error("some exception");
}

int main()
{
    cout << "Top of main" << endl;  

    try {
        asm volatile (
            "jmp *%0" // same thing happens with a call instead of a jmp
            :
            : "r"((long)thrower)
            :
        );
    } catch (exception &e) {
        cout << "Caught : " << e.what() << endl;
    }
    cout << "Bottom of main" << endl << endl;
}

The expected output:

Top of main 
Inside thrower 
Caught : some exception
Bottom of main

The actual output:

Top of main
Inside thrower
terminate called after throwing an instance of 'std::runtime_error'
  what():  some exception
Aborted
+1  A: 
e.James
That question doesn't provide much more information. My feeble attempt to understand exception handling (on Mac OS X) led me into some odd `libunwind` code, for which I couldn't find the open source. It was written in C++(!) and had Apple naming conventions. Point being, it isn't strictly functionality of GCC. Look in the GCC source and you'll see that it depends on OS supplied libunwind.
Potatoswatter
Nope it looks like the asm is just a call to the function (I put inline-asm comments around the call, and there are no instructions between the comments except the call itself).
SoapBox
@SoapBox: You need to compare the exception dispatch tables to see what the functions that *are* inserted at `try{` and `throw` operate on.
Potatoswatter
+3  A: 

Have you looked at how your implementation handles exceptions? It involves looking up PC addresses in tables to figure out what the program was doing the particular spot that threw, and what all callers were doing. At least on Mac OS X GCC.

The only other system I've looked at, Metrowerks Codewarrior for Mac (way back when) used a similar system, albeit somewhat more transparent. The compiler can transparently insert functions for any kind of exception context change.

You don't have a prayer of making this portable.

Potatoswatter
Interesting... But if I embed a legitimate, working call to "thrower()" inside the same try/catch, the inline-asm version still doesn't work, which it sounds like you're saying it should. I guess the exception handler could be looking at the address of the call itself, which is not one it knows about. This is the most promising answer so far....
SoapBox
+1  A: 

Once you do the inline assembly, you have implementation defined behavior (7.4/1).

You should try and set up a stack frame; details are platform specific and I don't know how to do it on x86_64.

R Samuel Klatchko