This is the mail archive of the
mailing list for the Archer project.
C++ Exceptions Status
- From: Phil Muldoon <pmuldoon at redhat dot com>
- To: Project Archer <archer at sourceware dot org>
- Date: Wed, 06 Aug 2008 13:38:20 +0100
- Subject: C++ Exceptions Status
I've written this up to accompany the call today. I have spent the week
investigating C++ exceptions; I have come up with these tasks so far.
Work - however - is on-going. I wanted to brain-dump the tasks I have
created for archive, for peer-review and sanity checking. I've created
gnats PRs for all of these tasks with reproducers/test-cases where
needed. Comments welcome. If you have encountered any of below, can add
to them, or disagree with some statements - please add comments in-line
or to the PR. If you believe there are any tasks missing, please add.
Task #1 (gnats pr 2493): Documentation change regarding the "catch"
command. The text needs to be updated regarding current "catch throw or
catch catch" support and the confusion between "raised exceptions" and
"c++ exceptions" clarified.
Task #2 (gnats pr 2494): "catch throw" and "catch catch" return control
of the inferior inside the: "__cxa_throw" and "__cxa_begin_catch"
functions respectively. This is not a very good user experience. I would
expect "catch throw" and "catch catch" to return control of the
inferior in the users' code, just before the throw or catch code
executes. The current infrastructure sets a breakpoint on: "__cxa_throw"
for "catch throw" and "__cxa_begin_catch" for "catch catch"
respectively. I believe that having the inferior stop here is
interesting for only a small subset of C++ users. The task here is
stopping the inferior at the throw/catch, instead of just after.
A different but related issue concerns me with 2493 and 2494. At the
moment, only C++ exceptions are caught via "catch catch" and "catch
throw". This is done in a specific way related directly to the level 2
C++ ABI mechanics. But, how is "catch catch" and "catch throw" going to
work and expand to other languages like Java and Python? The generic
model that exists is just not going to work. Therefore some thought must
be given to a language model inside GDB, instead of relying on library
or OS specifics to achieve support of features such as stopping on
exception raising and handling.
Task #3 (gnats pr 2495): Performing an inferior function call on a
function that contains a throw with an out-of-frame exception handler
causes GDB to lose control of the inferior. What I believe is happening
here is the "__cxa_throw" is invoking "__Unwind_RaiseException" and that
is attempting to find a handler during the _UA_SEARCH_PHASE step.
Because the handler exists in another frame, and this is an inferior
function call with a tinkered stack, the unwinder in not finding a
handler. This causes "__Unwind_RaiseException" to return with with the
error: _URC_END_OF_STACK and the default handler is called. I believe
this default handler just terminates the application. With a handler in
the frame, this does not occur as the handler is found in the inferior
Task #4 (gnats pr 2488): Using "next" command over a C++ "throw"
causes GDB to lose control of the inferior. This has taken a large
amount of time as it involved having to step through the unwinder code
and fully map out the life-cycle of a C++ exception. At the most basic
level, the "next" command relies on longjmp breakpoints to reassert
control of the inferior after it has been resumed. Unfortunately this
will not work with the system unwinder when matching and then
transferring control to exceptions handlers. A brief look at the life of
a C++ exception is needed to illustrate further.
There are 3 basic states and flow transfer that I understand happen on a
throw. A basic work-flow of a throw is:
yourfunction.c: throw foo
The first phase of the unwind process is: _UA_SEARCH_PHASE. This
searches for an exception handler in the current frame (via the C++
personality routine found in eh_personality.cc in this case). If it
finds a handler it returns: _URC_HANDLER_FOUND. If it does not it
returns: _URC_CONTINUE_UNWIND. The process continues frame-by-frame
until if finds an acceptable handler. If it cannot find a handler in any
frame, _URC_END_OF_STACK is returned, and the default C++ handler is
* If the default C++ handler is called, the next command should return
control of the inferior at this call in _Unwind_RaiseException.
At this point the unwinder knows the frame of the exception handler for
the exception. The second phase of the unwind process is started with
the: _UA_CLEANUP_PHASE. The unwinder calls the personality routine and
asks if clean-up is needed in the current frame. In the case of an
out-of-frame exception handler and the local-frame has local scope
variables, there will be a need to clean-up those variables. In this
case a call to uw_install_context is made in the unwinder and control
* If a local scope variable destructor is called as part of the clean-up
phase the next command should return control of the inferior in the
local variable destructor.
After clean-up has been completed - or no clean-up is needed -
unwinding is resumed and control returned to the exception handler in
the frame found in the search phase. In the case of an in-frame
exception handler, control is transferred immediately to the in-frame
handler - assuming that no local scope variables did not need to be
cleaned-up in a local block. This again is done via uw_install_context.
* If there was no clean-up to perform, or the handler is in-frame,
control should be returned at the catch statement in the appropriate
frame, in the users' code.
The issue that I am currently wrestling with is how specific is this
unwind and exception work to C++? Are Java and Python exceptions handled
in the same way? And how can we avoid language specific semantics in
very general commands like "next"? Can we?