-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MinGW: Use _Unwind_RaiseException to throw exceptions #278
MinGW: Use _Unwind_RaiseException to throw exceptions #278
Conversation
@lazka @MehdiChinoune It seems like |
I think the right way for this to be implemented on MinGW should match the Itanium model, where throwing an exception that doesn’t have a handler returns from the unwind library. Unfortunately, when we tried this, the unwind library did not return. Maybe that’s fixed? MinGW’s hybrid of SEH and Itanium ABIs is quite painful and more effort to support than either, I wish they’d just use the Visual Studio ABI. |
Hmm, interesting. I rewrote Is something like this what you had in mind? extern "C"
OBJC_PUBLIC
void objc_exception_throw(id object)
{
id *exc = (id *)__cxxabiv1::__cxa_allocate_exception(sizeof(id));
*exc = object;
objc_retain(object);
DEBUG_LOG("objc_exception_throw: Throwing 0x%x\n", *exc);
__cxa_eh_globals *globals = __cxa_get_globals ();
globals->uncaughtExceptions += 1;
// Definitely a primary.
__cxa_refcounted_exception *header =
__cxa_init_primary_exception(exc, & __objc_id_type_info, eh_cleanup);
header->referenceCount = 1;
_Unwind_Reason_Code err = _Unwind_RaiseException (&header->exc.unwindHeader);
// Some sort of unwinding error. Note that terminate is a handler.
__cxa_begin_catch (&header->exc.unwindHeader);
DEBUG_LOG("objc_exception_throw: _Unwind_RaiseException returned 0x%x for exception 0x%x\n", err, *exc);
if (_URC_END_OF_STACK == err && 0 != _objc_unexpected_exception)
{
DEBUG_LOG("Invoking _objc_unexpected_exception\n");
_objc_unexpected_exception(object);
}
DEBUG_LOG("Throw returned %d\n",(int) err);
abort();
} |
Yes, though I'm not sure you need the |
1814701
to
4af127a
Compare
@davidchisnall Here's another attempt. It comes at the expense of having to declare The build on the clang64 fails because that's using libc++ instead of libstdc++, I'll look into that. |
ok - this doesn't work yet on msys/clang64 because libc++ doesn't expose |
Backport two patches from libobjc2: - Fix uncaught exception handling (gnustep/libobjc2#278) - Export forwarders for C++ methods, so that we no longer need to link with the C++ runtime (gnustep/libobjc2#279)
Hi @davidchisnall, would you have the bandwidth to review this and 297 in the near future? I'm fairly confident the gnustep-base unit test will pass if this fix goes in. Alternatively, as a short-term workaround, I could submit these two fixes for the MSYS2 packages while we work our way through the code review here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to break a load of things. If it were guarded behind MinGW checks, I wouldn't mind so much, but we absolutely should not be doing this with the Itanium ABI.
objcxx_eh.cc
Outdated
@@ -515,14 +519,71 @@ static void eh_cleanup(void *exception) | |||
objc_release(*(id*)exception); | |||
} | |||
|
|||
namespace __cxxabiv1 | |||
{ | |||
struct __cxa_exception |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We used to have a definition like this, but we removed it because different C++ runtimes had subtly different variations on it. This is why we have the thing that dynamically probes it for the fields that we need. I'm not sure that reintroducing it is a good idea.
objcxx_eh.cc
Outdated
_Unwind_Exception unwindHeader; | ||
}; | ||
|
||
struct __cxa_refcounted_exception |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not all runtimes have this, and it causes different offsets because of alignment padding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, but MSYS2 really only ships with two runtimes -- libstdc++ and libc++.
objcxx_eh.cc
Outdated
objc_retain(object); | ||
DEBUG_LOG("objc_exception_throw: Throwing 0x%x\n", *exc); | ||
|
||
#ifdef _LIBCPP_VERSION |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is reintroducing tight coupling with C++ runtime implementations, which we had previously removed. It's also totally the wrong check, because you're checking for libc++ but libc++ can be built with libc++abi, libcxxrt, or libsupc++.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This #ifdef
is trying to determine whether you're on the mingw64 or ucrt64 MSYS2 environment (which use libstdc++ and have __cxa_init_primary_exception
) or the clang64 MSYS2 environment, which doesn't have __cxa_init_primary_exception
and for which we revert to the old behavior of using __cxa_throw
.
I've redefined the check as #ifndef __GLIBCXX__
, but not sure if that really addresses your comment.
As for the tight coupling: yes, I know, though we tried the dynamic check for the exception struct size and that didn't work.
Hi @davidchisnall, thanks for the feedback. I'll go through it in detail later. Just to clarify this comment:
All of these changes are guarded by the |
I spent some more time looking at this, but at the moment think we're stuck between a rock and a hard place:
So the options I see are:
|
1697a0b
to
c08f725
Compare
So, we can use the runtime check to determine the exception struct size, so that simplifies things a bit. This is as far as I can take it, I'm afraid. |
a399e91
to
1b753d8
Compare
1b753d8
to
38ebb38
Compare
libobjc2 used Vectored Exception Handlers to catch uncaught exceptions on mingw. This implementation is too greedy, and invokes `_objc_unexpected_exception` for (certain) exceptions which would be handled by the application itself. This patch `_Unwind_RaiseException` to throw exceptions instead of `__cxa_throw`, allowing us to process unhandled exceptions. Upstream PR: gnustep/libobjc2#278
libobjc2 used Vectored Exception Handlers to catch uncaught exceptions on mingw. This implementation is too greedy, and invokes `_objc_unexpected_exception` for (certain) exceptions which would be handled by the application itself. This patch `_Unwind_RaiseException` to throw exceptions instead of `__cxa_throw`, allowing us to process unhandled exceptions. Upstream PR: gnustep/libobjc2#278
@davidchisnall Would you mind having another look at this one? |
I still don’t like the fact that it’s conflating C++ runtimes with standard library implementations or all of the ifdefs. If you can put this code in a separate file that!s built only for MinGW so no one reading the code for less baroque platforms will be confused then it!s probably okay. |
@davidchisnall I moved the MinGW code to its own class, which I briefly considered calling The definitions of the C++ ABI functions and types as well as the family of |
The current implementation uses Vectored Exception Handlers. This implementation is too greedy, and invokes _objc_unexpected_exception for (certain) exceptions which would be handled by the application itself.
The current implementation uses Vectored Exception Handlers. This implementation is too greedy, and invokes
_objc_unexpected_exception
for (certain) exceptions which would be handled by the application itself.