What is incomplete for /lib/libgcc_s.so-based C++ exception handling (where WITH_LLVM_LIBUNWIND= and /usr/local/lib/gcc*/libgcc_s.so are not used)

Mark Millard marklmi at yahoo.com
Thu Oct 18 23:43:54 UTC 2018


[I add a note indicating that clang++ does
generate examples of DW_CFA_remember_state
and DW_CFA_restore_state use for pwoerpc64
that lead to /lib/libgcc_s.so messing up
the exception handling.]

On 2018-Oct-17, at 1:25 PM, Mark Millard <marklmi at yahoo.com> wrote:

> [This summarizes other results without the code
> and debugger evidence and such from my recent
> explorations. It should be much easier to
> follow than my exploration reports.]
> 
> Documents like DWARF5.pdf document the "row" vs. Location
> information for Call Frame Information as (also used
> for .eh_frame like materials for C++ exception handling):
> (CFA: Cannonical Frame Address)
> 
> QUOTE ("Structure of Call Frame Information")
> 	LOC CFA R0 R1...RN
> 
> 	L0
> 
> 	L1
> 
> 	...
> 
> 	LN
> END QUOTE
> 
> Note that the CFA is conceptually one of the
> registers in each row, even though it is not a
> machine register but a way to calculate the
> conceptual register's value from machine
> registers.
> 
> The information for the machine registers are
> typically based on the earlier CFA value (from
> the same row!). Absent a correct CFA cell in
> a row, most potential use of that row is likely
> messed up.
> 
> One way CFA is found is by adding an offset to
> the value of a machine register for the range
> in question, Ln up to L(n+1) [or based on the
> end of the overall range for the last Ln]. I
> will use that for illustration because there
> are examples of this in my testing.
> 
> /lib/libgcc_s.so.1 does not implement this
> fully for some DW_CFA_* operations:
> 
> QUOTE (note the "every register" reference, so including CFA)
> DW_CFA_remember_state
> 
> The DW_CFA_remember_state instruction takes no operands. The required action is to push the set of rules for every register onto an implicit stack.
> 
> DW_CFA_restore_state
> 
> The DW_CFA_restore_state instruction takes no operands. The required action is to pop the set of rules off the implicit stack and place them in the current row.
> END QUOTE
> 
> In other words: push and pop a complete row,
> not just machine registers information from
> the row.
> 
> For example, the the "cfa_offset" for computing the CFA
> value from from a register is not saved and restored.
> Nor is which register the offset is based on. (This
> can vary, though not in my examples.) In general the
> CFA cell is not saved and restored, what ever its
> contents.
> 
> So any compiler that produces code depending on
> DW_CFA_remember_state and DW_CFA_restore_state
> for .eh_frame like material ends up with C++
> exception handling messed up when the
> DW_CFA_restore_state should change the CFA to a 
> non-default one (from the prior
> DW_CFA_remember_state).
> 
> This prevents reliable use of throwing C++ exceptions
> when building via the likes of devel/powerpc64-gcc
> or lang/gcc8 ( when not using
> -Wl,-rpath=-Wl,-rpath=/usr/local/lib/gcc8 so that
> /lib/libgcc_s.so.1 ends up being used). One result
> can be _Unwind_RaiseException looping looking at
> the same frame over and over instead of progressing
> to the next frame. For example, this happens via
> cfa_offset 0 being used. devel/powerpc64-gcc -O2
> code tends to get that.
> 
> 
> Notes:
> 
> For powerpc64, clang++ tends to use another register
> (%r31) with the old value (of %r1, the stack pointer)
> instead of involving the
> DW_CFA_remember_state/DW_CFA_restore_state pair
> based on just %r1. (clang has other problems
> relative to sue for buildworld buildkernel.)

/usr/tests/lib/atf/libatf-c++/detail/exceptions_test
has examples were clang++ generated use of
DW_CFA_remember_state and DW_CFA_restore_state and
/lib/libgcc_s.so 's _Unwind_RaiseException ends
up stuck looping. There are other examples as well.

The problem is not limited to devel/powerpc64-gcc
or other g++ use that uses /lib/libgcc_s.so .

> Code generation styles matter for if the incomplete
> coverage by /lib/libgcc_s.so will be visible or not.
> 
> At this stage, WITH_LLVM_LIBUNWIND= builds
> targeting powerpc64 do not even compile/assemble
> the relevant code, apparently both because of
> darwin specific assembler code and FreeBSD's build
> not using the C-preprocessor on the .S file as
> required. (There could be more to getting it
> working.)
> 
> I do not know about other architecture/compiler
> (or toolchain) combinations that may not yet be
> able to use WITH_LLVM_LIBUNWIND= . But I'd
> expect a potentially similar status from some.
> 
> A range of modern /usr/local/lib/gcc*/libgcc_s.so
> do implement DW_CFA_remember_state/DW_CFA_restore_state
> operations and they are put to use. So using the likes
> of -Wl,-rpath=/usr/local/lib/gcc8 works for g++8 C++
> exception handling (but is problematical for buildworld
> buildkernel).
> 
> I made a similar exploration of the issue in around
> early 2016 and got basically the same results, not that
> I remembered much. But I now have a small source code
> example that shows the cfa_offset issue for the likes
> of devel/powerpc64-gcc output.
> 
> The standard source for throw_exception in
> /lib/libgcc_s.so produces the cfa_offset problem
> when devel/powerpc64-gcc is used to buildworld.
> This turns all thrown C++ exceptions in to
> unbounded looping in _Unwind_RaiseException for
> that kind of context.


===
Mark Millard
marklmi at yahoo.com
( dsl-only.net went
away in early 2018-Mar)



More information about the freebsd-hackers mailing list