Skip to content

Commit 6ef33e5

Browse files
committed
MSYS2: Export forwarders for __cxa_begin_catch, __cxa_end_catch, __cxa_rethrow and __gxx_personality_seh0
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](msys2/MINGW-packages#18589) 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: - https://learn.microsoft.com/en-us/cpp/build/reference/exports?view=msvc-170 - https://devblogs.microsoft.com/oldnewthing/20060719-24/?p=30473 - https://devblogs.microsoft.com/oldnewthing/20121116-00/?p=6073
1 parent f983cdb commit 6ef33e5

File tree

5 files changed

+45
-10
lines changed

5 files changed

+45
-10
lines changed

.github/workflows/main.yml

+2-3
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,12 @@ jobs:
202202
include:
203203
- msystem: ucrt64
204204
package-prefix: ucrt-x86_64
205-
cmake-flags: LDFLAGS="-fuse-ld=lld -lstdc++ -lgcc_s"
205+
cmake-flags: LDFLAGS="-fuse-ld=lld"
206206
- msystem: mingw64
207207
package-prefix: x86_64
208-
cmake-flags: LDFLAGS="-fuse-ld=lld -lstdc++ -lgcc_s"
208+
cmake-flags: LDFLAGS="-fuse-ld=lld"
209209
- msystem: clang64
210210
package-prefix: clang-x86_64
211-
cmake-flags: LDFLAGS="-lc++"
212211
ctest-flags: -E UnexpectedException*
213212
# Don't abort runners if a single one fails
214213
fail-fast: false

CMakeLists.txt

+24
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ endif ()
2828

2929
INCLUDE (CheckCXXSourceCompiles)
3030
INCLUDE (FetchContent)
31+
INCLUDE (CheckCXXSymbolExists)
3132

3233
set(libobjc_VERSION 4.6)
3334

@@ -130,6 +131,29 @@ else ()
130131
list(APPEND libobjc_C_SRCS eh_personality.c)
131132
endif ()
132133

134+
# Export the __cxa_begin_catch, __cxa_end_catch and __gxx_personality_seh0 symbols from libobjc2
135+
# and forward them to the C runtime
136+
if (MINGW)
137+
check_cxx_symbol_exists(_GLIBCXX_RELEASE "version" HAVE_LIBSTDCXX)
138+
check_cxx_symbol_exists(_LIBCPP_VERSION "version" HAVE_LIBCXX)
139+
140+
if (HAVE_LIBSTDCXX)
141+
find_library(CXX_RUNTIME "stdc++" REQUIRED)
142+
set(CXX_RUNTIME_NAME "libstdc++-6")
143+
elseif(HAVE_LIBCXX)
144+
find_library(CXX_RUNTIME "c++" REQUIRED)
145+
get_filename_component(CXX_RUNTIME_NAME ${CXX_RUNTIME} NAME_WE CACHE)
146+
else ()
147+
message(WARNING "Could not determine the C++ runtime.")
148+
endif ()
149+
150+
if (CXX_RUNTIME)
151+
message(STATUS "Adding forwarders for __cxa_begin_catch, __cxa_end_catch, __cxa_rethrow and __gxx_personality_seh0 to ${CXX_RUNTIME_NAME}")
152+
configure_file(eh_forwards.def.in ${CMAKE_CURRENT_BINARY_DIR}/eh_forwards.def @ONLY)
153+
list(APPEND libobjc_CXX_SRCS ${CMAKE_CURRENT_BINARY_DIR}/eh_forwards.def)
154+
endif()
155+
endif ()
156+
133157
find_package(tsl-robin-map)
134158

135159
if (NOT tsl-robin-map_FOUND)

eh_forwards.def.in

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
EXPORTS
2+
__cxa_begin_catch = @CXX_RUNTIME_NAME@.__cxa_begin_catch
3+
__cxa_end_catch = @CXX_RUNTIME_NAME@.__cxa_end_catch
4+
__cxa_rethrow = @CXX_RUNTIME_NAME@.__cxa_rethrow
5+
__gxx_personality_seh0 = @CXX_RUNTIME_NAME@.__gxx_personality_seh0

objcxx_eh.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ PRIVATE void cxx_throw()
463463
throw x;
464464
}
465465

466+
#ifndef __MINGW32__
466467
/**
467468
* Personality function that wraps the C++ personality and inspects the C++
468469
* exception structure on the way past. This should be used only for the
@@ -490,7 +491,6 @@ BEGIN_PERSONALITY_FUNCTION(test_eh_personality)
490491
* personality function, allowing us to inspect a C++ exception that is in a
491492
* known state.
492493
*/
493-
#ifndef __MINGW32__
494494
extern "C" void test_cxx_eh_implementation()
495495
{
496496
if (done_setup)

objcxx_eh.h

+13-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ extern "C" {
1414
#undef CXA_ALLOCATE_EXCEPTION_SPECIFIER
1515
#define CXA_ALLOCATE_EXCEPTION_SPECIFIER
1616
#endif
17-
__attribute__((weak))
17+
18+
#ifndef __MINGW32__
19+
#define OBJC_WEAK __attribute__((weak))
20+
#else
21+
#define OBJC_WEAK
22+
#endif
23+
OBJC_WEAK
1824
void *__cxa_allocate_exception(size_t thrown_size) CXA_ALLOCATE_EXCEPTION_SPECIFIER;
1925

2026
/**
@@ -23,32 +29,33 @@ void *__cxa_allocate_exception(size_t thrown_size) CXA_ALLOCATE_EXCEPTION_SPECIF
2329
* _Unwind_Exception structure within this structure, and should be passed to
2430
* the C++ personality function.
2531
*/
26-
__attribute__((weak))
32+
OBJC_WEAK
2733
struct _Unwind_Exception *objc_init_cxx_exception(id thrown_exception);
2834
/**
2935
* The GNU C++ exception personality function, provided by libsupc++ (GNU) or
3036
* libcxxrt (PathScale).
3137
*/
32-
__attribute__((weak)) DECLARE_PERSONALITY_FUNCTION(__gxx_personality_v0);
38+
OBJC_WEAK
39+
DECLARE_PERSONALITY_FUNCTION(__gxx_personality_v0);
3340
/**
3441
* Frees an exception object allocated by __cxa_allocate_exception(). Part of
3542
* the Itanium C++ ABI.
3643
*/
37-
__attribute__((weak))
44+
OBJC_WEAK
3845
void __cxa_free_exception(void *thrown_exception);
3946
/**
4047
* Tests whether a C++ exception contains an Objective-C object, and returns if
4148
* if it does. The second argument is a pointer to a boolean value indicating
4249
* whether this is a valid object.
4350
*/
44-
__attribute__((weak))
51+
OBJC_WEAK
4552
void *objc_object_for_cxx_exception(void *thrown_exception, int *isValid);
4653

4754
/**
4855
* Prints the type info associated with an exception. Used only when
4956
* debugging, not compiled in the normal build.
5057
*/
51-
__attribute__((weak))
58+
OBJC_WEAK
5259
void print_type_info(void *thrown_exception);
5360

5461
/**

0 commit comments

Comments
 (0)