[llvm-dev] Catching exceptions while unwinding through -fno-exceptions code

Modi Mo via llvm-dev llvm-dev at lists.llvm.org
Wed Dec 9 19:16:15 PST 2020


The gcc_personality_v0 in compiler-rt is actually kind of an oddball. It maps to a custom GCC extension `__attribute__((cleanup(xx)))` (https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html) which is “destructors but for C” which has to work if you have:
C++ code calling C code calling C++ code
To ensure that this attribute is correctly maintained when throwing from C++ to C++ code with C code sandwiched in the middle.

For (1) if the exception type desired is the C++ exception type, then that’ll definitely move this into libcxxabi. There’s a lower level code (https://github.com/llvm-project/llvm-project-dev/blob/master/libcxxabi/src/cxa_exception.hpp#L23) that exists to identify if an exception is from Clang/GCC/MSVC which would still be suitable for compiler-rt.

I think the rest of nice-to-have also move it towards libcxxabi. Stack unwinding and function context for C++ have ABI specifications that lock it into a C++ runtime library.

My suggestion would be to get a std::terminate implementation with acceptable size bloat tested and working. That by itself can be reviewed and is self-contained such that a more robust solution can easily take its place later. As Hubert points out, you can also override std::terminate with https://en.cppreference.com/w/cpp/error/set_terminate so there’s quite some flexibility there.

Adding runtime code is (in my experience) pretty difficult because of backwards-compatibility and the larger surface area.

Modi

On 12/9/20, 3:51 PM, "llvm-dev on behalf of Everett Maus via llvm-dev" <llvm-dev-bounces at lists.llvm.org<mailto:llvm-dev-bounces at lists.llvm.org> on behalf of llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>> wrote:

It depends on the amount of functionality here and how language specific this is. As it stands I don’t think this is C++ specific and could extend to other llvm language targets which makes it a better candidate with compiler-rt. It would be good to flesh out what behavior you want out of the personality function then go from there.

That makes sense.

I think there's one "must have" behavior and a few "very nice-to-have but not required" behaviors.

The "must have" for the handler would be "it terminates the program when an exception passes through code compiled with -fno-exceptions, hopefully in a way that can be easily identified as different from a normal/healthy program exit".

The nice-to-have list is probably longer:
1. Printing the exception type that was thrown
2. Recovering the place the exception was thrown /from/ and printing the function name & offset.
3. Printing the function name that the unwinder reached that led to it exiting.
4. (Extra fancy/pie in the sky "what I'd want if I were debugging this situation"): printing the full demangled stack to the location the exception was thrown from or the method that led to the exit.

I expect that (1) requires some language-specific handling (RTTI in C++/etc.), so that'd imply that it might be good to have a libcxxabi implementation even if there's also a compiler-rt version (it looks like this is the case with __gxx_personality_v0 which is defined both in compiler-rt here: https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/builtins/gcc_personality_v0.c#L172 and in libcxxabi here: https://github.com/llvm/llvm-project/blob/main/libcxxabi/src/cxa_personality.cpp#L953

I also think that possibly (2)/(3)/(4) might require some non-compiler-rt components, but I'm not certain if they would (I haven't dug into what's in compiler-rt in depth quite yet).  That may also be reasonable to defer or to have a sanitizer handle.

--EJM


On Wed, Dec 9, 2020 at 12:15 PM Reid Kleckner via llvm-dev <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>> wrote:
Bleh, early send. We'd also have to overcome the issue of functions being nounwind with -fno-exceptions, which means LLVM would optimize those invokes to calls immediately.

On Wed, Dec 9, 2020 at 12:14 PM Reid Kleckner <rnk at google.com<mailto:rnk at google.com>> wrote:
Using existing personality functions requires emitting an LLVM IR cleanup around every function when building in this -fterminate-exceptions mode. Then all functions would have IR like this:

invoke void @foo(...) unwind to %terminate
invoke void @bar(...) unwind to %terminate
...
landingpad cleanup ...
  call void @myterminate()

On Tue, Dec 8, 2020 at 12:56 PM James Y Knight <jyknight at google.com<mailto:jyknight at google.com>> wrote:
Why is adding a new personality function useful? Can't you share a single LSDA table for every noexcept function in a TU (both those implicitly noexcept due to -fno-exceptions and for those marked "noexcept")?

On Tue, Dec 8, 2020 at 1:05 PM Reid Kleckner via llvm-dev <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>> wrote:
I would suggest using a custom personality function for this. It will optimize better and be much smaller than using a standard personality function. It saves the LSDA tables.

LLVM supports custom personality functions, so only clang changes are required. You could either do something like add a flag to override the EH personality with a custom one, or come up with a new dedicated fno-exceptions termination personality and add it to compiler-rt.

On Mon, Dec 7, 2020 at 3:31 PM Modi Mo via llvm-dev <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>> wrote:
If you don’t need to capture more information and can just terminate, you can directly register std::terminate as the personality routine as opposed to __gxx_personality_v0 or __CxxFrameHandler3/4 (Windows) which lets you omit other metadata and work cross-platform.

Modi

From: llvm-dev <llvm-dev-bounces at lists.llvm.org<mailto:llvm-dev-bounces at lists.llvm.org>> on behalf of Everett Maus via llvm-dev <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>>
Reply-To: Everett Maus <evmaus at google.com<mailto:evmaus at google.com>>
Date: Monday, December 7, 2020 at 12:47 PM
To: "llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>" <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>>
Subject: [llvm-dev] Catching exceptions while unwinding through -fno-exceptions code

Hey all:

I wanted to bring up something that was discussed a few years ago around the behavior of exceptions when interacting with code compiled with -fno-exceptions. (In https://lists.llvm.org/pipermail/llvm-dev/2017-February/109992.html<https://lists.llvm.org/pipermail/llvm-dev/2017-February/109992.html> and https://lists.llvm.org/pipermail/llvm-dev/2017-February/109995.html<https://lists.llvm.org/pipermail/llvm-dev/2017-February/109995.html>)

It's possible to compile (and link/etc.) code with -fexceptions for some compilation units and -fno-exceptions for others.  Unlike the behavior of noexcept (which requires termination), this doesn't have a specified behavior in the C++ standard as far as I can tell.  However, it can lead to memory leaks & other issues (e.x. with TSAN, it messes up the tracking of the current stack frame).

I'd be interested in looking into potentially doing the work to add an option to clang/etc. to terminate when an exception traverses code compiled with -fno-exceptions, instead of simply allowing the unwinder to walk through the stack frame & leak memory/etc. (possibly behind a flag?).  This particular issue bit a team I work closely with, and I'd imagine it could be causing subtle issues for other clang users.

I'm mostly concerned with solving this on Linux/x86_64, although if there's a way to solve it more generally I'm open to looking into doing that instead.

I /think/ the right place to change this (from the discussions I linked) would be in the LLVM -> assembly layer, adding an appropriate .gcc_except_table for functions that are determined to be unable to throw exceptions (either due to noexcept or due to -fno-exceptions). Then the unwinder would find .eh_frame but no entry in the .gcc_except_table and should terminate (via  __gxx_personality_v0).

Am I understanding that correctly?  What's the best way to propose this sort of change to clang? (document/just try to look at putting together a PR/other?)

Alternatively--one other thing that occurred to me is that it could be reasonably cheap to simply add try/catch blocks that report an UBSAN error in all methods that shouldn't be able to throw an exception.  This obviously doesn't fix the code-generation problem and would lead to larger binary sizes, but that seems less bad for an UBSAN build in particular.  That would likely meet my needs around wanting a way to automatically detect this behavior/problem, but might not address the more generic issue.

Thanks,
--
--EJM
_______________________________________________
LLVM Developers mailing list
llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev<https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev>
_______________________________________________
LLVM Developers mailing list
llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev<https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev>
_______________________________________________
LLVM Developers mailing list
llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev<https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev>


--
--EJM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20201210/c652e1aa/attachment.html>


More information about the llvm-dev mailing list