G
G
Governor2017-10-03 01:44:05
C++ / C#
Governor, 2017-10-03 01:44:05

How do exceptions work?

I watched videos, read articles, but nowhere is it really described how exceptions work.
Can anyone explain at a low level, step by step, how exceptions work in c++?
For more details about why the stack is untwisted there and why.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
J
jcmvbkbc, 2017-10-03
@Mr-Governor

Can anyone explain at a low level, step by step, how exceptions work in c++?

Here is a description of the part of Itanium ABI related to stack unwinding:
https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html
The ABIs of processors of other architectures are arranged in this place in exactly the same way.
Here is a detailed description of the structures used in stack unwinding in gcc generated code:
www.airs.com/blog/archives/460
www.airs.com/blog/archives/464 Linked
documents are quite complex to read. To make it easier to understand, you can compile a simple C++ code that throws and catches an exception and find in it the parts described in the first document.
for example
void e_destructor();
void s_destructor();

struct E {
        int code;

        E(int c): code(c)
        {
        }
        ~E()
        {
                e_destructor();
        }
};

struct S {
        ~S()
        {
                s_destructor();
        }
};

void f(int v)
{
        throw E(v);
}

int g(void (*p)(int v), int v)
{
        try {
                struct S s;
                p(v);
        } catch(struct E e) {
                return e.code;
        } catch (int i) {
                return i;
        } catch (...) {
                throw;
        }
        return 0;
}

after g++ -O2 -S turns into the following snippets:
function f:
_Z1fi:
.LFB9:
        .cfi_startproc
        pushq   %rbx
        .cfi_def_cfa_offset 16
        .cfi_offset 3, -16
        movl    %edi, %ebx
        movl    $4, %edi
        call    __cxa_allocate_exception
        movl    $_ZN1ED1Ev, %edx
        movl    %ebx, (%rax)  <---- инициализация E::code
        movl    $_ZTI1E, %esi
        movq    %rax, %rdi
        call    __cxa_throw
        .cfi_endproc

Here you can see the calls to __cxa_allocate_exception, the class E object constructor, and the __cxa_throw
function g:
_Z1gPFviEi:
.LFB10:
        .cfi_startproc
        .cfi_personality 0x3,__gxx_personality_v0
        .cfi_lsda 0x3,.LLSDA10
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        pushq   %rbx
        .cfi_def_cfa_offset 24
        .cfi_offset 3, -24
        movq    %rdi, %rax
        movl    %esi, %edi
        subq    $8, %rsp
        .cfi_def_cfa_offset 32
.LEHB0:
        call    *%rax  <--- вызов функции по указателю
.LEHE0:
.LEHB1:
        call    _Z12s_destructorv  <--- вызов деструктора объекта s при нормальном выходе из блока try
.LEHE1:
        xorl    %eax, %eax
.L17:
        addq    $8, %rsp
        .cfi_remember_state
        .cfi_def_cfa_offset 24
        popq    %rbx
        .cfi_def_cfa_offset 16
        popq    %rbp
        .cfi_def_cfa_offset 8
        ret

Tail with exception handlers:
.L13:
        .cfi_restore_state
        movq    %rdx, %rbx
        movq    %rax, %rbp
        call    _Z12s_destructorv
        movq    %rbx, %rdx
.L6:
        cmpq    $1, %rdx
        je      .L8
        cmpq    $2, %rdx
        jne     .L22
        movq    %rbp, %rdi
        call    __cxa_begin_catch
        movl    (%rax), %ebx
        call    __cxa_end_catch
        movl    %ebx, %eax
        jmp     .L17
.L14:
        movq    %rax, %rbp
        jmp     .L6
.L22:
        movq    %rbp, %rdi
        call    __cxa_begin_catch
.LEHB2:
        call    __cxa_rethrow
.LEHE2:
.L8:
        movq    %rbp, %rdi
        call    __cxa_get_exception_ptr
        movq    %rbp, %rdi
        movl    (%rax), %ebx
        call    __cxa_begin_catch
.LEHB3:
        call    _Z12e_destructorv
.LEHE3:
.LEHB4:
        call    __cxa_end_catch
.LEHE4:
        movl    %ebx, %eax
        jmp     .L17
.L16:
        movq    %rax, %rbx
        call    __cxa_end_catch
        movq    %rbx, %rdi
.LEHB5:
        call    _Unwind_Resume
.LEHE5:
.L15:
        movq    %rax, %rbx
        call    __cxa_end_catch
        movq    %rbx, %rdi
.LEHB6:
        call    _Unwind_Resume
.LEHE6:
        .cfi_endproc

Here you can see calls to __cxa_begin_catch and __cxa_end_catch, __cxa_rethrow rethrows the caught exception, __cxa_get_exception_ptr and _Unwind_Resume, called if the catch block does not catch this exception.
Next comes the LSDA structure described in the third document.
There is no stack unwinding itself in this code. It is executed by the following libgcc code: phase 1 and phase 2 .

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question