g++ compiler has the feature of zero-cost exception handling. To my understanding, try
does nothing, but when exception is thrown, a subroutine for the exception handler is executed. Like this:
void foo() {
try {
bar(); // throws.
} catch (Type exc) {
baz();
}
}
In pseudocode (c-stylish) would look like this:
void foo() {
bar();
return;
catch1_Type:
baz();
}
bar() throws. The exception routine does the following:
Ah, the return address is in function foo()! And the return address is in the first try-catch block, and we throw type Type, so the exception handler routine is at address foo+catch1_Type. So cleanup the stack so we end up there!
Now my question: is there any way to implement it in C? (Can be C99 or newer, although I'm interested in C dialect supported by gcc). I know that I can use e.g libunwind for stack examination and traversal, although I have no idea how to get the address of catch1_Type
label. It may not be possible.
The exception handler may be a different function, that's equally OK, but then how to get addresses of local variables of stackframe foo
in that other function? It also seem to be impossible.
So... is there any way to do it? I would like not to go into assembler with this, but it also is acceptable if everything else fails (although local variables - man, you never know where they are if using different optimization levels).
And to be clear - the purpose of this question is to avoid setjmp/longjmp approach.
EDIT: I've found a quite cool idea, but does not work entirely:
Nested functions in gcc. What they can do?
Downside which prevents me from doing anything zero-cost:
I've found some time to play with this idea, and I'm very close to finding the solution for my own question. Here are the details:
The code that I used for checking has obvious flaw: it's not 'zero-cost', as the global pointer to the exception handling routine must be set during function's execution. If only we could make this compile-time!
Well, after all if one wants to use the internal function properly, like passing a pointer to it into callees, so they can call it in case of exceptions thrown, we could probably have very fast exception handling, much, MUCH faster than setjmp/longjmp...
I'll keep on hacking, maybe I'll find a way (some assembler chunk of code to force GAS registering the function as a personality routine of the parent?).
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
typedef void (*catch_routine)(void*);
catch_routine g_r = NULL;
void tostr_internal(char* str, int a)
{
int result = a + 'a';
if (result < 'a' || result > 'z')
{
// handle exception
if(g_r)
{
g_r(&a);
}
else
{
fprintf(stderr, "Exception not caught!");
abort();
}
}
else
{
str[0] = result;
str[1] = '\0';
}
}
char* tostring(int a)
{
__label__ exhandler;
char* string = (char*)malloc(2*sizeof(char));
void personality(void* exid) {
fprintf(stderr, "Number %d is not a character!\n", *(int*)(exid));
free(string);
goto exhandler;
}
g_r = personality;
tostr_internal(string, a);
return string;
exhandler:
return NULL;
}
int main(int a, char** b)
{
int i = 0;
for(i = 0; i < 10000; i++)
{
int trythisbastard = i % 95;
char* result = tostring(trythisbastard);
if (result)
{
fprintf(stderr, "Number %d is %s\n", trythisbastard, result);
free(result);
}
}
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With