zoukankan      html  css  js  c++  java
  • gcc的异常处理机制

    一、序言
    这个异常处理可以说是C++语法中一个重要的组成部分,但是在使用中很少有人认真的研究过它的实现,甚至在工程中使用这个工具的人都很少,加上我之前对于C++的使用也是大概在windows下使用了两年,所以一些基本的问题我也不是很熟悉。但是对于windows下的结构化异常处理方法,在Matt pietreck之前的一篇文章中有深入的说明(在google里面搜索seh,第一篇应该就是那篇文章),这也是我最早觉得非常有技术含量的文章。当时的Matt在borland工作,好像是搞调试器的,例如当时著名的SoftICE工具,bound checker之类的,所以在Microsoft Journal上的Under the hood 专栏中时常有惊艳的文章。
    之后在linux下开发,主要接触的并不是语言本身的问题,大多是Linux系统级别的一些环境级别的问题,而且使用的是C语言开发,对于C++的很多特性并不是有很深入系统的了解,例如模板、构造函数的执行时机等一些比较细节的东西,但是对于C++的整个内存布局,在之前应该是CodeProject网站上对于windows下动态类型识别的功能有比较详细的描述,所以对于C++的内存布局,动态类型识别,虚函数实现等相对比较简单的功能印象依然深刻。
    可是gcc下的异常处理机制在很多的文章中很少有描述,至少说我们使用Google搜索gcc的异常实现很少找到相关的说明文档,即使有文档我们也可以看到文档里的内容语焉不详,根本没有达到代码级的分析,很多都是数据结构的分析,这些分析对于了解大致原理来进行一些YY来说是可以满足条件的,但是对于工程中问题的追查没有太大实际意义,因为在实际应用过程中,问题并不是知道原理就可以的,而是需要精确到程序实现的每一行代码。
    二、基础知识
    在C++中,异常处理就是使用try catch及 throw三个关键字来实现,对于try,在语法分析阶段被消化掉,在汇编代码中,try的位置没有而外的代码体现,而只是在附加的exception_table中有相应的结构定界体现,表示这个结构中的代码是受保护的代码,并且如果它有对应的catch,那么catch处有相应的action table结构。
    与此相反,对于throw和catch语句,它们在最终生成的汇编代码中有自己的体现,其中throw被编译器在生成代码的时候就转换成了一个函数,函数的名称叫做__cxx_throw,有些同学如果使用到过gdb 的catch throw可能会有印象,事实上在捕捉throw的时候gdb也只是简单的在这个函数的地方打上一个断点而已。如果你的可执行文件是通过动态链接生成的,那么在程序开始还没有启动的时候执行catch throw的时候gdb会提示这个函数没有定义,所以你手动执行b __cxx_throw即可,此时gdb把它作为一个普通的延迟断点,在gcc动态库加载的时候断点即可生效。
    对于catch同样是翻译成一组函数。这里是一个比较有意思的现象,就是在编译器生成代码的时候,它直接生成一些自己也不一定定义的函数调用,然后在链接的时候让用户或者其他的模块来定义,这样就解耦了语义分析后代码生成和辅助功能实现的关系。这种方法在linux系统的其它地方下也有实现,例如gcc thread local 变量中对于外部定义变量的引用、gcc中对于重载new函数的调用,在汇编中对于__gcc_personality_v0的调用,在gcc的时候它都不知道这些函数的实现,但是依然勇敢的生成了对这些函数的调用,如果在链接的时候没有链接他们相应的定义模块,就会出现链接错误。
    三、异常处理的基本思路和基本结构
    1、基本实现思路
    异常处理包括两个主要的部分,一个是栈帧展开,一个是异常执行。这两个是一种独立的实现机制,实现的关联性并非必然。所谓的栈帧展开不仅这里需要使用,在我们日常使用的gdb中,这个栈帧展开也是一个非常基本的功能。在gdb的堆栈回溯中,经常需要在bt之后通过frame找到对应的某一层堆栈位置,进而查看该层中局部变量的定义。大家不要认为这个展开就是直接的调用链回溯之后设置变量的值,问题的难点在于对于系统来说它一组寄存器状态可以放入CPU中,而回溯到指定层的栈帧不仅只是就该栈帧(ebp和esp),还包括了其他的通用寄存器(eax,ebx,esi,edi)等这些寄存器的内容,因为在栈帧的特殊点,变量的计算需要使用寄存器来实现。
    另一个personality的实现则是语言特有的一个内容,它涉及到异常的识别和执行。这个异常的识别和展开是在gcc下就有两种实现,一种是通过setjmp+longjmp的实现方式,就是通过两个函数在每个try的开始执行setjmp操作,把所有的寄存器数值都保存起来,当异常发生之后,在全局结构中找到这个保存的跳转点位置,然后通过longjmp跳转过去,这个中实现方法在gcc中使用的personality叫做__gcc_personality_sj0,和 现在成用的__gcc_personality_v0相对应。
    而__gcc_personality_v0胜出的原因在于这种实现对于代码的正常执行流来说影响极小,它只是在额外程序section中生成特定的数据结构来表示源代码中的异常结构,在可执行代码中并不需要因为有异常而执行跳转点的保存。但是这种实现的缺点就是不太直观,很多人并不理解相关的辅助数据结构,所以成为了认知的“黑洞”。
    gcc到底是使用哪种personality是在gcc可执行文件生成的时候就已经确定的,一旦代码生成,就不能在编译阶段选择在两种实现中切换,这个特性在编译时确定。
    2、堆栈展开数据结构
    栈帧展开在可执行文件的eh_frame节中中体现。正如之前所说的,这个结构和调试信息中的debug_frame信息是相似的,使用的都是dwarf格式的文件结构,但是两者有一个重要的区别,debug结构的frame在strip之后就不再包含调试信息,而且调试信息默认是不会加载的内存中的,只是放在内存中,当调试器需要的时候从硬盘上读取数据。
    在eh_frame中包含的是一个一个的fde结构,每个fde结构描述了一个函数堆栈的栈帧信息,包含了最为基本的一个函数的其实地址、长度以及不同的指令回复需要的字节码,也就是byte code。

    例如下面是一个完整的fde结构
    [root@Harry exception]# readelf --debug-dump=frames  *.o
    Contents of the .eh_frame section:

    00000000 00000018 00000000 CIE
      Version:               1
      Augmentation:          "zPR"
      Code alignment factor: 1
      Data alignment factor: -4
      Return address column: 8
      Augmentation data:     00 00 00 00 00 1b

      DW_CFA_def_cfa: r4 (esp) ofs 4
      DW_CFA_offset: r8 (eip) at cfa-4

    0000001c 00000018 00000020 FDE cie=00000000 pc=00000000..0000003e cie表示cie的位置,PC表示该fde覆盖的范围,开始的第一项应该是该节的起始地址
      DW_CFA_advance_loc: 1 to 00000001
      DW_CFA_def_cfa_offset: 8
      DW_CFA_advance_loc: 2 to 00000003
      DW_CFA_offset: r5 (ebp) at cfa-8
      DW_CFA_def_cfa_register: r5 (ebp)
      DW_CFA_nop
      DW_CFA_nop
      DW_CFA_nop

    00000038 0000001c 00000000 CIE
      Version:               1
      Augmentation:          "zPLR"
      Code alignment factor: 1
      Data alignment factor: -4
      Return address column: 8
      Augmentation data:     00 00 00 00 00 00 1b

      DW_CFA_def_cfa: r4 (esp) ofs 4
      DW_CFA_offset: r8 (eip) at cfa-4
      DW_CFA_nop
      DW_CFA_nop

    00000058 00000020 00000024 FDE cie=00000038 pc=00000000..00000063
      Augmentation data:     00 00 00 00

      DW_CFA_advance_loc: 1 to 00000001
      DW_CFA_def_cfa_offset: 8
      DW_CFA_advance_loc: 2 to 00000003
      DW_CFA_offset: r5 (ebp) at cfa-8
      DW_CFA_def_cfa_register: r5 (ebp)
      DW_CFA_advance_loc: 20 to 00000017
      DW_CFA_offset: r3 (ebx) at cfa-16
      DW_CFA_offset: r6 (esi) at cfa-12
      DW_CFA_nop
      DW_CFA_nop

    0000007c 0000003c 00000048 FDE cie=00000038 pc=00000063..000000c4
      Augmentation data:     0c 00 00 00 这里有一些扩展字段,这些字段也就是额外的L(LSDA的数据结构位置)

      DW_CFA_advance_loc: 1 to 00000064
      DW_CFA_def_cfa_offset: 8
      DW_CFA_advance_loc: 2 to 00000066
      DW_CFA_offset: r5 (ebp) at cfa-8
      DW_CFA_def_cfa_register: r5 (ebp)
      DW_CFA_advance_loc: 8 to 0000006e
      DW_CFA_expression: r3 (ebx) (DW_OP_breg5: 0; DW_OP_const1s: -16; DW_OP_and; DW_OP_const1s: -8; DW_OP_plus)
      DW_CFA_expression: r6 (esi) (DW_OP_breg5: 0; DW_OP_const1s: -16; DW_OP_and; DW_OP_const1s: -4; DW_OP_plus)
      DW_CFA_advance_loc1: 81 to 000000bf
      DW_CFA_restore: r3 (ebx)
      DW_CFA_advance_loc: 1 to 000000c0
      DW_CFA_restore: r6 (esi)
      DW_CFA_advance_loc: 2 to 000000c2
      DW_CFA_def_cfa_register: r4 (esp)
      DW_CFA_advance_loc: 1 to 000000c3
      DW_CFA_restore: r5 (ebp)
      DW_CFA_def_cfa_offset: 4

    最后的nop是为了长度对其而添加的内容,其中的advance_loc则表示了PC指针的动态变化,所以真正的字节码解析器(byte code interpreter)需要将这些loc指令转换为绝对地址,也就是再次细化一个函数内的结构信息(函数外信息则在FDE的开始PC位置表示)。这里也说明了代码涉及的一个重要原则,就是对不同的逻辑单位定义对应的专有结构。
    3、cie和pde的关系
    cie是一个编码格式、fde内容,对齐大小、返回地址寄存器等信息的存储单位,这个信息通常来说变化不大,不同的fde明显可以共享这些信息,所以这些cie被独立出来,不同结构的FDE可以共享这些信息,例如上面的两个结构中,可能包含有两种不同的结构,他们的一个明显区别就在于时候包含了L字符,而这个字符的意义在代码中的解析为
    gcc-4.1.0gccunwind-dw2-fde.c
    /* Return the FDE pointer encoding from the CIE.  */
    /* ??? This is a subset of extract_cie_info from unwind-dw2.c.  */
    static int
    get_cie_encoding (const struct dwarf_cie *cie)
    {
      const unsigned char *aug, *p;
      _Unwind_Ptr dummy;
      _Unwind_Word utmp;
      _Unwind_Sword stmp;

      aug = cie->augmentation;
      if (aug[0] != 'z')
        return DW_EH_PE_absptr;

      p = aug + strlen ((const char *)aug) + 1; /* Skip the augmentation string.  */
      p = read_uleb128 (p, &utmp);        /* Skip code alignment.  */
      p = read_sleb128 (p, &stmp);        /* Skip data alignment.  */
      if (cie->version == 1)        /* Skip return address column.  */
        p++;
      else
        p = read_uleb128 (p, &utmp);

      aug++;                /* Skip 'z' */
      p = read_uleb128 (p, &utmp);        /* Skip augmentation length.  */
      while (1)
        {
          /* This is what we're looking for.  */
          if (*aug == 'R')
        return *p;
          /* Personality encoding and pointer.  */
          else if (*aug == 'P')
        {
          /* ??? Avoid dereferencing indirect pointers, since we're
             faking the base address.  Gotta keep DW_EH_PE_aligned
             intact, however.  */
          p = read_encoded_value_with_base (*p & 0x7F, 0, p + 1, &dummy);
        }
          /* LSDA encoding.  */
          else if (*aug == 'L')
        p++
    ;
          /* Otherwise end of string, or unknown augmentation.  */
          else
        return DW_EH_PE_absptr;
          aug++;
        }
    }
    4、LSDA及gcc_except_table
    这个结构是异常特有结构,它不仅包含了try+catch的结构,而且包含了cleanup的信息,所谓的cleanup就是当一个函数调用的更为底层的函数抛出了异常时,自己已经执行过构造函数的对象需要执行这些对象的析构函数,析构之后再返回到上层执行展开或者析构。用例子来展示一下:
          1 struct nvd
          2 {
          3 ~nvd() {int i = 0;};
          4 };
          5 int foo()
          6 {
          7 int bar();
          8 nvd n;
          9 bar();
         10 }
    在这个例子中,如果bar中抛出了异常,那么局部变量n的析构函数需要被执行,执行之后返回到foo的上一层,继续判断是执行cleanup还是执行catch中的某一个动作。在gcc的异常管理结构中,在刚才说明的fde中,大家应该记得在CIE的开始有一个编码方式'L',它表示了一个LSDA(Language  Specification Data Area)结构,而这个结构就引导了整个异常处理结构的开始。这个LSDA在之前的FDE的augmentation 中有描述,所以可以找到这些字段。这些字段包括了Type Info信息,也就是catch中指明的不同类型的变量,这些类型是匹配一个异常语句是否匹配被执行的关键,该函数中try结构或者说包含有析构函数的block的起始位置和结束位置call site,当call site内发生异常时需要跳转到的位置 landing pad信息。当然也不是说每个函数都有LSDA,有些不会抛出异常的函数(例如一些没有任何调用的叶子函数)就不需要LSDA,这也是为什么我们在上面的例子中看到两种不同的CIE的原因,因为一种类型的函数是叶子函数,这个属性在之后的例子展示中还有说明,所以大家注意一下。
    gcc-4.1.0libstdc++-v3libsupc++eh_personality.cc
    PERSONALITY_FUNCTION (_Unwind_State state,
                  struct _Unwind_Exception* ue_header,
                  struct _Unwind_Context* context)
      language_specific_data = (const unsigned char *)
        _Unwind_GetLanguageSpecificData (context);
    void *
    _Unwind_GetLanguageSpecificData (struct _Unwind_Context *context)
    {
      return context->lsda;
    }
    而这个LSDA的初始化位置在
    static _Unwind_Reason_Code
    uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
      if (fs->lsda_encoding != DW_EH_PE_omit)
        {
          _Unwind_Ptr lsda;
          
          aug = read_encoded_value (context, fs->lsda_encoding, aug, &lsda);
          context->lsda = (void *) lsda;
        }
    5、LSDA的解析
    gcc-4.1.0gccunwind-c.c
    typedef struct
    {
      _Unwind_Ptr Start;  tryblock的起始地址
      _Unwind_Ptr LPStart; 结束地址。
      _Unwind_Ptr ttype_base;
      const unsigned char *TTypecatch(type)中type信息布局,同样是将相同结构的信息放在一起组成数组结构,便于代码中通过自然循环遍历
      const unsigned char *action_table;一个try对应的一组catch结构列表,多个catch并列在一起可能组成catch的table
      unsigned char ttype_encoding;
      unsigned char call_site_encoding;
    } lsda_header_info;

    static const unsigned char *
    parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
               lsda_header_info *info)
    {
      _Unwind_Word tmp;
      unsigned char lpstart_encoding;

      info->Start = (context ? _Unwind_GetRegionStart (context) : 0);

      /* Find @LPStart, the base to which landing pad offsets are relative.  */
      lpstart_encoding = *p++;
      if (lpstart_encoding != DW_EH_PE_omit)
        p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
      else
        info->LPStart = info->Start;

      /* Find @TType, the base of the handler and exception spec type data.  */
      info->ttype_encoding = *p++;
      if (info->ttype_encoding != DW_EH_PE_omit)
        {
          p = read_uleb128 (p, &tmp);
          info->TType = p + tmp;
        }
      else
        info->TType = 0;

      /* The encoding and length of the call-site table; the action table
         immediately follows.  */
      info->call_site_encoding = *p++;
      p = read_uleb128 (p, &tmp);
      info->action_table = p + tmp;

      return p;
    }
    6、personality处理及gcc_except_table
    注意的是,堆栈的展开是在上层由eh_frame完成的,它不涉及异常处理相关逻辑,事实上eh_frame是和gdb的堆栈信息非常接近的,所以它并不是专门为C++的异常处理程序设定的,异常的特有结构是在except_table中设定的。而这个结构的解析和使用又是由具体的personality函数完成
    gcc-4.1.0libstdc++-v3libsupc++eh_personality.cc
    extern "C" _Unwind_Reason_Code
    #ifdef __ARM_EABI_UNWINDER__
    PERSONALITY_FUNCTION (_Unwind_State state,
                  struct _Unwind_Exception* ue_header,
                  struct _Unwind_Context* context)
    #else
    PERSONALITY_FUNCTION (int version,
                  _Unwind_Action actions,
                  _Unwind_Exception_Class exception_class,
                  struct _Unwind_Exception *ue_header,
                  struct _Unwind_Context *context)
    #endif
    {
      enum found_handler_type
      {
        found_nothing,
        found_terminate,
        found_cleanup,
        found_handler
      } found_type;

      lsda_header_info info;
      const unsigned char *language_specific_data;
      const unsigned char *action_record;
      const unsigned char *p;
      _Unwind_Ptr landing_pad, ip;
      int handler_switch_value;
      void* thrown_ptr = ue_header + 1;
      bool foreign_exception;
      __cxa_exception* xh = __get_exception_header_from_ue(ue_header);

      // Interface version check.
      if (version != 1)
        return _URC_FATAL_PHASE1_ERROR;
      foreign_exception = !__is_gxx_exception_class(exception_class);


      // Shortcut for phase 2 found handler for domestic exception.
      if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
          && !foreign_exception)
        {
          restore_caught_exception(ue_header, handler_switch_value,
                       language_specific_data, landing_pad);
          found_type = (landing_pad == 0 ? found_terminate : found_handler);
          goto install_context;
        }

      language_specific_data = (const unsigned char *)
        _Unwind_GetLanguageSpecificData (context);

      // If no LSDA, then there are no handlers or cleanups.
      if (! language_specific_data)
        CONTINUE_UNWINDING
    如果没有LSDA,则将异常直接透传到更上层,本层不处理,也不出错

      // Parse the LSDA header.
      p = parse_lsda_header (context, language_specific_data, &info);
      info.ttype_base = base_of_encoded_value (info.ttype_encoding, context);
      ip = _Unwind_GetIP (context) - 1;
      landing_pad = 0;
      action_record = 0;
      handler_switch_value = 0;
    遍历actiontable,也就是一个function中所有的可能出错的block结构的范围,其中的cs_start和cs_len分表表示这些结构的开始和长度,其中cs为call site的缩写。
      // Search the call-site table for the action associated with this IP.
      while (p < info.action_table)
        {
          _Unwind_Ptr cs_start, cs_len, cs_lp;
          _Unwind_Word cs_action;

          // Note that all call-site encodings are "absolute" displacements.
          p = read_encoded_value (0, info.call_site_encoding, p, &cs_start); action对应的call site的开始
          p = read_encoded_value (0, info.call_site_encoding, p, &cs_len); call site的长度
          p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);   landing pad的位置,
          p = read_uleb128 (p, &cs_action);    对应的catch blocks的开始位置。

          // The table is sorted, so if we've passed the ip, stop.
          if (ip < info.Start + cs_start)
        p = info.action_table;
          else if (ip < info.Start + cs_start + cs_len)
        {
          if (cs_lp)
            landing_pad = info.LPStart + cs_lp;如果landing pad的地址非空,说明存在cleanup或者action结构
          if (cs_action) 存在action,说明存在catch结构
            action_record = info.action_table + cs_action - 1;
          goto found_something;
        }
        }
    执行到这里,说明函数包含有LSDA,但是当前异常地址不在任何一个call site范围内,此时程序将会被结束,及出现了未被处理的异常,此时将会终止掉程序的执行,下面的注释也就是这个意思。。
      // If ip is not present in the table, call terminate.  This is for
      // a destructor inside a cleanup, or a library routine the compiler
      // was not expecting to throw.

      found_type = found_terminate;
      goto do_something;

     found_something:
      if (landing_pad == 0)
        {
          // If ip is present, and has a null landing pad, there are
          // no cleanups or handlers to be run.
          found_type = found_nothing;
        }
      else if (action_record == 0)
        {
          // If ip is present, has a non-null landing pad, and a null
          // action table offset, then there are only cleanups present.
          // Cleanups use a zero switch value, as set above
    .
          found_type = found_cleanup;
        }
      else
        {
          // Otherwise we have a catch handler or exception specification.

          _Unwind_Sword ar_filter, ar_disp;
          const std::type_info* catch_type;
          _throw_typet* throw_type;
          bool saw_cleanup = false;
          bool saw_handler = false;

          // During forced unwinding, we only run cleanups.  With a foreign
          // exception class, there's no exception type.
          // ??? What to do about GNU Java and GNU Ada exceptions.

          if ((actions & _UA_FORCE_UNWIND)
          || foreign_exception)
        throw_type = 0;
          else
    #ifdef __ARM_EABI_UNWINDER__
        throw_type = ue_header;
    #else
        throw_type = xh->exceptionType;
    #endif

          while (1)遍历所有的catch blocks,调用每个不同的catch的filter结构,进行动态类型匹配,找到第一个匹配的类型之后返回
        {
          p = action_record;
          p = read_sleb128 (p, &ar_filter);
          read_sleb128 (p, &ar_disp);

          if (ar_filter == 0)
            {
              // Zero filter values are cleanups.
              saw_cleanup = true;
            }
          else if (ar_filter > 0)
            {
              // Positive filter values are handlers.
              catch_type = get_ttype_entry (&info, ar_filter);

              // Null catch type is a catch-all handler; we can catch foreign 对应于catch(...)语句
              // exceptions with this.  Otherwise we must match types.
              if (! catch_type
              || (throw_type
                  && get_adjusted_ptr (catch_type, throw_type,
                           &thrown_ptr)))
            {
              saw_handler = true;
              break;
            }
            }
          else
            {
              // Negative filter values are exception specifications.
              // ??? How do foreign exceptions fit in?  As far as I can
              // see we can't match because there's no __cxa_exception
              // object to stuff bits in for __cxa_call_unexpected to use.
              // Allow them iff the exception spec is non-empty.  I.e.
              // a throw() specification results in __unexpected.
              if (throw_type
              ? ! check_exception_spec (&info, throw_type, thrown_ptr, 动态类型识别及匹配
                            ar_filter)
              : empty_exception_spec (&info, ar_filter))
            {
              saw_handler = true;
              break;
            }
            }

          if (ar_disp == 0)
            break;
          action_record = p + ar_disp;
        }

          if (saw_handler)
        {
          handler_switch_value = ar_filter;
          found_type = found_handler;
        }
          else
        found_type = (saw_cleanup ? found_cleanup : found_nothing);
        }

     do_something:
       if (found_type == found_nothing)
         CONTINUE_UNWINDING
    ;如果没有找到处理函数,则继续展开,这里包含了所有catch的block中均不匹配的情况也在这里返回

      if (actions & _UA_SEARCH_PHASE)
        {
          if (found_type == found_cleanup)
        CONTINUE_UNWINDING;

          // For domestic exceptions, we cache data from phase 1 for phase 2.
          if (!foreign_exception)
            {
          save_caught_exception(ue_header, context, thrown_ptr,
                    handler_switch_value, language_specific_data,
                    landing_pad, action_record);
        }
          return _URC_HANDLER_FOUNDsearch阶段,terminate也走该流程
        }

     install_context:
      
      // We can't use any of the cxa routines with foreign exceptions,
      // because they all expect ue_header to be a struct __cxa_exception.
      // So in that case, call terminate or unexpected directly.
      if ((actions & _UA_FORCE_UNWIND)
          || foreign_exception)
        {
          if (found_type == found_terminate)
        std::terminate ();
          else if (handler_switch_value < 0)
        {
          try 
            { std::unexpected (); } 
          catch(...) 
            { std::terminate (); }
        }
        }
      else
        {
          if (found_type == found_terminate)
        __cxa_call_terminate(ue_header);

          // Cache the TType base value for __cxa_call_unexpected, as we won't
          // have an _Unwind_Context then.
          if (handler_switch_value < 0)
        {
          parse_lsda_header (context, language_specific_data, &info);

    #ifdef __ARM_EABI_UNWINDER__
          const _Unwind_Word* e;
          _Unwind_Word n;
          
          e = ((const _Unwind_Word*) info.TType) - handler_switch_value - 1;
          // Count the number of rtti objects.
          n = 0;
          while (e[n] != 0)
            n++;

          // Count.
          ue_header->barrier_cache.bitpattern[1] = n;
          // Base (obsolete)
          ue_header->barrier_cache.bitpattern[2] = 0;
          // Stride.
          ue_header->barrier_cache.bitpattern[3] = 4;
          // List head.
          ue_header->barrier_cache.bitpattern[4] = (_Unwind_Word) e;
    #else
          xh->catchTemp = base_of_encoded_value (info.ttype_encoding, context);
    #endif
        }
        }

      /* For targets with pointers smaller than the word size, we must extend the
         pointer, and this extension is target dependent.  */
      _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
             __builtin_extend_pointer (ue_header));
      _Unwind_SetGR (context, __builtin_eh_return_data_regno (1),
             handler_switch_value);
      _Unwind_SetIP (context, landing_pad); 调整IP位置,也就是landing pad位置,开始跳转。
    #ifdef __ARM_EABI_UNWINDER__
      if (found_type == found_cleanup)
        __cxa_begin_cleanup(ue_header);
    #endif
      return _URC_INSTALL_CONTEXT;
    }
    7、一个异常处理函数的例子
    [root@Harry exception]# cat nestteddest.cpp 
    #include <stdio.h>
    struct nvdest
    {
    ~nvdest() {printf("destructing ");}
    };
    int foo()
    {
        nvdest outter;
        extern int bar();
        {
        nvdest nvdest;
        try
        {
            bar();
        }
        catch(int &var)
        {
            printf("gotcha ");
        }
        }
    }
    int baz()
    {
        nvdest outer;
        {
            nvdest inner;
            extern int bay();
            bay();
        }
        printf("I am a marker ");    
    }
    [root@Harry exception]# cat nestteddest.cpp 
    #include <stdio.h>
    struct nvdest
    {
    ~nvdest() {printf("destructing ");}
    };
    int foo()
    {
        nvdest outter;
        extern int bar();
        {
        nvdest nvdest;
        try
        {
            bar();
        }
        catch(int &var)
        {
            printf("gotcha ");
        }
        }
    }
    int baz()
    {
        nvdest outer;
        {
            nvdest inner;
            extern int bay();
            bay();
        }
        printf("I am a marker ");    
    }
    [root@Harry exception]# gcc -S -dA -fverbose-asm nestteddest.cpp 
    [root@Harry exception]# cat nestteddest.s 
        .file    "nestteddest.cpp"
    # GNU C++ (GCC) version 4.4.2 20091027 (Red Hat 4.4.2-7) (i686-redhat-linux)
    #    compiled by GNU C version 4.4.2 20091027 (Red Hat 4.4.2-7), GMP version 4.3.1, MPFR version 2.4.1.
    # GGC heuristics: --param ggc-min-expand=98 --param ggc-min-heapsize=128396
    # options passed:  -D_GNU_SOURCE nestteddest.cpp -mtune=generic -march=i686
    # -fverbose-asm
    # options enabled:  -falign-loops -fargument-alias -fauto-inc-dec
    # -fbranch-count-reg -fcommon -fdwarf2-cfi-asm -fearly-inlining
    # -feliminate-unused-debug-types -fexceptions -ffunction-cse -fgcse-lm
    # -fident -finline-functions-called-once -fira-share-save-slots
    # -fira-share-spill-slots -fivopts -fkeep-static-consts
    # -fleading-underscore -fmath-errno -fmerge-debug-strings
    # -fmove-loop-invariants -fpcc-struct-return -fpeephole -fsched-interblock
    # -fsched-spec -fsched-stalled-insns-dep -fsigned-zeros
    # -fsplit-ivs-in-unroller -ftrapping-math -ftree-coalesce-vars
    # -ftree-cselim -ftree-loop-im -ftree-loop-ivcanon -ftree-loop-optimize
    # -ftree-parallelize-loops= -ftree-reassoc -ftree-scev-cprop
    # -ftree-switch-conversion -ftree-vect-loop-version -funit-at-a-time
    # -fvect-cost-model -fverbose-asm -fzero-initialized-in-bss -m32 -m80387
    # -m96bit-long-double -maccumulate-outgoing-args -malign-stringops
    # -mfancy-math-387 -mfp-ret-in-387 -mglibc -mieee-fp -mno-red-zone
    # -mno-sse4 -mpush-args -msahf -mtls-direct-seg-refs

    # Compiler executable checksum: 1654075adcfd832dfb7b0208272c8238

        .section    .rodata
    .LC0:
        .string    "destructing "
        .section    .text._ZN6nvdestD1Ev,"axG",@progbits,_ZN6nvdestD1Ev,comdat
        .align 2
        .weak    _ZN6nvdestD1Ev
        .type    _ZN6nvdestD1Ev, @function
    _ZN6nvdestD1Ev:
    .LFB2:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        # basic block 2
        pushl    %ebp    #
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp    #,
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        subl    $24, %esp    #,
        movl    $.LC0, (%esp)    #,
        call    puts    #
        leave
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
        .cfi_endproc
    .LFE2:
        .size    _ZN6nvdestD1Ev, .-_ZN6nvdestD1Ev
        .section    .rodata
    .LC1:
        .string    "gotcha"
    .globl _Unwind_Resume
        .text
    .globl _Z3foov
        .type    _Z3foov, @function
    _Z3foov:
    .LFB3:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        .cfi_lsda 0x0,.LLSDA3
        # basic block 2
        pushl    %ebp    #
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp    #,
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        pushl    %esi    #
        pushl    %ebx    #
        subl    $32, %esp    #,
    .LEHB0:
        .cfi_offset 3, -16
        .cfi_offset 6, -12
        call    _Z3barv    #
    .LEHE0:
        # basic block 3
        jmp    .L5    #
    .L15:
        # basic block 4
        cmpl    $1, %edx    #, tmp68
        jne    .L10    #,
    .L6:
        # basic block 5
        movl    %eax, (%esp)    # tmp67,
        call    __cxa_begin_catch    #
        movl    %eax, -12(%ebp)    # var.0, var
        movl    $.LC1, (%esp)    #,
    .LEHB1:
        call    puts    #
    .LEHE1:
        # basic block 6
    .LEHB2:
        call    __cxa_end_catch    #
    .LEHE2:
        # basic block 7
        jmp    .L5    #
    .L14:
        # basic block 8
    .L8:
        movl    %edx, %ebx    # tmp68, save_filt.6
        movl    %eax, %esi    # tmp67, save_eptr.5
        call    __cxa_end_catch    #
        movl    %esi, %eax    # save_eptr.5, tmp67
        movl    %ebx, %edx    # save_filt.6, tmp68
        jmp    .L10    #
    .L5:
        # basic block 9
        leal    -14(%ebp), %eax    #, tmp69
        movl    %eax, (%esp)    # tmp69,
    .LEHB3:
        call    _ZN6nvdestD1Ev    #
    .LEHE3:
        # basic block 10
        jmp    .L18    #
    .L16:
        # basic block 11
    .L10:
        # basic block 12
        movl    %edx, %ebx    # tmp68, save_filt.8
        movl    %eax, %esi    # tmp67, save_eptr.7
        leal    -14(%ebp), %eax    #, tmp70
        movl    %eax, (%esp)    # tmp70,
        call    _ZN6nvdestD1Ev    #
        movl    %esi, %eax    # save_eptr.7, tmp67
        movl    %ebx, %edx    # save_filt.8, tmp68
        jmp    .L11    #
    .L18:
        # basic block 13
        leal    -13(%ebp), %eax    #, tmp71
        movl    %eax, (%esp)    # tmp71,
    .LEHB4:
        call    _ZN6nvdestD1Ev    #
    .LEHE4:
        addl    $32, %esp    #,
        popl    %ebx    #
        .cfi_remember_state
        .cfi_restore 3
        popl    %esi    #
        .cfi_restore 6
        popl    %ebp    #
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
    .L17:
        # basic block 14
    .L11:
        # basic block 15
        .cfi_restore_state
        movl    %edx, %ebx    # tmp68, save_filt.10
        movl    %eax, %esi    # tmp67, save_eptr.9
        leal    -13(%ebp), %eax    #, tmp72
        movl    %eax, (%esp)    # tmp72,
        call    _ZN6nvdestD1Ev    #
        movl    %esi, %eax    # save_eptr.9, tmp67
        movl    %ebx, %edx    # save_filt.10, tmp68
        movl    %eax, (%esp)    # tmp67,
    .LEHB5:
        call    _Unwind_Resume    #
    .LEHE5:
        .cfi_endproc
    .LFE3:
        .size    _Z3foov, .-_Z3foov
    .globl __gxx_personality_v0
        .section    .gcc_except_table,"a",@progbits
        .align 4
    .LLSDA3:
        .byte    0xff    # @LPStart format (omit)
        .byte    0x0    # @TType format (absolute)
        .uleb128 .LLSDATT3-.LLSDATTD3    # @TType base offset
    .LLSDATTD3:
        .byte    0x1    # call-site format (uleb128)
        .uleb128 .LLSDACSE3-.LLSDACSB3    # Call-site table length
    .LLSDACSB3:
        .uleb128 .LEHB0-.LFB3    # region 0 start
        .uleb128 .LEHE0-.LEHB0    # length
        .uleb128 .L15-.LFB3    # landing pad
        .uleb128 0x3    # action
        .uleb128 .LEHB1-.LFB3    # region 1 start
        .uleb128 .LEHE1-.LEHB1    # length
        .uleb128 .L14-.LFB3    # landing pad
        .uleb128 0x0    # action
        .uleb128 .LEHB2-.LFB3    # region 2 start
        .uleb128 .LEHE2-.LEHB2    # length
        .uleb128 .L16-.LFB3    # landing pad
        .uleb128 0x0    # action
        .uleb128 .LEHB3-.LFB3    # region 3 start
        .uleb128 .LEHE3-.LEHB3    # length
        .uleb128 .L17-.LFB3    # landing pad
        .uleb128 0x0    # action
        .uleb128 .LEHB4-.LFB3    # region 4 start
        .uleb128 .LEHE4-.LEHB4    # length
        .uleb128 0x0    # landing pad
        .uleb128 0x0    # action
        .uleb128 .LEHB5-.LFB3    # region 5 start
        .uleb128 .LEHE5-.LEHB5    # length
        .uleb128 0x0    # landing pad
        .uleb128 0x0    # action
    .LLSDACSE3:
        .byte    0x0    # Action record table
        .byte    0x0
        .byte    0x1
        .byte    0x7d
        .align 4
        .long    _ZTIi
    .LLSDATT3:
        .text
        .section    .rodata
    .LC2:
        .string    "I am a marker"
        .text
    .globl _Z3bazv
        .type    _Z3bazv, @function
    _Z3bazv:
    .LFB4:
        .cfi_startproc
        .cfi_personality 0x0,__gxx_personality_v0
        .cfi_lsda 0x0,.LLSDA4
        # basic block 2
        pushl    %ebp    #
        .cfi_def_cfa_offset 8
        movl    %esp, %ebp    #,
        .cfi_offset 5, -8
        .cfi_def_cfa_register 5
        pushl    %esi    #
        pushl    %ebx    #
        subl    $32, %esp    #,
    .LEHB6:
        .cfi_offset 3, -16
        .cfi_offset 6, -12
        call    _Z3bayv    #
    .LEHE6
    从下面表格知道,该区间对应的landing pad为L24,即对于bay的调用在try的区间内
        # basic block 3
        leal    -10(%ebp), %eax    #, tmp63
        movl    %eax, (%esp)    # tmp63,
    .LEHB7:
        call    _ZN6nvdestD1Ev    #局部变量inner的析构也在try的保护范围内。
    .LEHE7
    :
        # basic block 4
        jmp    .L26    #
    .L24:
        # basic block 5
    .L21:
        movl    %edx, %ebx    # tmp64, save_filt.2
        movl    %eax, %esi    # tmp65, save_eptr.1
        leal    -10(%ebp), %eax    #, tmp66
        movl    %eax, (%esp)    # tmp66,
        call    _ZN6nvdestD1Ev    #
        movl    %esi, %eax    # save_eptr.1, tmp65
        movl    %ebx, %edx    # save_filt.2, tmp64
        jmp    .L22
        # bay函数如果抛出异常,跳转到该block执行,从L21开始,此处跳转到L22,再次执行外层变量的析构函数这里有一点非常重要,那就是这个cleanup函数本身不在任何的call site范围内,所以在cleanup阶段析构函数如果再次出现异常,将会在personality函数中执行到有LSDA,但是异常PC不再任何一个call site的情况,此时也就是满足了handler为terminate的条件,此时整个进程被结束。这样就是为什么一些书上说不要在析构函数中抛出异常的原因:cleanup阶段抛出的异常对程序是致命的,没有人能捕获,或者说是二次异常问题
    .L26:
        # basic block 6
        movl    $.LC2, (%esp)    #,
    .LEHB8:
        call    puts    #
    .LEHE8:
        # basic block 7
        leal    -9(%ebp), %eax    #, tmp67
        movl    %eax, (%esp)    # tmp67,
    .LEHB9:
        call    _ZN6nvdestD1Ev    #
    .LEHE9:
        addl    $32, %esp    #,
        popl    %ebx    #
        .cfi_remember_state
        .cfi_restore 3
        popl    %esi    #
        .cfi_restore 6
        popl    %ebp    #
        .cfi_restore 5
        .cfi_def_cfa 4, 4
        ret
    .L25:
        # basic block 8
    .L22:
        # basic block 9
        .cfi_restore_state
        movl    %edx, %ebx    # tmp64, save_filt.4
        movl    %eax, %esi    # tmp65, save_eptr.3
        leal    -9(%ebp), %eax    #, tmp68
        movl    %eax, (%esp)    # tmp68,
        call    _ZN6nvdestD1Ev    #
        movl    %esi, %eax    # save_eptr.3, tmp65
        movl    %ebx, %edx    # save_filt.4, tmp64
        movl    %eax, (%esp)    # tmp65,
    .LEHB10:
        call    _Unwind_Resume    #
    .LEHE10:
        .cfi_endproc
    .LFE4:
        .size    _Z3bazv, .-_Z3bazv
        .section    .gcc_except_table
    .LLSDA4:
        .byte    0xff    # @LPStart format (omit)
        .byte    0xff    # @TType format (omit)
        .byte    0x1    # call-site format (uleb128)
        .uleb128 .LLSDACSE4-.LLSDACSB4    # Call-site table length
    .LLSDACSB4:
        .uleb128 .LEHB6-.LFB4    # region 0 start
        .uleb128 .LEHE6-.LEHB6    # length
        .uleb128 .L24-.LFB4    # landing pad
        .uleb128 0x0    # action

        .uleb128 .LEHB7-.LFB4    # region 1 start 对于局部变量inner的析构受保护,跳转到25处。
        .uleb128 .LEHE7-.LEHB7    # length
        .uleb128 .L25-.LFB4    # landing pad
        .uleb128 0x0    # action

        .uleb128 .LEHB8-.LFB4    # region 2 start
        .uleb128 .LEHE8-.LEHB8    # length
        .uleb128 .L25-.LFB4    # landing pad
        .uleb128 0x0    # action
        .uleb128 .LEHB9-.LFB4    # region 3 start
        .uleb128 .LEHE9-.LEHB9    # length
        .uleb128 0x0    # landing pad
        .uleb128 0x0    # action
        .uleb128 .LEHB10-.LFB4    # region 4 start
        .uleb128 .LEHE10-.LEHB10    # length
        .uleb128 0x0    # landing pad
        .uleb128 0x0    # action
    .LLSDACSE4:
        .text
        .ident    "GCC: (GNU) 4.4.2 20091027 (Red Hat 4.4.2-7)"
        .section    .note.GNU-stack,"",@progbits
    [root@Harry exception]#

  • 相关阅读:
    【java基础学习一】int[]、Integer[]、String[] 排序( 正序、倒叙)、去重
    【转】jqGrid 各种参数 详解
    CSS 中文字体的英文名称 (simhei, simsun) 宋体 微软雅黑
    Web应用程序项目XX已配置为使用IIS
    zsh安装及配置
    vscode安装及配置
    matlab2018a安装及配置
    teminator安装及配置
    clion安装及配置
    pcl之octree的使用
  • 原文地址:https://www.cnblogs.com/tsecer/p/10487516.html
Copyright © 2011-2022 走看看