【问题标题】:Reaching unwind handlers到达展开处理程序
【发布时间】:2018-01-14 11:15:48
【问题描述】:

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

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

再次无条件跳转到返回块和似乎无法到达的展开块(从第二个 lea rdi 开始)。

【问题讨论】:

  • 您是否尝试过在启用优化的情况下进行编译?
  • 异常处理程序不应该通常可以访问,这就是重点
  • 好吧,让我改写一下:异常处理程序无法通过正常的控制流访问。您只查看正常的控制流,因此它们无法访问。
  • 通过展开过程,所以看一下.eh_frame之类的
  • @PaulFloyd 如果您在没有优化的情况下进行编译,那么在二进制文件中看到死(无法访问)代码并不少见。毕竟,您通过关闭优化器来禁止编译删除此类代码。

标签: c++ assembly x86-64 stack-unwinding


【解决方案1】:

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

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

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

    # 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

我猜想unwind handler函数可以根据栈上的地址和这张表的偏移量计算出unwind handler块的位置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-19
    • 2015-06-15
    • 2011-01-01
    • 2020-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多