• Please explain exception handling in x86_64 Linux

    From Frederick Virchanza Gotham@cauldwell.thomas@nospicedham.gmail.com to comp.lang.asm.x86 on Mon Apr 24 08:20:32 2023
    From Newsgroup: comp.lang.asm.x86

    I've written the following C++ code as an example:
    #include <cstdio> // puts
    extern void LibFunc(void) noexcept(false); // likely to throw an exception void Func(void)
    {
    try
    {
    LibFunc();
    }
    catch (...)
    {
    std::puts("caught");
    }
    }
    gets compiled to:
    .LC0:
    .string "caught"
    Func():
    push rbx
    call LibFunc()
    jmp .L1
    mov rdi, rax
    call __cxa_begin_catch
    mov edi, OFFSET FLAT:.LC0
    call puts
    call __cxa_end_catch
    .L1:
    pop rbx
    ret
    mov rbx, rax
    call __cxa_end_catch
    mov rdi, rbx
    call _Unwind_Resume
    There's a few things I don't understand here:
    (1) Why does Func save and restore the RBX register if it never changes it? I realise that RBX is callee-saved and so you must push it onto the stack before altering it, but I don't see anywhere where it's altered.
    (2) If 'LibFunc' throws an exception, how does it know where to jump back to? In normal circumstances if the function returned normally, it would jump back to the 'jmp .L1' instruction, but instead it has to jump back to one instruction after that. How does it know what offset of the return address to jump back to?
    (3) I don't know why those last four lines are there. They look like unreachable code to me.
    Can anyone enlighten me please?
    --- Synchronet 3.21d-Linux NewsLink 1.2
  • From Thorsten Glaser@tg@nospicedham.mirbsd.de to comp.lang.asm.x86 on Wed Apr 26 13:43:27 2023
    From Newsgroup: comp.lang.asm.x86

    Frederick Virchanza Gotham dixit:
    (1) Why does Func save and restore the RBX register if it never changes it? Compilers tend to do that (frame pointer things).
    (2) If 'LibFunc' throws an exception, how does it know where to jump
    back to? In normal circumstances if the function returned normally, it
    would jump back to the 'jmp .L1' instruction, but instead it has to
    jump back to one instruction after that. How does it know what offset
    of the return address to jump back to?
    This is only one form of exception handling: it uses unwind information
    from a separate part of the executable to know where to jump back to.
    In normal operation, the call returns normally, then the jmp is
    executed terminating the function. If an exception is thrown, the
    handler walks back the call stack then changes the return address.
    ThererCOs also setjmp/longjmp-based exception handling (rCLsjljrCY if you
    ever compile GCC) which uses these libc functions instead of relying
    on magic debugging information to unwind. That may be easier to
    understand so I suggest having a look at that.
    (3) I don't know why those last four lines are there. They look like >unreachable code to me.
    As above, theyrCOll be reached by changing the return address. Your
    compiler probably added some .cfi_* pseudo-ops for the assembler
    there which would have the corresponding debugging info.
    bye,
    //mirabilos
    --
    15:41rAL<Lo-lan-do:#fusionforge> Somebody write a testsuite for helloworld :-) --- Synchronet 3.21d-Linux NewsLink 1.2