是否可以用C编写零成本异常处理?

是否可以用C编写零成本异常处理?,c,exception,optimization,elf,C,Exception,Optimization,Elf,g++编译器具有零成本异常处理的特性。据我所知,try什么都不做,但当抛出异常时,会执行异常处理程序的子例程。像这样: void foo() { try { bar(); // throws. } catch (Type exc) { baz(); } } 在伪代码(c-Style)中,如下所示: void foo() { bar(); return; catch1_Type: baz(); } 棒()抛出。异常

g++编译器具有零成本异常处理的特性。据我所知,
try
什么都不做,但当抛出异常时,会执行异常处理程序的子例程。像这样:

void foo() {
    try {
        bar(); // throws.
    } catch (Type exc) {
        baz();
    }
}
在伪代码(c-Style)中,如下所示:

void foo() {
    bar();
    return;
catch1_Type:
    baz();
}
棒()抛出。异常例程执行以下操作:

啊,返回地址在函数foo()中!返回地址在第一个try-catch块中,我们抛出类型type,因此异常处理程序例程位于地址foo+catch1\u type。所以清理堆栈,这样我们就可以结束了

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

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

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

需要明确的是,这个问题的目的是避免setjmp/longjmp方法

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

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

  • 可以访问局部变量
  • 有可能转到父函数中的本地标签
  • 可以由函数的被调用方调用,前提是我们传递一个指向嵌套函数的指针,因此它可以通过被调用方中的指针使用
阻碍我做任何零成本事情的不利因素:

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

    • 我已经找到了一些时间来研究这个想法,我很快就能找到解决我自己问题的办法了。详情如下:

      • gcc允许嵌套函数,这些函数可以访问父函数的局部变量,并且可以转到父函数中的标签
      • 如果只定义了内部函数而没有引用它,那么gcc将不会发出内部函数的代码。您可以定义内联no op函数,该函数获取指向本地函数的指针,并在父函数中调用它。这将强制为内部函数生成代码,并且成本为零(在更高的优化级别,内联no-op调用将被删除)。在最近的gcc中,优化内联调用可能会阻止生成内部函数的代码
      • 糟糕的是,我看不出有什么办法可以强制嵌套(内部)函数成为全局符号。它们总是本地的,因此不可能使用dlsym获取地址
      • 另一个坏消息是,如果程序使用这种嵌套函数,valgrind将崩溃;)我能够验证我的简单测试程序,但我无法使用valgrind来验证是否存在内存冲突
      我用于检查的代码有明显的缺陷:它不是“零成本”,因为在函数执行期间必须设置指向异常处理例程的全局指针。要是我们能在编译时完成就好了

      好吧,毕竟,如果想要正确地使用内部函数,比如将指向它的指针传递给被调用方,以便在抛出异常时调用它,我们可能会有非常快速的异常处理,比setjmp/longjmp快得多

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

      #包括
      #包括
      #包括
      typedef void(*catch_例程)(void*);
      catch_例程g_r=NULL;
      无效tostr_内部(字符*str,内部a)
      {
      int结果=a+‘a’;
      如果(结果<'a'|结果>'z')
      {
      //处理异常
      if(g_r)
      {
      g_r&a;
      }
      其他的
      {
      fprintf(stderr,“未捕获异常!”);
      中止();
      }
      }
      其他的
      {
      str[0]=结果;
      str[1]='\0';
      }
      }
      char*tostring(int a)
      {
      __标签u uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;
      char*string=(char*)malloc(2*sizeof(char));
      无效人格(void*exid){
      fprintf(stderr,“数字%d不是字符!\n”,*(int*)(exid));
      自由(弦);
      去呼气机;
      }
      g_r=个性;
      tostr_内部(字符串,a);
      返回字符串;
      呼气机:
      返回NULL;
      }
      int main(int a,字符**b)
      {
      int i=0;
      对于(i=0;i<10000;i++)
      {
      int TRYTHISBURST=i%95;
      char*result=tostring(trythisburd);
      如果(结果)
      {
      fprintf(stderr,“编号%d为%s\n”,trythis,result);
      自由(结果);
      }
      }
      返回0;
      }
      
      对这个问题的看法似乎略有不同:。。。它询问异常如何在C++中工作;一旦你知道C++如何实现异常,你就可以决定是否为C程序复制该实现。@ MartinAtkins,我只稍微知道G++中如何实现异常,尽管我想我读到了G+中的异常支持部分是在编译器和链接器的一边,因此,在不修改工具的情况下,在C中复制它将是一个问题。我将阅读你提到的主题以获得更多的魔法力。你完全错了,
      尝试
      /
      捕获
      构造是有代价的。首先,
      try
      必须以某种方式标记s的位置
      #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;
      }