1

知道为什么代码看起来像这样

list<Foo> fooList;
processList(&fooList);

生成以下机器码

    lea     rax, [rbp-48]
    mov     rdi, rax
    call    processList(std::__cxx11::list<Foo, std::allocator<Foo> >*)
    lea     rax, [rbp-48]
    mov     rdi, rax
    call    std::__cxx11::list<Foo, std::allocator<Foo> >::~list()
    jmp     .L11
    mov     rbx, rax
    lea     rax, [rbp-48]
    mov     rdi, rax
    call    std::__cxx11::list<Foo, std::allocator<Foo> >::~list()
    mov     rax, rbx
    mov     rdi, rax
    call    _Unwind_Resume
.L11:
    add     rsp, 40
    pop     rbx
    pop     rbp
    ret

特别是,在无条件之后,我看不到任何通往该行的路径jmp .L11

(这是在编译器资源管理器上生成的没有优化的 GCC 6.2)

为了比较,clang 5.0.0 产生

    call    processList(std::__cxx11::list<Foo, std::allocator<Foo> >*)
    jmp     .LBB5_1
.LBB5_1:
    lea     rdi, [rbp - 24]
    call    std::__cxx11::list<Foo, std::allocator<Foo> >::~list()
    add     rsp, 48
    pop     rbp
    ret
    lea     rdi, [rbp - 24]
    mov     ecx, edx
    mov     qword ptr [rbp - 32], rax
    mov     dword ptr [rbp - 36], ecx
    call    std::__cxx11::list<Foo, std::allocator<Foo> >::~list()
    mov     rdi, qword ptr [rbp - 32]
    call    _Unwind_Resume

再次无条件跳转到 return 块,并且 unwind 块(从 second 开始lea rdi)似乎无法到达。

4

1 回答 1

3

经过对C++异常机制的一点研究,我的结论是流程如下:

  1. 在异常抛出点,__cxa_throw被调用。这有点像longjmp()函数被调用但从不返回。该函数执行两个主要任务
    • 它沿着调用堆栈向上寻找捕获。如果它没有找到,std::terminate就会被调用。
    • 如果它确实找到了一个 catch 块,那么它会调用当前函数和 catch 块之间的所有展开处理程序,然后调用 catch 块。

回到我原来的机器代码(在编译器资源管理器中关闭过滤)。我在哈希后的评论。

    # this is the normative path
    call    std::list<Handle, std::allocator<Handle> >::~list()
    # unconditional jump around the unwind handler
    jmp     .L11
.L10:
    # unwind handler code, calls the local variable destructor
    mov     rbx, rax
    .loc 2 30 0
    lea     rax, [rbp-32]
    mov     rdi, rax
    call    std::list<Handle, std::allocator<Foo> >::~list()
    mov     rax, rbx
    mov     rdi, rax
.LEHB1:
    # carry on unwinding
    call    _Unwind_Resume

.L11:

然后是异常表

   .section        .gcc_except_table,"a",@progbits
.LLSDA1386:
    .byte   0xff
    .byte   0xff
    .byte   0x1
    .uleb128 .LLSDACSE1386-.LLSDACSB1386
.LLSDACSB1386:
    # entry for unwind handler
    .uleb128 .LEHB0-.LFB1386
    .uleb128 .LEHE0-.LEHB0
    .uleb128 .L10-.LFB1386
    .uleb128 0
    .uleb128 .LEHB1-.LFB1386
    .uleb128 .LEHE1-.LEHB1
    .uleb128 0
    .uleb128 0

我猜想展开处理函数可以从堆栈上的地址和这张表中的偏移量计算出展开处理块的位置。

于 2018-01-14T17:36:55.723 回答