Open triplef opened 9 months ago
This abort is at the end of the throw function. The unwind library is returning from the unwind function, which happens only if unwinding fails. For some reason, it looks as if it is not reporting the end of the stack as the reason. Can you see what the value of err is? If you define DEBUG_EXCEPTIONS At the top of the file, it will log it (and a load of other info) for you.
These are the logs I get:
Exception caught by C++: 0
Throwing 0x5555558ee0
Throw returned -1115110992
(I commented out the log "Throwing %p, in flight exception: %p" because it doesn’t build as td->lastThrownObject
doesn’t exist.)
Sometimes it also returns -1113013840
, but seemingly always either that or more often -1115110992
.
Hmm, that's deeply strange. That looks like _Unwind_RaiseException
is returning something that isn't a valid enum value, and possibly not even a valid integer. I've run this on FreeBSD/AArch64 (which uses the LLVM unwind library), and it passes.
Does Android use the GNU unwinder? Can you look in _Unwind_RaiseException
and see if you can see what it thinks it's returning?
Interestingly when running without the debugger the err value seems to be random.
I’m not sure about which unwinder Android uses, and I haven’t been able to locate the sources for _Unwind_RaiseException
so far. I’d also be interested to know whether the failure is the same using cross-builds like on the CI targets. If so maybe it’s easier to debug this there?
Unfortunately I don’t have time to dig into this deeper right now, but I wanted to at least document this issue here.
Can you look in _Unwind_RaiseException and see if you can see what it thinks it's returning?
The error originates from the libgcc implementation of _Unwind_RaiseException
and the generated asm code. Somehow all registers from the start of _Unwind_RaiseException
are reloaded before returning _URC_END_OF_STACK in unwind.inc:108. As a result the first parameter passed gets returned.
Note that uw_frame_state_for (&cur_context, &fs);
returns _URC_END_OF_STACK.
I just requested an account creation for GCC Bugzilla.
Current position right after if (code == _URC_END_OF_STACK)
.
The return value of uw_frame_state_for
is 5 (_URC_END_OF_STACK).
if (code == _URC_END_OF_STACK)
/* Hit end of stack with no handler found. */
return _URC_END_OF_STACK;
0xfffff7f27894 <+404>: b 0xfffff7f277cc ; <+204> at unwind.inc:141:1
(lldb) register read
General Purpose Registers:
x0 = 0x0000000000000005
x1 = 0x0000000000000001
x2 = 0x0000000000000005
x3 = 0x0000aaaaaaafa790
x4 = 0x0000000000000000
x5 = 0x0000ffffffffe160
x6 = 0xfffffffffffffff8
x7 = 0x0000000000000004
x8 = 0x0000000000000001
x9 = 0x0000fffff7f402a8
x10 = 0x0000000000000000
x11 = 0x0000fffff7f40308
x12 = 0x0000fffff7ff77c0
x13 = 0x0000000000000010
x14 = 0x0000000000000000
x15 = 0x0000fffff7bc63c0
x16 = 0x0000fffff7f40000
x17 = 0x0000000000000000
x18 = 0x0000000000000007
x19 = 0x0000ffffffffe610
x20 = 0x0000aaaaaaafa790
x21 = 0x0000ffffffffe9d0
x22 = 0x0000ffffffffe250
x23 = 0x0000ffffffffefc8
x24 = 0x0000fffff7ffdb90 ld-linux-aarch64.so.1`_rtld_global_ro
x25 = 0x0000000000000000
x26 = 0x0000fffff7ffe008 _rtld_global
x27 = 0x0000aaaaaaabfda0 UnexpectedExceptionDebug`__do_global_dtors_aux_fini_array_entry
x28 = 0x0000000000000000
fp = 0x0000ffffffffe190
lr = 0x0000fffff7f277ac libgcc_s.so.1`_Unwind_RaiseException + 172 at unwind.inc:104:14
sp = 0x0000ffffffffe190
pc = 0x0000fffff7f277d0 libgcc_s.so.1`_Unwind_RaiseException + 208 at unwind.inc:141:1
cpsr = 0x60201000
(lldb) register read
General Purpose Registers:
x0 = 0x0000aaaaaaafa790
x1 = 0x0000000000000000
x2 = 0x0000000000000058
x3 = 0x0000000000000000
x4 = 0x0000000000000000
x5 = 0x0000ffffffffe160
x6 = 0xfffffffffffffff8
x7 = 0x0000000000000004
x8 = 0x0000000000000001
x9 = 0x0000fffff7f402a8
x10 = 0x0000000000000000
x11 = 0x0000fffff7f40308
x12 = 0x0000fffff7ff77c0
x13 = 0x0000000000000010
x14 = 0x0000000000000000
x15 = 0x0000fffff7bc63c0
x16 = 0x0000fffff7f40000
x17 = 0x0000000000000000
x18 = 0x0000000000000007
x19 = 0x0000ffffffffefb8
x20 = 0x0000000000000001
x21 = 0x0000aaaaaaabfda0 UnexpectedExceptionDebug`__do_global_dtors_aux_fini_array_entry
x22 = 0x0000aaaaaaaa0ae8 UnexpectedExceptionDebug`main at UnexpectedException.m:33
x23 = 0x0000ffffffffefc8
x24 = 0x0000fffff7ffdb90 ld-linux-aarch64.so.1`_rtld_global_ro
x25 = 0x0000000000000000
x26 = 0x0000fffff7ffe008 _rtld_global
x27 = 0x0000aaaaaaabfda0 UnexpectedExceptionDebug`__do_global_dtors_aux_fini_array_entry
x28 = 0x0000000000000000
fp = 0x0000ffffffffee00
lr = 0x0000fffff7f76d38 libobjc.so.4.6`objc_exception_throw + 520 at eh_personality.c:256:22
sp = 0x0000ffffffffeda0
pc = 0x0000fffff7f27810 libgcc_s.so.1`_Unwind_RaiseException + 272 at unwind.inc:141:1
cpsr = 0x60201000
(lldb)
Thanks for root causing this! Let's leave this open until there's a fix, but until then we can recommend that Arm users use LLVM's unwinder instead of GCC's.
How does one choose between the LLVM vs. GCC unwinder, and is the GCC one also ever used when building with Clang?
I’m a bit confused about this issue because we don’t see issues with exception handling on Android in our app (built with Clang and the NDK toolchain) and the exception hook is working fine too, but I was originally able to reproduce this with the test using the NDK toolchain.
How does one choose between the LLVM vs. GCC unwinder, and is the GCC one also ever used when building with Clang?
Here is an example:
clang-18 test.c -o test -rtlib=compiler-rt --unwindlib=libunwind -fuse-ld=lld-18
but I was originally able to reproduce this with the test using the NDK toolchain
Yep thats weird. Do you know the NDK version you ran the test on?
I think Android uses the LLVM unwinder. It's typically integrated in the C support files, but precisely where depends on the platform. FreeBSD uses the LLVM one as well, which is why I didn't see these issues.
It's also not always clear which one you're using. On FreeBSD, we ship the LLVM one but with the same name as the GCC one to avoid configure scripts failing.
I'd report this to distros as a bug and tell them that the simple fix is to use the LLVM one instead of the GCC one.
Just tested it with libunwind-18 on Ubuntu 23.10 and it works as expected:
hugo@ubuntu:/tmp$ clang -L/usr/lib/llvm-18/lib test.c -o test -rtlib=compiler-rt --unwindlib=libunwind -fuse-ld=lld -lunwind
hugo@ubuntu:/tmp$ ./test
RaiseException returned 0x5
I'll test it on Android this evening.
Do you know the NDK version you ran the test on?
Unfortunately no. I think the NDK migrated to the LLVM toolchain over the last couple of releases, so it might have been a release that was still using the GCC unwinder.
Can we detect in CMake which unwinder is used and already enable the test when LLVM is used?
Just found this re. unwinder on Android:
The unwinder APIs are exposed from the platform's libc.so starting with API 30 (Android R).
Can we detect in CMake which unwinder is used and already enable the test when LLVM is used?
I would enable it unconditionally. The libgcc patch will probably be backported to gcc 13 and maybe gcc 12.
Just found this re. unwinder on Android:
The unwinder APIs are exposed from the platform's libc.so starting with API 30 (Android R).
Seems like libgcc was removed in NDK r23. Here is the change in clang: https://reviews.llvm.org/D96403
I would enable it unconditionally. The libgcc patch will probably be backported to gcc 13 and maybe gcc 12.
s/libgcc/gcc/. The bug is not in libgcc directly but rather the code that GCC produces has the bug.
I hope to get it backported in time for the GCC 11.5 release but we will see. I will be posting the patch over the weekend and I doubt it will be reviewed until Monday or later.
Just an FYI I have posted the GCC patch: https://gcc.gnu.org/pipermail/gcc-patches/2024-April/650080.html .
Thank you @pinskia!
This test was recently added in #220 and subsequently disabled on ARM (https://github.com/gnustep/libobjc2/commit/226455bd10d8b4a0a090449b5b7ebf1e42daf0a5) due to failing on the cross-build ARM CI targets.
I’m able to reproduce it on an arm64 Android emulator with this result in gdb:
Any idea what might be going on here? Interestingly the exception hook is working fine in our app on Android ARM devices and has been for years.
Steps to reproduce with an Android emulator (on macOS ARM host, NDK paths may vary):