【问题标题】:GCC C++ Exception Handling ImplementationGCC C++异常处理实现
【发布时间】:2013-09-11 09:43:02
【问题描述】:

我想知道 GCC 如何为 C++ 程序实现异常处理。我在 Web 上找不到易于理解且不言自明的文章(尽管有很多针对 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 位。)谢谢!

ma​​in.cpp:

void f()
{
    throw 1;
}

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

ma​​in.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

【问题讨论】:

标签: c++ gcc assembly exception-handling reverse-engineering


【解决方案1】:

Itanium ABI(gcc、clang 和许多其他遵循)指定异常处理应遵循 Zero-Cost strategy

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

此外,DWARF 信息(实际上是调试信息)用于展开堆栈。此功能通常以库的形式提供,例如 libunwind,例如,源代码是令人窒息的汇编(因此非常特定于平台)。

优点:

  • 0-进入try/catch块的成本(就像没有一样快)
  • 在函数中包含 throw 语句的成本为 0(只要不使用)

缺点:

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

对于所有主要编译器来说,它都是 32 位和 64 位平台上非常流行的策略实现......除了 MSVC 32 位(如果我没记错的话)。

【讨论】:

    【解决方案2】:

    .eh_frame 布局在LSB documentation 中有简要描述。 Ian Lance Taylor(黄金链接器的作者)也发表了一些博客文章 on .eh_frame.gcc_except_table layout

    如需更多类似参考的描述,请查看我的Recon 2012 slides(从 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-02
      • 1970-01-01
      • 2020-12-21
      • 2011-02-17
      • 2011-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多