首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >可以用C编写零成本异常处理吗?

可以用C编写零成本异常处理吗?
EN

Stack Overflow用户
提问于 2013-03-17 18:59:55
回答 1查看 2.3K关注 0票数 13

g++编译器具有零成本异常处理的特点.据我理解,try什么也不做,但是当抛出异常时,会执行异常处理程序的一个子例程。如下所示:

代码语言:javascript
复制
void foo() {
    try {
        bar(); // throws.
    } catch (Type exc) {
        baz();
    }
}

在伪代码(c风格)中,如下所示:

代码语言:javascript
复制
void foo() {
    bar();
    return;
catch1_Type:
    baz();
}

酒吧()抛出。异常例程执行以下操作:

啊,返回地址在foo()函数中!返回地址位于第一个尝试-捕捉块中,我们抛出类型类型,因此异常处理程序例程位于address foo+catch1_Type。所以清理堆栈,这样我们就能在那里结束了!

现在我的问题是:在C中有实现它的方法吗?(可以是C99或更新版本,尽管我对gcc支持的C方言感兴趣)。我知道我可以使用libunwind来进行堆栈检查和遍历,尽管我不知道如何获得catch1_Type标签的地址。这可能是不可能的。

异常处理程序可能是一个不同的函数,这同样可以,但是如何在另一个函数中获取堆栈框架foo的局部变量的地址呢?这似乎也是不可能的。

所以..。有什么办法吗?我不想进入汇编程序,但如果其他一切都失败了也是可以接受的(尽管局部变量-伙计,如果使用不同的优化级别,您永远不知道它们在哪里)。

而且要明确的是--这个问题的目的是避免使用 setjmp/longjmp方法。

编辑:我发现了一个很酷的想法,但并不完全有效:

gcc中的嵌套函数他们能做什么?

  • 可以访问局部变量,
  • 有可能去本地标签在父函数!
  • 如果我们传递一个指向嵌套函数的指针,我们就可以通过调用函数的调用来调用它,因此它可以通过被调用函数中的指针调用。

它阻碍了我做任何零成本的事情:

  • 即使在-O0级别,如果它们未使用,也会进行优化。我能做点什么吗?如果可以的话,当抛出异常时,我可以按符号名获得地址,它只需完成实现不抛出时不需要花费任何代价的异常.
EN

回答 1

Stack Overflow用户

发布于 2013-03-31 16:57:20

我已经找到了一些时间来玩这个想法,我非常接近为我自己的问题找到解决方案。以下是详细信息:

  • gcc允许嵌套函数,它可以访问父函数的局部变量,并且可以在父函数中进行标记!
  • 如果只定义了内部函数,而不是引用它,gcc就不会发出内部函数的代码。您可以定义内联no-op函数,它获得指向本地函数的指针,并在父函数中调用它。这将强制为内部函数生成代码,并且它将是零成本(在较高的优化级别上,内联无操作调用将被删除)。但是,在最近的gcc中,优化内联调用可能会阻止为内部函数生成代码。
  • 不好的是,我看不出有什么办法强迫嵌套(内部)函数成为全局符号。它们总是本地的,所以不可能使用dlsym获得地址。
  • 另一个坏消息是,如果程序使用这样的嵌套函数,valgrind就崩溃了;)我能够验证我的简单测试程序,但我无法使用val差事来验证是否存在内存冲突。

我用来检查的代码有明显的缺陷:它不是“零成本”,因为指向异常处理例程的全局指针必须在函数执行过程中设置。要是我们能把这个编译时间做好就好了!

嗯,毕竟,如果一个人想要正确地使用内部函数,比如将指向它的指针传递给被调用者,这样他们就可以在抛出异常的情况下调用它,那么我们可能有非常快的异常处理,比setjmp/longjmp快得多.

我将继续进行黑客攻击,也许我会找到一种方法(一些汇编代码块强制GAS将函数注册为父程序的个性例程?)。

代码语言:javascript
复制
#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;
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15464891

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档