GCC C++ 异常处理实现

11

我想了解GCC如何为C++程序实现异常处理。我在网上找不到易于理解和自我解释的文章(虽然对于Visual C++来说有很多这样的文章)。我所知道的是,GCC的实现被称为DWARF异常处理。

我编写了一个小的C++程序,并使用以下命令将其转换为汇编:

g++ main.cpp -S -masm=intel -fno-dwarf2-cfi-asm

这里提供了main.cppmain.s文件。请问有人可以逐行解释main.s文件的内容,特别是.gcc_except_table.eh_frame部分吗?(我的操作系统是Ubuntu 13.04 32位。)谢谢!

main.cpp:

void f()
{
    throw 1;
}

int main()
{
    int j;
    try {
        f();
    } catch (int i) {
        j = i;
    }   
    return 0;
}

main.s:

.file "main.cpp"
.intel_syntax noprefix
.text
.globl  _Z1fv
.type   _Z1fv, @function
_Z1fv:
.LFB0:
    push    ebp
.LCFI0:
    mov ebp, esp
.LCFI1:
    sub esp, 24
    mov DWORD PTR [esp], 4
    call    __cxa_allocate_exception
    mov DWORD PTR [eax], 1
    mov DWORD PTR [esp+8], 0
    mov DWORD PTR [esp+4], OFFSET FLAT:_ZTIi
    mov DWORD PTR [esp], eax
    call    __cxa_throw
.LFE0:
    .size   _Z1fv, .-_Z1fv
    .globl  main
    .type   main, @function
main:
.LFB1:
    push    ebp
.LCFI2:
    mov ebp, esp
.LCFI3:
    and esp, -16
    sub esp, 32
.LEHB0:
    call    _Z1fv
.LEHE0:
.L7:
    mov eax, 0
    jmp .L9
.L8:
    cmp edx, 1
    je  .L6
    mov DWORD PTR [esp], eax
.LEHB1:
    call    _Unwind_Resume
.LEHE1:
.L6:
    mov DWORD PTR [esp], eax
    call    __cxa_begin_catch
    mov eax, DWORD PTR [eax]
    mov DWORD PTR [esp+24], eax
    mov eax, DWORD PTR [esp+24]
    mov DWORD PTR [esp+28], eax
    call    __cxa_end_catch
    jmp .L7
.L9:
    leave
.LCFI4:
    ret
.LFE1:
    .globl  __gxx_personality_v0
    .section    .gcc_except_table,"a",@progbits
    .align 4
.LLSDA1:
    .byte   0xff
    .byte   0
    .uleb128 .LLSDATT1-.LLSDATTD1
.LLSDATTD1:
    .byte   0x1
    .uleb128 .LLSDACSE1-.LLSDACSB1
.LLSDACSB1:
    .uleb128 .LEHB0-.LFB1
    .uleb128 .LEHE0-.LEHB0
    .uleb128 .L8-.LFB1
    .uleb128 0x1
    .uleb128 .LEHB1-.LFB1
    .uleb128 .LEHE1-.LEHB1
    .uleb128 0
    .uleb128 0
.LLSDACSE1:
    .byte   0x1
    .byte   0
    .align 4
    .long   _ZTIi
.LLSDATT1:
    .text
    .size   main, .-main
    .section    .eh_frame,"a",@progbits
.Lframe1:
    .long   .LECIE1-.LSCIE1
.LSCIE1:
    .long   0
    .byte   0x1
    .string "zPL"
    .uleb128 0x1
    .sleb128 -4
    .byte   0x8
    .uleb128 0x6
    .byte   0
    .long   __gxx_personality_v0
    .byte   0
    .byte   0xc
    .uleb128 0x4
    .uleb128 0x4
    .byte   0x88
    .uleb128 0x1
    .align 4
.LECIE1:
.LSFDE1:
    .long   .LEFDE1-.LASFDE1
.LASFDE1:
    .long   .LASFDE1-.Lframe1
    .long   .LFB0
    .long   .LFE0-.LFB0
    .uleb128 0x4
    .long   0
    .byte   0x4
    .long   .LCFI0-.LFB0
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI1-.LCFI0
    .byte   0xd
    .uleb128 0x5
    .align 4
.LEFDE1:
.LSFDE3:
    .long   .LEFDE3-.LASFDE3
.LASFDE3:
    .long   .LASFDE3-.Lframe1
    .long   .LFB1
    .long   .LFE1-.LFB1
    .uleb128 0x4
    .long   .LLSDA1
    .byte   0x4
    .long   .LCFI2-.LFB1
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI3-.LCFI2
    .byte   0xd
    .uleb128 0x5
    .byte   0x4
    .long   .LCFI4-.LCFI3
    .byte   0xc5
    .byte   0xc
    .uleb128 0x4
    .uleb128 0x4
    .align 4
.LEFDE3:
    .ident  "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3"
    .section    .note.GNU-stack,"",@progbits

3
GCC使用Itanium ABI,该ABI描述了异常处理的方式。 - Kerrek SB
1
以上链接已失效。@KerrekSB - FaceBro
2
@FaceBro:好的,也许可以看一下 https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html - Kerrek SB
2个回答

21

Itanium ABI(gcc、clang和其他许多编译器都遵循的规范)指定异常处理应该遵循零成本策略

零成本策略的想法是将所有异常处理推入侧表中,这些表不在主程序执行路径上(因此不会破坏指令缓存)。这些表由程序点索引。

此外,DWARF信息(实际上是调试信息)用于展开堆栈。通常提供此功能的库如libunwind等,源代码充斥着汇编(因此非常特定于平台)。

优点:

  • 进入try/catch块的成本为0(与无异常处理相同的速度)
  • 在函数中有一个throw语句的成本为0(只要不执行它)

缺点:

  • 在出现异常时速度慢(比if策略慢10倍),因为侧表通常不在缓存中,然后需要运行昂贵的计算才能知道哪个catch子句实际上与之匹配(基于RTTI)。

这是一个非常流行的策略,实现在32位和64位平台上的所有主要编译器上...除了MSVC 32位(如果我记得正确的话)。


10

.eh_frame布局在LSB文档中简要描述。Ian Lance Taylor(gold链接器的作者)还在.eh_frame.gcc_except_table布局上发表了一些博客文章。

如需更详细的参考描述,请查看我的Recon 2012幻灯片(从37开始)。

编辑:这是您示例中的带注释结构。首先,是.eh_table(为了清晰起见省略了一些部分):

.Lframe1:                     # start of CFI 1
    .long   .LECIE1-.LSCIE1   # length of CIE 1 data
.LSCIE1:                      # start of CIE 1 data
    .long   0                 # CIE id
    .byte   0x1               # Version
    .string "zPL"             # augmentation string:
                              # z: has augmentation data
                              # P: has personality routine pointer
                              # L: has LSDA pointer
    .uleb128 0x1              # code alignment factor
    .sleb128 -4               # data alignment factor
    .byte   0x8               # return address register no.
    .uleb128 0x6              # augmentation data length (z)
    .byte   0                 # personality routine pointer encoding (P): DW_EH_PE_ptr|DW_EH_PE_absptr
    .long   __gxx_personality_v0 # personality routine pointer (P)
    .byte   0                 # LSDA pointer encoding: DW_EH_PE_ptr|DW_EH_PE_absptr
    .byte   0xc               # Initial CFI Instructions
    [...]
    .align 4
.LECIE1:                      # end of CIE 1
    [...]

.LSFDE3:                      # start of FDE 3
    .long   .LEFDE3-.LASFDE3  # length of FDE 3
.LASFDE3:                     # start of FDE 3 data
    .long   .LASFDE3-.Lframe1 # Distance to parent CIE from here
    .long   .LFB1             # initial location                
    .long   .LFE1-.LFB1       # range length                    
    .uleb128 0x4              # Augmentation data length (z)    
    .long   .LLSDA1           # LSDA pointer (L)                
    .byte   0x4               # CFI instructions                
    .long   .LCFI2-.LFB1
    [...]
    .align 4
.LEFDE3:                      # end of FDE 3

接下来,FDE 3所引用的 LSDA(语言特定数据区):

.LLSDA1:                           # LSDA 1
    .byte   0xff                   # LPStart encoding: DW_EH_PE_omit
    .byte   0                      # TType encoding: DW_EH_PE_ptr|DW_EH_PE_absptr
    .uleb128 .LLSDATT1-.LLSDATTD1  # TType offset
.LLSDATTD1:                        # LSDA 1 action table
    .byte   0x1                    # call site encoding: DW_EH_PE_uleb128|DW_EH_PE_absptr
    .uleb128 .LLSDACSE1-.LLSDACSB1 # call site table length
.LLSDACSB1:                        # LSDA 1 call site entries
    .uleb128 .LEHB0-.LFB1          # call site 0 start
    .uleb128 .LEHE0-.LEHB0         # call site 0 length
    .uleb128 .L8-.LFB1             # call site 0 landing pad
    .uleb128 0x1                   # call site 0 action (1=action 1)
    .uleb128 .LEHB1-.LFB1          # call site 1 start
    .uleb128 .LEHE1-.LEHB1         # call site 1 length
    .uleb128 0                     # call site 1 landing pad
    .uleb128 0                     # call site 1 action (0=no action)
.LLSDACSE1:                        # LSDA 1 action table entries
    .byte   0x1                    # action 1 filter (1=T1 typeinfo)
    .byte   0                      # displacement to next action (0=end of chain)
    .align 4
    .long   _ZTIi                  # T1 typeinfo ("typeinfo for int")
.LLSDATT1:                         # LSDA 1 TTBase

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接