[PATCH, ARM] RFC: Backtracing through C++ exception-handling constructs

Julian Brown julian@codesourcery.com
Mon Jul 30 14:31:00 GMT 2012


Hi,

I've been investigating a patch which we've been using locally to fix
an issue with backtraces (using, e.g., glibc's backtrace() function)
through C++ exception-handling constructs on ARM. The original author of
the patch was Daniel Jacobowitz (please correct me if my understanding
is wrong!).

There are two issues in play here:

1. Exception-handling is handled in a target-specific way for ARM,
defined in the EHABI document ("Exception handling ABI for the ARM
architecture", IHI 0038A). However, no mention of "forced unwinding" is
made in this document.

2. Backtracing in particular isn't even the "normal" use case for
forced unwinding: e.g.,

http://www.ucw.cz/~hubicka/papers/abi/node25.html#SECTION00923200000000000000

suggests that forced unwinding is "a single-phase process (phase 2 of
the normal exception-handling process)", whereas for producing a
backtrace, something more like a phase 1 lookup is done (no cleanup
handlers are called -- we're merely observing the state of the stack).

So, to be clear, we're definitely dealing with a corner case here. The
problem is that _Unwind_Backtrace in libgcc will fail to make progress
in some cases if it hits a frame with a cleanup associated with it,
leading to unhelpful behaviour like (for the attached program):

bar calling abort
abort handler invoked, depth 25
./test() [0x8968]
../install/arm-none-linux-gnueabi/libc/lib/libc.so.6(__default_rt_sa_restorer_v2+0) [0x401cf860]
../install/arm-none-linux-gnueabi/libc/lib/libc.so.6(gsignal+0x40) [0x401ce5e0]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]
./test() [0x8a4c]

which is clearly wrong, no matter how you look at it. I'll defer to Dan
for a better description of the problem/fix (on a private branch, circa
February 2010):

"This bug was a failure of backtrace() when presented with a C++ abort
- in particular, one which inherited throw() but called cout, so
needed its own call to __cxa_call_unexpected.  We'd get stuck in a
loop in _Unwind_Backtrace because the code was not prepared for the
handler to return _URC_HANDLER_FOUND.

"The GCC ARM unwinders already have _US_FORCED_UNWIND passed to the
personality routine.  ISTM that forced unwinding when doing
essentially a 'phase 1' lookup has no other useful meaning, and this
is a useful meaning to assign to it: skip handlers, keep unwinding."

The patch still seems to produce a reasonable improvement in
behaviour, giving:

build$ arm-none-linux-gnueabi-g++ test.cc -o test -g

$ ./test
bar calling abort                                                               
abort handler invoked, depth 5                                                  
./test() [0x8968]                                                               
../install/arm-none-linux-gnueabi/libc/lib/libc.so.6(__default_rt_sa_restorer_v2+0) [0x401cf860]                                                                
../install/arm-none-linux-gnueabi/libc/lib/libc.so.6(gsignal+0x40) [0x401ce5e0] 
./test() [0x8a4c]                                                               
./test() [0x8a4c]

although tbh I'd hope for backtrace_symbols to produce something a
little more useful than that (unrelated to this patch), and I'd also
expect identical results from the test whether the "throw ()" is
present on the declaration of "bar", or not -- which unfortunately
isn't the case. Without throw (), we get:

bar calling abort
abort handler invoked, depth 8
./test() [0x897c]
../install/arm-none-linux-gnueabi/libc/lib/libc.so.6(__default_rt_sa_restorer_v2+0) [0x401cf860]
../install/arm-none-linux-gnueabi/libc/lib/libc.so.6(gsignal+0x40) [0x401ce5e0]
./test() [0x8a60]
./test() [0x8a60]
./test() [0x8a7c]
./test() [0x8acc]
../install/arm-none-linux-gnueabi/libc/lib/libc.so.6(__libc_start_main+0x114) [0x401b8e44]

I.e., it looks like the backtrace progresses all the way to the
outermost frame -- which IIUC, was the intended resulting behaviour for
the attached patch to start with.

So: does anyone have an opinion about whether the attached is a correct
fix, or if the spinning-during-backtrace problem might have a better
solution? (I'm a little fuzzy on the intricate details of all this
stuff!).

Thanks,

Julian

ChangeLog

    Daniel Jacobowitz  <drow@false.org>

    libstdc++-v3/
    * libsupc++/eh_personality.cc (PERSONALITY_FUNCTION): For
    ARM EABI, skip handlers for _US_VIRTUAL_UNWIND_FRAME
    | _US_FORCE_UNWIND.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: backtrace-through-throw-2.diff
Type: text/x-patch
Size: 645 bytes
Desc: not available
URL: <http://gcc.gnu.org/pipermail/gcc-patches/attachments/20120730/a6bba1d2/attachment.bin>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test.cc
Type: text/x-c++src
Size: 805 bytes
Desc: not available
URL: <http://gcc.gnu.org/pipermail/gcc-patches/attachments/20120730/a6bba1d2/attachment-0001.bin>


More information about the Gcc-patches mailing list