Reference:
- When we write a throw statement,
the compiler will translate it into a pair of calls into libstdc++ functions
that allocate the exception and then start the stack unwinding process by calling libstdc. - For each catch statement, the compiler will write some special information after the method's body, a table of exceptions this method can catch and a cleanup table (more on the cleanup table later).
- As the unwinder goes through the stack it will call a special function provided by libstdc++(called personality routine) that checks for each function in the stack which exceptions can be caught.
- If no matching catch is found for the exception, std::terminate is called.
(No RAII object clean up happend. Same as calling std::exit() ) - If a matching catch is found, the unwinder now starts again on the top of the stack.
- As the unwinder goes through the stack a second time it will
ask the personality routine to perform a cleanup for this method. - The personality routine will check the cleanup table on the current method.
If there are any cleanup actions to be run, it will "jump" into the current stack
frame and run the cleanup code.
This will run the destructor for each object allocated at the *current* scope. - Once the unwinder reaches the frame in the stack that can handle the exception it will jump into the proper catch statement.
- Upon finishing the execution of the catch statement, a cleanup function will be called to release the memory held for the exception.
Runtime functions involved:
__cxa_allocate_exceptionMost implementations seem to allocate memory on a local thread storage (heap)
but resort to an emergency storage (presumably static) if out of memory.
We, of course, don't want to worry about the ugly details
so we can just have a static buffer if we want to.
__cxa_throw
Once the exception has been created __cxa_throw will be called.
This function will be responsible of starting the stack unwinding.
__cxa_throw is never supposed to return.
It either delegates execution to the correct catch block to handle
the exception or calls (by default) std::terminate, but it never ever returns.
vtable for __cxxabiv1::__class_type_info
This is the entry point the ABI defines to know (in runtime)
whether two types are the same or not.
This is the function that gets called to determine whether a
catch(Parent) can handle a throw Child.
We need to give it an address for the linker
(ie defining it won't be enough, we need to instantiate it)
and it has to have a vtable (that is, it must have a virtual method).
Detail:
The throw keyword is compiled into two function calls to libstdc++ :- __cxa_allocate_exception
- __cxa_throw
seppuku: .LFB3: [...] call __cxa_allocate_exception movl $0, 8(%esp) movl $_ZTI9Exception, 4(%esp) movl %eax, (%esp) call __cxa_throw [...]
__cxa_allocate_exception and __cxa_throw "live" on libstdc++
__cxa_allocate_exception will allocate memory for the new exception
__cxa_throw will prepare a bunch of stuff and forward this exception to _Unwind_ ,
a set of functions that live in libstdc and perform the real stack unwinding
(the ABI defines the interface for these functions)
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.