Al *_*l W 12 c optimization exception-handling gnu-assembler elf
g ++编译器具有零成本异常处理功能.据我所知,try什么都不做,但是当抛出异常时,会执行异常处理程序的子例程.像这样:
void foo() {
try {
bar(); // throws.
} catch (Type exc) {
baz();
}
}
Run Code Online (Sandbox Code Playgroud)
伪代码(c-stylish)看起来像这样:
void foo() {
bar();
return;
catch1_Type:
baz();
}
Run Code Online (Sandbox Code Playgroud)
bar()抛出.例程例程执行以下操作:
啊,返回地址是函数foo()!并且返回地址在第一个try-catch块中,我们抛出类型Type,因此异常处理程序例程位于地址foo + catch1_Type.所以清理堆栈让我们最终到达那里!
现在我的问题是:有没有办法在C中实现它?(可以是C99或更新,虽然我对gcc支持的C语言感兴趣).我知道我可以使用例如libunwind进行堆栈检查和遍历,虽然我不知道如何获取catch1_Type标签的地址.这可能是不可能的.
异常处理程序可能是一个不同的函数,同样可以,但是如何foo在另一个函数中获取stackframe的局部变量的地址?这似乎也是不可能的.
所以...有什么办法吗?我不想用这个进入汇编程序,但如果其他一切都失败也是可以接受的(尽管局部变量 - 伙计,如果使用不同的优化级别,你永远不会知道它们在哪里).
并且要明确 - 这个问题的目的是避免使用 setjmp/longjmp方法.
编辑:我发现了一个非常酷的想法,但完全不起作用:
gcc中的嵌套函数.他们能做什么?
下行阻止我做任何零成本的事情:
我花了一些时间来思考这个想法,并且我非常接近找到我自己问题的解决方案。详细信息如下:
我用于检查的代码有明显的缺陷:它不是“零成本”,因为必须在函数执行期间设置指向异常处理例程的全局指针。如果我们能让这个编译时就好了!
好吧,毕竟如果有人想正确使用内部函数,比如将指向它的指针传递给被调用者,这样他们就可以在抛出异常的情况下调用它,我们可能会有非常快的异常处理,比 setjmp/ 快得多龙跃...
我会继续黑客攻击,也许我会找到一种方法(一些汇编代码块来强制 GAS 将函数注册为父级的个性例程?)。
#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;
}
Run Code Online (Sandbox Code Playgroud)