gnustep / libobjc2

Objective-C runtime library intended for use with Clang.
http://www.gnustep.org/
MIT License
434 stars 118 forks source link

MSYS2: Export forwarders for `__cxa_begin_catch`, `__cxa_end_catch`, `__cxa_rethrow` and `__gxx_personality_seh0` #279

Closed qmfrederik closed 7 months ago

qmfrederik commented 7 months ago

libobjc2 uses native C++ exceptions on MinGW. The clang compiler will emit references to __cxa_begin_catch, __cxa_end_catch, __cxa_rethrow and __gxx_personality_seh0 for Objective C code which uses Objective C exceptions.

These symbols are defined in the C++ runtime, not in libobjc2. As a result, merely linking with libobjc2 is not sufficient. Objective C code such as GNUstep must be compiled with the LDFLAGS="-lgcc_s -lstdc++" or LDFLAGS="-lc++", depending on the environment.

This is tedious. Additionally, specifying -lc++ on the msys/clang64 environment causes linker errors:

 Linking library libgnustep-base ...
ld.lld: error: libc++.dll.a(libc++.dll): .idata$4 should not refer to special section 0

A similar error has been observed for other libraries

A solution for this is to define forwarding exports for __cxa_begin_catch, __cxa_end_catch, __cxa_rethrow and __gxx_personality_seh0. This is implemented by adding a eh_forwards.def file to the list of libobjc2 source files, which forwards the symbols to the actual C++ runtime. On MSYS2, the libstdc++ and libc++ runtimes are supported, which covers all MinGW environments: https://www.msys2.org/docs/environments/.

Forwarding exports are discussed here:

qmfrederik commented 7 months ago

@MehdiChinoune - FYI. This is my best attempt at removing the need to set LDFLAGS (other than -fuse-ld=lld) on all platforms, and avoid a linker crash on clang64. One thing which bugs me is that I'd like to get the name of the shared library of the C++ runtime (e.g. libstdc++-6). I can use find_library(CXX_RUNTIME "stdc++" REQUIRED) but this will get me the name of the import lbirary (libstd++). Do you know of a way to get the actual runtime library name in CMake?

@davidchisnall It's a bit clunky, but it does work. The root problem this PR is trying to address is that ObjC code will generate calls to __cxa_begin_catch and friends, but they are not part of libobjc2, they are in the C++ runtime. An alternative is to declare wrapper functions in libobjc2, such as objc_begin_catch, which are just wrappers around the C++ runtime equivalent, and have clang emit calls to the objc_begin_catch instead?

davidchisnall commented 7 months ago

Option 2 might be better for modularity. Unfortunately, LLVM 18 has now branched and so we will have to support both options unless you can get the change in before it ships.

qmfrederik commented 7 months ago

Getting those changes in LLVM 18 while they have already declared release candidates feels like a stretch. What if I amend this PR so that it declares those functions, and we can work to get an update in the next release of LLVM? We'll have to be hybrid for a while, but MSYS2 is a fast-moving rolling release so the forwarders could be deprecated in the next 6 months or so.

I assume the API shape would look like this:

libobjc2 method façade for
objc_begin_catch __cxa_begin_catch
objc_end_catch __cxa_end_catch
objc_exception_rethrow __cxa_rethrow
__gnustep_objcxx_personality_seh0 __gxx_personality_seh0

The rethrow method is named objc_exception_rethrow and not objc_rethrow, in line with Apple's implementation. An alternative name for the personality method would be __objc_personality_v0 or __objc_personality_seh0, which would match the Apple implementation.

qmfrederik commented 7 months ago

Let's do it properly - #280