简体   繁体   中英

Reaching unwind handlers

Any idea why code that looks like this

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

Generates the following machine code

    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

In particular, I don't see any paths leading to the line after the unconditional jmp .L11

(this is with GCC 6.2 with no optimization, generated on compiler explorer)

For comparison, clang 5.0.0 produces

    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

Again there is an unconditional jump to a return block, and and unwind block (starting with the second lea rdi ) that seems unreachable.

After a bit of research on C++ exception mechanisms, my conclusion is that the process is as follows:

  1. At the point of exception throw, __cxa_throw gets called. This is somewhat like longjmp() in that the function gets called but never returns. The function performs two main tasks
    • It walks up the call stack looking for a catch. If it doesn't find any, std::terminate gets called.
    • If it does find a catch block then it calls all of the unwind handlers between the current function and the catch block, then calls the catch block.

Back to my original machine code (with filtering turned off in compiler explorer). My comments after the hashes.

    # 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:

Then there is the exception table

   .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

I guess that the unwind handler function can work out the positions of the unwind handler blocks from the addresses on the stack and the offsets in this table.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM