Skip to content
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

Support for libc++abi #152

Merged
merged 1 commit into from
Dec 15, 2020
Merged

Support for libc++abi #152

merged 1 commit into from
Dec 15, 2020

Conversation

ngrewe
Copy link
Member

@ngrewe ngrewe commented Apr 14, 2020

Okay, this is my attempt to fix #142 — I'm still not entirely happy with it, but I think this is now at least in a state where it doesn't break anything else and feels reasonably robust (all things considered). So now seems like a good time as any to solicit feedback.

The first part of this is detection of whether the compiler will use a libc++ flavoured EH ABI or the GNU one. It turns out that this is quite difficult and this PR unfortunately relies on secondary signals to determine which one to use: In general, we will assume that being able to compile typeinfo_test.cc with a certain C++ ABI runtime library is a good indication for the ABI: if either libcxxrt and libsupc++ work, this means that we should use the GNU ABI, if libc++abi works, we should use the LLVM one.
But that leaves the case where we need to link the standard library in order to get the runtime bits and pieces. To handle this case, we try to compile the typeinfo_non_gnu.cc test programme, which is set up in a way that it will fail with libstdc++, in which case we assume that we working in a libc++ environment (MSVC is already dealt with beforehand). We also have to keep in mind that there are environments (e.g. FreeBSD), where we use libc++ and still use the GNU ABI, which happens to work because we prefer the result from checking for the C++ runtime libraries.

Using these CMake tests, we set up the CXX_ABI_IS_GNU macro, which changes a couple of things if it happens to be 0:

  • The exception class for C++ exceptions is set to CLNGC++ (as opposed to GNUC++ for the GNU ABI)
  • Determination of whether an exception handler catches a certain exceptions goes via the can_catch member as per the LLVM ABI.
  • The layout of the std::typeinfo class is changed quite drastically. I consider this the most ugly part of the entire PR: libc++abi made a few implementation decisions that make it hard for libobjc2 to support it. Firstly, it intersperses a __shim_type_info class between std::typeinfo and the concrete type info implementations. This is the class defining the can_catch() member (the GNU equivalent, __do_catch(), is declared directly on std::typeinfo). Secondly, that class doesn't have a constructor, presumably because the implementors thought that the compiler would always emit the different typeinfo instances directly. For our flavour of ObjC++, this is true only when catching a specific class. For __objc_id_type_info, we call the base class constructor to set up the instance. To support this, the std::typeinfo declaration for libc++ includes various bits and pieces that allow us to use its initialiser and still keep the vtable in a shape that it can be used as if inheriting from __shim_type_info

Since that's comparatively hacky, an equally important part of this is the extension of the CI pipeline, which now also builds and tests the libc++ support on Linux (I originally developed and tested this using a OpenBSD VM, but it doesn't seem that there are any free CI services allowing you to build on OpenBSD). That necessitated moving the builds into containers because otherwise there would be cross-pollution between the builds 🙄. Also, since the Cirrus CI build seems to have stopped working with FreeBSD 12.0, I took the liberty of bumping that to 12.1.

Please let me know what you think, I envisage that this will need a few more iterations before it is ready to merge…

Thanks!

Niels

@ngrewe ngrewe requested review from triplef and davidchisnall April 14, 2020 13:25
triplef added a commit to gnustep/tools-android that referenced this pull request Apr 15, 2020
Copy link
Member

@triplef triplef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @ngrewe for putting this into a PR! We’ve been using this updated branch for a couple days now on various machines for Android and we haven’t seen any issues.

Unfortunately I don’t feel qualified to really review this, as I know next to nothing about the C++ ABI. I did look through the changes and didn’t see anything standing out to me (just a couple small spelling/grammar issues in the comments – let me know if I should report these), so in that way and as it’s working well for us: lgtm.

@ngrewe
Copy link
Member Author

ngrewe commented Apr 21, 2020

just a couple small spelling/grammar issues in the comments – let me know if I should report these

Yes please! Thanks for having a look at this...

CMakeLists.txt Outdated Show resolved Hide resolved
CMakeLists.txt Outdated Show resolved Hide resolved
type_info_llvm.h Outdated Show resolved Hide resolved
@ngrewe
Copy link
Member Author

ngrewe commented Apr 25, 2020

Oh. I realised just now that Github foobared my last comment quite rigorously 😄 – anyways, thanks again for the just as rigorous copy editing!

if (CXX_ABI_IS_GNU)
add_definitions(-DCXX_ABI_IS_GNU=1)
else ()
add_definitions(-DCXX_ABI_IS_GNU=0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please can you make this path something like CXX_ABI_IS_LLVM? I find this naming slightly confusing because we already support MSVC EH and GNU-flavoured Itanium EH, so having not-GNU mean LLVM-flavoured-Itanium is a bit odd.

@@ -3,24 +3,43 @@ jobs:
displayName: Ubuntu-16.04
pool:
vmImage: ubuntu-16.04
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I bumped this to 18.04, so may have caused some merge conflicts for you here. Sorry!

@davidchisnall
Copy link
Member

Thank you very much for working on this.

Looking at the vtable layouts, it appears that we ought to be able to just use the GNU one, as long as we don't depend on any symbols. The GNU vtable contains two functions to check if a type is a pointer or function, these are noop1 and noop2 in the LLVM layout and so we can provide the GNU definitions.

The third function in both is __do_catch or can_catch. The first two arguments are the same here, and we don't use the third, so we should be able to use the LLVM signature here.

The fourth function is only present in the GNU definition, so there's no conflict if we provide it.

Can we get away with defining our own std::type_info class that has this layout and fudging it, or do any of the runtimes rely on dynamic_casting std::type_info subclasses?

It would also be nice, if possible, to make this a dynamic check. I am working on doing an initialisation pass that throws a C++ exception through a frame with a personality function that inspects the C++ exception. It would be nice to use the same (or similar) logic to handle this.

@davidchisnall
Copy link
Member

#156 has the initial draft of the auto-detection.

@davidchisnall
Copy link
Member

FYI: We are now correctly detecting the exception class, but libc++abi support will still require having the correct vtable layout for the catch helpers.

@davidchisnall
Copy link
Member

With the latest set of EH fixes, I removed the dependency on a specific vtable layout in std::type_info, which should make this a bit easier.

I believe that the libc++abi version, because it doesn't call __is_pointer_p, may need __do_catch to do the double dereference to get the right pointer. If so, that can be guarded by a check on the C++ exception class that the dynamic detection found and run only if we picked up "CLNGC++\0".

@triplef
Copy link
Member

triplef commented Jun 4, 2020

It would be great to see this updated with the latest changes from David. @ngrewe is there anything I can do to help with this?

@triplef
Copy link
Member

triplef commented Jul 3, 2020

I took a stab at merging master into the libc++ branch:
https://github.com/triplef/libobjc2/tree/libc%2B%2B-merge

Unfortunately it crashes in both on ARM and x86 in __do_catch() when throwing an exception in an ObjC++ file, so I am probably missing some modifications to make it work with the latest EH changes in master:

  * frame #0: 0xf0c14a00 libart.so`art_sigsegv_fault
    frame #1: 0xf0c14fa4 libart.so`art::FaultManager::HandleFault(int, siginfo*, void*) + 372
    frame #2: 0xf0c14ccb libart.so`art::art_fault_handler(int, siginfo*, void*) (.llvm.17267581585005653364) + 43
    frame #3: 0x5aca0fe1 app_process32`___lldb_unnamed_symbol25$$app_process32 + 625
    frame #4: 0xf4bd5180 libc.so`___lldb_unnamed_symbol2$$libc.so + 1
    frame #5: 0xc7b16452 libobjc.so`objc_resolve_class(cls=0xc83ae870) at class_table.c:200
    frame #6: 0xc7b16b9c libobjc.so`class_getSuperclass(cls=0xc83ae870) at class_table.c:538
    frame #7: 0xc7b297b1 libobjc.so`gnustep::libobjc::__objc_class_type_info::__do_catch(std::type_info const*, void**, unsigned int) const [inlined] isKindOfClass(thrown=0xc83ae870, type=0xc824da68) at objcxx_eh.cc:183
    frame #8: 0xc7b2979c libobjc.so`gnustep::libobjc::__objc_class_type_info::__do_catch(this=0xc83ae28c, thrownType=0xc7b5bd20, obj=0xff9f7274, outer=<unavailable>) const at objcxx_eh.cc:314
    frame #9: 0xc7b29867 libobjc.so`gnustep::libobjc::__objc_class_type_info::can_catch(this=0xc83ae28c, thrownType=0xc7b5bd20, obj=0xff9f7274) const at objcxx_eh.cc:334
    frame #10: 0xc7b2b187 libobjc.so`__cxxabiv1::scan_eh_tab(results=0xff9f72c0, actions=_UA_SEARCH_PHASE, native_exception=<unavailable>, unwind_exception=0xea5f9910, context=0xff9f7400)::scan_results&, _Unwind_Action, bool, _Unwind_Exception*, _Unwind_Context*) at cxa_personality.cpp:758
    frame #11: 0xc7b2ad0f libobjc.so`::__gxx_personality_v0(version=1, actions=_UA_SEARCH_PHASE, exceptionClass=4849336966747728640, unwind_exception=0xea5f9910, context=0xff9f7400) at cxa_personality.cpp:969
    frame #12: 0xc7b22124 libobjc.so`__gnustep_objcxx_personality_v0(version=1, actions=1, exceptionClass=<unavailable>, exceptionObject=0xea5f9910, context=0xff9f7400) at eh_personality.c:575
    frame #13: 0xc7b45e25 libobjc.so`_Unwind_RaiseException(exc=0xc73ae390) at unwind.inc:113
    frame #14: 0xc7b21abc libobjc.so`objc_exception_throw(object=0xc83ae870) at eh_personality.c:239
    frame #15: 0xc83a5f78 libnative-lib.so`::-[TestCXX throwException](self=0xc73c61a4, _cmd="Y\x1f") at ObjCXX.mm:111

Unfortunately I’m mostly flying in the dark here as I don’t really understand the changes, but if it helps I’d be happy to work on this further if someone can point me in the right direction.

FWIW I also had to do add some modifications to get the compilation of eh_trampoline.cc working in a cross-compilation environment with the Android CMake toolchain file, which I think sets CMAKE_CXX_FLAGS as a string instead of a list – I am not sure if this is portable as-is: triplef@2af2a6b

We’re currently still using the libc++ branch for the Android toolchain (which works great btw.), but it’d love to be able to switch to an official build with libc++abi support.

@davidchisnall
Copy link
Member

@ngrewe, any chance you'll have time to look at merging this with the new infrastructure for better supporting different C++ standard library implementations? We now create a function with a custom personality function and throw a known C++ exception through it the first time a C++ exception is thrown, so we can figure out the layout of the C++ exception objects.

@ngrewe
Copy link
Member Author

ngrewe commented Aug 23, 2020

Thanks for pinging me! Things had gotten terribly busy in my neck of the woods and this slipped off the radar. I'll pick it back up sometime this week.

@triplef
Copy link
Member

triplef commented Aug 23, 2020

Thanks @ngrewe! Please let me know if there’s anything I can test or help with.

@ngrewe
Copy link
Member Author

ngrewe commented Aug 31, 2020

I believe that the libc++abi version, because it doesn't call __is_pointer_p, may need __do_catch to do the double dereference to get the right pointer.

Thanks so much, @davidchisnall… that was spot on and probably saved me a lot of poking at random things! I think basic detection at runtime is now working fine. I'm now stripping out the old compile-time detection bits from this branch.

@triplef
Copy link
Member

triplef commented Sep 1, 2020

Thank you very much @ngrewe! I’m traveling this week and next, but I’m looking forward to try this out as soon as I return.

@ngrewe
Copy link
Member Author

ngrewe commented Sep 13, 2020

I've gotten this PR to be delightfully minimal now. Unfortunately, this is now also experiencing the problem with ceilf being undefined, as described in #181 😕

@davidchisnall
Copy link
Member

We probably need to unconditionally link libm if it exists.

objcxx_eh.cc Show resolved Hide resolved
@@ -49,6 +49,9 @@ class type_info2 : public std::type_info
virtual bool __do_catch(const type_info *thrown_type,
void **thrown_object,
unsigned outer) const { return true; }
virtual bool __do_upcast(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually need this test anymore? I think we're now detecting the C++ runtime at run time, so we should work with any C++ runtime.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now work with every runtime, but we still need this test to find out whether there is a usable runtime or if we need to link the full stdlib.

Copy link
Member Author

@ngrewe ngrewe Sep 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change here is needed to prevent a dependency on symbols from the GNU runtime, btw.

@davidchisnall
Copy link
Member

It looks as if we're linking libc++, not libc++abi. This means that we get libc++'s definition of std::throw_length_error but this depends on ceilf. Maybe we can detect libc++ and add libm to link commands when it's found?

@ngrewe
Copy link
Member Author

ngrewe commented Sep 14, 2020

It looks as if we're linking libc++, not libc++abi. This means that we get libc++'s definition of std::throw_length_error but this depends on ceilf.

It was actually linked unconditionally in the CI configuration (an oversight on my part), and we also had some cross-talk between the two builds so that the libstdc++ build would pick up libc++abi and try to link it with the GNU C++ ABI. Both should now be fixed (the latter by only trying to libc++abi if libc++ is among the CMAKE_CXX_IMPLICIT_LINK_LIBARARIES)

Maybe we can detect libc++ and add libm to link commands when it's found?

I've changed the build script to do that.

@triplef
Copy link
Member

triplef commented Sep 15, 2020

After getting cross-compilation to work again (#183, not specific to this branch), I am now getting the following errors when building for armeabi-v7a (arm64 and x86/x64 work fine, I’ll test these in a bit):

unwind-arm.h:140: error: undefined reference to '_Unwind_VRS_Get(_Unwind_Context*, _Unwind_VRS_RegClass, unsigned int, _Unwind_VRS_DataRepresentation, void*)'
unwind-arm.h:145: error: undefined reference to '_Unwind_VRS_Set(_Unwind_Context*, _Unwind_VRS_RegClass, unsigned int, _Unwind_VRS_DataRepresentation, void*)'
objcxx_eh.cc:113: error: undefined reference to '__gnu_unwind_frame(_Unwind_Exception*, _Unwind_Context*)'

These seem to be defined in libunwind.a for armeabi-v7a. Do we maybe need to link that manually?

@triplef
Copy link
Member

triplef commented Sep 15, 2020

I tried explicitly linking libunwind, but I’m still getting the same linker error. Any thoughts?

~/Library/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang --target=armv7-none-linux-androideabi21 --gcc-toolchain=~/Library/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64 --sysroot=~/Library/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a -mthumb -Wformat -Werror=format-security  -Xclang -fexceptions -Xclang -fobjc-exceptions -O2 -g -DNDEBUG  -pthread -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libatomic.a -Wl,--build-id -Wl,--fatal-warnings -Wl,--exclude-libs,libunwind.a -Wl,--no-undefined -Qunused-arguments  -shared -Wl,-soname,libobjc.so -o libobjc.so CMakeFiles/objc.dir/alias_table.c.o CMakeFiles/objc.dir/block_to_imp.c.o CMakeFiles/objc.dir/caps.c.o CMakeFiles/objc.dir/category_loader.c.o CMakeFiles/objc.dir/class_table.c.o CMakeFiles/objc.dir/dtable.c.o CMakeFiles/objc.dir/encoding2.c.o CMakeFiles/objc.dir/hooks.c.o CMakeFiles/objc.dir/ivar.c.o CMakeFiles/objc.dir/loader.c.o CMakeFiles/objc.dir/mutation.m.o CMakeFiles/objc.dir/protocol.c.o CMakeFiles/objc.dir/runtime.c.o CMakeFiles/objc.dir/sarray2.c.o CMakeFiles/objc.dir/selector_table.c.o CMakeFiles/objc.dir/sendmsg2.c.o CMakeFiles/objc.dir/eh_personality.c.o CMakeFiles/objc.dir/block_trampolines.S.o CMakeFiles/objc.dir/objc_msgSend.S.o CMakeFiles/objc.dir/eh_trampoline.s.o CMakeFiles/objc.dir/NSBlocks.m.o CMakeFiles/objc.dir/Protocol2.m.o CMakeFiles/objc.dir/associate.m.o CMakeFiles/objc.dir/blocks_runtime.m.o CMakeFiles/objc.dir/properties.m.o CMakeFiles/objc.dir/gc_none.c.o CMakeFiles/objc.dir/arc.mm.o CMakeFiles/objc.dir/objcxx_eh.cc.o  -Wl,-Bstatic -lc++abi -lm -lunwind -Wl,-Bdynamic -latomic -lm 
unwind-arm.h:140: error: undefined reference to '_Unwind_VRS_Get(_Unwind_Context*, _Unwind_VRS_RegClass, unsigned int, _Unwind_VRS_DataRepresentation, void*)'
unwind-arm.h:145: error: undefined reference to '_Unwind_VRS_Set(_Unwind_Context*, _Unwind_VRS_RegClass, unsigned int, _Unwind_VRS_DataRepresentation, void*)'
objcxx_eh.cc:113: error: undefined reference to '__gnu_unwind_frame(_Unwind_Exception*, _Unwind_Context*)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

@triplef
Copy link
Member

triplef commented Sep 15, 2020

I tried this in the Android x86 emulator. Throwing and catching an ObjC++ exception works fine the first time, but the second time crashes with this backtrace:

* thread #1, name = 'helloobjectivec', stop reason = signal SIGABRT
  * frame #0: 0xf32efad9 [vdso]`__kernel_vsyscall + 9
    frame #1: 0xef2d7329 libc.so`syscall + 41
    frame #2: 0xef2f2652 libc.so`abort + 194
    frame #3: 0xef2f2b89 libc.so`__assert2 + 57
    frame #4: 0xc5328db5 libobjc.so`::abort_message(format="terminating") at abort_message.cpp:72
    frame #5: 0xc5328e59 libobjc.so`demangling_terminate_handler() at cxa_default_handlers.cpp:77
    frame #6: 0xc532582a libobjc.so`std::__terminate(func=(libobjc.so`demangling_terminate_handler() at cxa_default_handlers.cpp:27))()) at cxa_handlers.cpp:59
    frame #7: 0xc5325772 libobjc.so`std::terminate() at cxa_handlers.cpp:0
    frame #8: 0xc5324f2e libobjc.so`::__cxa_rethrow() at cxa_exception.cpp:0
    frame #9: 0xc531c6f4 libobjc.so`objc_exception_throw(object=0xc5e16870) at eh_personality.c:212
    frame #10: 0xc5e0df78 libnative-lib.so`::-[TestCXX throwException](self=0xc4efe3a4, _cmd="G\x1f") at ObjCXX.mm:111

This is the log output I am getting:

// first exception:
Throwing 0xc5e16870, in flight exception: 0xc4f70a84
    Exception caught by C++: 0
// second exception:
Throwing 0xc5e16870, in flight exception: 0xc5e16870
Exception caught by C++: 1
    terminating
    /Volumes/Android/buildbot/src/android/ndk-release-r21/external/libcxx/../../external/libcxxabi/src/abort_message.cpp:72: abort_message: assertion "terminating" failed

@ngrewe
Copy link
Member Author

ngrewe commented Sep 15, 2020

Throwing and catching an ObjC++ exception works fine the first time, but the second time crashes with this backtrace.

Interesting. Is this two separate exceptions being thrown? It looks like we are entering the code path where we let C++ rethrow the exception instead of throwing it ourselves. That fails because the runtime is not aware of any enclosing exception handler. Can you make available the reproducer?

@ngrewe
Copy link
Member Author

ngrewe commented Sep 18, 2020

We need it when we link libc++

So just to confirm: There are versions of libc++ (not libc++abi) in the wild which are not already linked against libm? I've been testing mostly with the variant from the llvm repositories for Debian/Ubuntu, and that has that right already:

$ ldd /lib/x86_64-linux-gnu/libc++.so.1.0
	linux-vdso.so.1 (0x00007ffc78986000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f61d1d7a000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f61d1b88000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f61d1a39000)
	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f61d1a2e000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f61d1a13000)
	libc++abi.so.1 => /lib/x86_64-linux-gnu/libc++abi.so.1 (0x00007f61d19dd000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f61d1e6c000)

I think the correct thing to do is link it unconditionally if it exists, irrespective of the C++ runtime.

That would make for a simpler build script, and I'm all for that.

@ngrewe
Copy link
Member Author

ngrewe commented Sep 18, 2020

About the __cxa_rethrow-from-objc_exception_throw problem surfaced by the test in fc08e09: I think we can decide wether we need to rethrow by looking at the C++ runtime's exception stack (__cxa_eh_globals.caughtExceptions) instead, or am I missing something here?

@davidchisnall
Copy link
Member

So just to confirm: There are versions of libc++ (not libc++abi) in the wild which are not already linked against libm? I've been testing mostly with the variant from the llvm repositories for Debian/Ubuntu, and that has that right already:

I think that's the only way that we'd end up with teh build failures that we were seeing.

About the __cxa_rethrow-from-objc_exception_throw problem surfaced by the test in fc08e09: I think we can decide wether we need to rethrow by looking at the C++ runtime's exception stack (__cxa_eh_globals.caughtExceptions) instead, or am I missing something here?

Yes, I think so.

triplef added a commit to gnustep/tools-android that referenced this pull request Oct 12, 2020
@triplef
Copy link
Member

triplef commented Nov 19, 2020

With #183 out of the way (and after merging master) I’m able to build this for Android on all architectures except armeabi-v7a, where unfortunately I still get the following linker error:

[ 13%] Linking C shared library libobjc.so
clang --target=armv7-none-linux-androideabi23 -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a -mthumb -Wformat -Werror=format-security  -Xclang -fexceptions -Xclang -fobjc-exceptions -O2 -g -DNDEBUG  -pthread -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libatomic.a -Wl,--build-id -Wl,--fatal-warnings -Wl,--exclude-libs,libunwind.a -Wl,--no-undefined -Qunused-arguments  -shared -Wl,-soname,libobjc.so -o libobjc.so alias_table.c.o block_to_imp.c.o caps.c.o category_loader.c.o class_table.c.o dtable.c.o encoding2.c.o hooks.c.o ivar.c.o loader.c.o mutation.m.o protocol.c.o runtime.c.o sarray2.c.o selector_table.c.o sendmsg2.c.o eh_personality.c.o block_trampolines.S.o objc_msgSend.S.o eh_trampoline.s.o NSBlocks.m.o Protocol2.m.o associate.m.o blocks_runtime.m.o properties.m.o gc_none.c.o arc.mm.o objcxx_eh.cc.o  -Wl,-Bstatic -lc++abi -lm -Wl,-Bdynamic -latomic -lm 
unwind-arm.h:140: error: undefined reference to '_Unwind_VRS_Get(_Unwind_Context*, _Unwind_VRS_RegClass, unsigned int, _Unwind_VRS_DataRepresentation, void*)'
unwind-arm.h:145: error: undefined reference to '_Unwind_VRS_Set(_Unwind_Context*, _Unwind_VRS_RegClass, unsigned int, _Unwind_VRS_DataRepresentation, void*)'
objcxx_eh.cc:113: error: undefined reference to '__gnu_unwind_frame(_Unwind_Exception*, _Unwind_Context*)'

These symbols are defined in libunwind.a, but I’ve tried a bunch of things (including explicitly adding -lunwind) and cannot figure out how to get this working. I compared the linker invocations between the last version that was still working for me (d521f61) and this, and there are only two changes:

  • libm is linked explicitly. Removing this does not make any difference.
  • eh_trampoline.s.o is linked instead of libstdcxx_current_primary_exception.cc.o – but eh_trampoline does not seem to be using the above symbols.

I also looked through all the changes since that last working release but don’t see what change could be causing this. Any ideas?

@davidchisnall
Copy link
Member

The fact that these symbols have parameter types in a linker message suggests that they are C++ mangled names, but they should be C symbols. I think we're probably missing an extern "C" somewhere.

@triplef
Copy link
Member

triplef commented Nov 19, 2020

You’re a magician! 😀 This patch fixes it:

diff --git a/unwind-arm.h b/unwind-arm.h
index 7e2038b..724624e 100644
--- a/unwind-arm.h
+++ b/unwind-arm.h
@@ -1,5 +1,9 @@
 #include <stdint.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /**
  * ARM-specific unwind definitions.  These are taken from the ARM EHABI
  * specification.
@@ -207,3 +211,7 @@ _Unwind_Reason_Code name(_Unwind_State state,\
   (dst)->barrier_cache = (src)->barrier_cache; \
   (dst)->cleanup_cache = (src)->cleanup_cache; \
   (dst)->pr_cache = (src)->pr_cache;
+
+#ifdef __cplusplus
+}
+#endif

Is it ok if I merge master into this branch and add this change, or should I leave that to @ngrewe?

@davidchisnall
Copy link
Member

Can you rebase the branch, rather than merge from master, so we have vaguely comprehensible history?

@triplef
Copy link
Member

triplef commented Nov 19, 2020

I tried rebasing, but I’m running into multiple conflicts as the changes from this branch are interleaved with changes in master.

Given that the current changes of this branch over master are pretty compact, the most straightforward way might be to squash all changes, but I’d like to leave that to @ngrewe or at least get his consent as we’d loose all history of the changes in this branch.

@ngrewe
Copy link
Member Author

ngrewe commented Nov 19, 2020

I think I'd even prefer squashing prior to merging this...

@davidchisnall
Copy link
Member

It definitely wants a squash at some point before merging, but it also needs updating for @triplef's fix and a for CI to run.

@triplef
Copy link
Member

triplef commented Nov 23, 2020

@ngrewe I’d be happy to squash and rebase this branch (keeping you as the author) if that helps, just let me know.

I could also pull out the added ObjCXXEHInteropTwice test into its own PR to fix later as to unblock merging this.

Apart from that, I think the only open issue is the following:

I think the libm detection logic is not quite right. We need it when we link libc++ (possibly indirectly, via an application linking libc++). I think the correct thing to do is link it unconditionally if it exists, irrespective of the C++ runtime.

Does the following patch look ok to you?

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1d2342a..16928b7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -295,10 +295,6 @@ if (ENABLE_OBJCXX)
 			list (FIND CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "c++" _libcxx_index)
 			if (${_libcxx_index} GREATER -1)
 				test_cxx(c++abi false)
-				if (CXX_RUNTIME)
-					# When linking libc++abi, try to find libm for linking
-					find_library(M_LIBRARY m)
-				endif()
 			endif()
 		endif (NOT CXX_RUNTIME)
 
@@ -327,6 +323,9 @@ if (ENABLE_OBJCXX)
 			MAIN_DEPENDENCY eh_trampoline.cc)
 		list(APPEND libobjc_ASM_SRCS eh_trampoline.s)
 		list(APPEND libobjc_CXX_SRCS objcxx_eh.cc)
+
+		# Find libm for linking, as some versions of libc++ don't link against it
+		find_library(M_LIBRARY m)
 	endif ()
 endif (ENABLE_OBJCXX)

@davidchisnall
Copy link
Member

Looks good to me, thanks!

@davidchisnall
Copy link
Member

It would also be good to add something to ANNOUNCE about libc++abi support.

Extends C++ interop with autodetected support for libc++abi in addition to the existing support for libcxxrt and libsubc++.

Fixes #142.
triplef pushed a commit that referenced this pull request Dec 15, 2020
…imes sequentially.

This originally came up as an issue with libc++abi support (#152), but is not specific to that ABI.
@triplef
Copy link
Member

triplef commented Dec 15, 2020

I have squashed the changes, minus the failing test for throwing the same exception twice which was extracted into #188.

As for adding this to ANNOUNCE: should it be added to both ANNOUNCE and ANNOUNCE.2.1 or just the former? If it’s ok I would just change the first point of the highlights as follows:

  • Numerous improvements to the Objective-C++ exception interoperation code.
    The runtime now dynamically detects whether the libcxxrt, libsupc++, or
    libc++abi variant of the Itanium C++ Exception ABI is being used.

@davidchisnall
Copy link
Member

ANNOUNCE is the WIP announcement for the next release. I then copy this to the versioned version when I do a release. We shouldn't modify the versioned ones after a release. Happy to see this merged soon - I'll do it later today if you don't push an update to ANNOUNCE before then.

@davidchisnall
Copy link
Member

Sorry, I misunderstood your question. ANNOUNCE should have been reset to a placeholder after 2.1, but it wasn't.

@davidchisnall davidchisnall merged commit 014f386 into master Dec 15, 2020
@davidchisnall davidchisnall deleted the libc++ branch December 15, 2020 11:31
@triplef
Copy link
Member

triplef commented Dec 15, 2020

Most excellent, thank you both!

davidchisnall pushed a commit that referenced this pull request Dec 17, 2020
…imes sequentially.

This originally came up as an issue with libc++abi support (#152), but is not specific to that ABI.
davidchisnall pushed a commit that referenced this pull request Dec 17, 2020
As discussed in #152, use the function defined in the Itanium C++ ABI to
check whether the thrown exception is the current caught C++ exception
and needs rethrowing via `__cxa_rethrow()`.
triplef pushed a commit that referenced this pull request Dec 23, 2021
…imes sequentially.

This originally came up as an issue with libc++abi support (#152), but is not specific to that ABI.
triplef pushed a commit that referenced this pull request Dec 23, 2021
As discussed in #152, use the function defined in the Itanium C++ ABI to
check whether the thrown exception is the current caught C++ exception
and needs rethrowing via `__cxa_rethrow()`.
davidchisnall pushed a commit that referenced this pull request Dec 24, 2021
…imes sequentially. (#188)

This originally came up as an issue with libc++abi support (#152), but is not specific to that ABI.

* Use the C++ runtime to check for uncaught C++ exceptions.

As discussed in #152, use the function defined in the Itanium C++ ABI to
check whether the thrown exception is the current caught C++ exception
and needs rethrowing via `__cxa_rethrow()`.

Co-authored-by: Niels Grewe <grewe@ocean-insights.com>
Co-authored-by: David Chisnall <gnustep@theravensnest.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

C++ EH interop broken with libc++abi
3 participants