-
Notifications
You must be signed in to change notification settings - Fork 253
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
[BUG] Exceptions thrown across shared objects cannot be caught. Unwind symbols reported as LOCAL instead of GLOBAL. #1091
Comments
Do your exception types have key functions? RTTI across shared library boundaries is tricky. https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md#rtti_exceptions-not-working-across-library-boundaries
No, this is very much intentional. If libraries are exporting unwind symbols you can get into a lot of trouble when the unwinder changes (as it has before).
That could also be your problem, and is very much not expected with ndk-build. That alone is a bug. If you can tell us how to reproduce, please file that so I can fix it.
Exceptions and RTTI are actually on by default with CMake, btw, so you can remove this line. It doesn't control visibility of the unwinder (nor does anything else). |
No, our exception types all inherit from std::runtime_error and don't have any virtual functions. The only real reason we derive at all is just to communicate the class of error by what type it is.
Ah, so my intuition was completely backwards! Just so I make sure we're not looking at a red herring, I want to make sure the way I detected the GLOBAL symbols for my main library (libfire.so) wasn't an invalid approach. After building my app I extracted the contents of <ANDROID_STUDIO_PROJECT>/app/build/outputs/apk/debug and ran readelf -sW on the binary in lib/arm64-v8a/ subfolder:
Oddly enough when I perform the readelf commands on any of the other shared object dependencies the libunwind symbols are no longer reported at all like they were before they were packaged into the app. I assume this is because of some symbol hiding / localization / stripping performed during packaging. Ok, so assuming this all seems fine I'll describe my app's ndk-build process. My app's build.gradle file is relatively straight forward as executes a standard externalNativeBuild block with some configurable bits coming from the local.properties file:
My android.mk file:
My Application.mk file:
The ndk build script for my main library (fire.mk) looks like this:
And the ndk build script for including my dependencies looks like this:
Sorry for the giant text dump! If this doesn't illuminate anything I can try and trim it down to a simple app without the dependencies. |
I should also mention that I cannot reproduce this if I use the gnustl_shared standard library building against ndk 16b. |
That's probably the issue then. RTTI relies on typeinfo objects being exactly equal (the same address). The is accomplished by either emitting a single strong definition of the typeinfo with the vtable in the same object as the type's key function, or by emitting weak typeinfos in every object that uses it and relying on them being merged at link/load time. Dynamic linking, especially when its not happening all at once (as is often the case when
This also hints at the above being correct. GNU's implementation of RTTI is not compliant with the ABI spec. It reduces these false negatives at the risk of also enabling false positives.
Yep, that's the correct readelf incantation.
Pretty sure we've solved both issues. The main problem is the lack of key functions, the ndk-build issue is #1092 (but that's probably not causing your problem). |
Thanks for the help, that gives me a lot to investigate! Just to clarify if I'm not using If I am better off using rtti is the suggested resolution to implement virtual destructors for the exception subclasses? |
I think you want to use |
(closing since I'm pretty sure this is solved, if our guess is wrong lmk and we'll reopen) |
Yep. |
K, just adding rtti to my LOCAL_CPP_FEATURES block seems to have resolved the issue (I didn't need to implement the virtual destructors). Thanks for the help! |
Description
I'm hopeful this is just a usage error but I've ran into a strange issue when running my android app where exceptions thrown across the boundary between two shared objects cannot be caught by surrounding try / catch blocks. Any exceptions that are thrown and caught in the same binary behave as expected.
After some googling I surmised this must be due to how the libunwind symbols are built into / referenced by my shared libraries. So to confirm this I ran readelf commands on all my prebuilt libraries and all of them gave output like the following:
readelf -sW libhps_core.so | grep _Unwind
149758: 00000000066096cc 60 FUNC LOCAL DEFAULT 10 _Unwind_SetSpColumn
149759: 0000000006609708 88 FUNC LOCAL DEFAULT 10 _Unwind_GetGR.localalias.0
149768: 000000000660abb4 192 FUNC LOCAL DEFAULT 10 _Unwind_RaiseException_Phase2
149769: 000000000660ac74 236 FUNC LOCAL DEFAULT 10 _Unwind_ForcedUnwind_Phase2
149770: 000000000660ae54 4 FUNC LOCAL DEFAULT 10 _Unwind_DebugHook
149791: 000000000660bab0 1052 FUNC LOCAL DEFAULT 10 _Unwind_IteratePhdrCallback
149906: 0000000006609930 8 FUNC LOCAL DEFAULT 10 _Unwind_GetTextRelBase
150109: 0000000006609708 88 FUNC LOCAL DEFAULT 10 _Unwind_GetGR
150486: 000000000660b2b8 36 FUNC LOCAL DEFAULT 10 _Unwind_DeleteException
150594: 000000000660b1c0 248 FUNC LOCAL DEFAULT 10 _Unwind_Resume_or_Rethrow
151274: 000000000660b2dc 208 FUNC LOCAL DEFAULT 10 _Unwind_Backtrace
151599: 00000000066098cc 8 FUNC LOCAL DEFAULT 10 _Unwind_GetIP
151645: 00000000066098f8 8 FUNC LOCAL DEFAULT 10 _Unwind_GetRegionStart
151651: 0000000006609874 88 FUNC LOCAL DEFAULT 10 _Unwind_SetGR
151943: 000000000660ca48 452 FUNC LOCAL DEFAULT 10 _Unwind_Find_FDE
151949: 00000000066098e8 8 FUNC LOCAL DEFAULT 10 _Unwind_SetIP
151953: 000000000660b0c4 252 FUNC LOCAL DEFAULT 10 _Unwind_Resume
152066: 0000000006609928 8 FUNC LOCAL DEFAULT 10 _Unwind_GetDataRelBase
152189: 00000000066098d4 20 FUNC LOCAL DEFAULT 10 _Unwind_GetIPInfo
152195: 000000000660ae58 376 FUNC LOCAL DEFAULT 10 _Unwind_RaiseException
152506: 00000000066098f0 8 FUNC LOCAL DEFAULT 10 _Unwind_GetLanguageSpecificData
152522: 000000000660afd0 244 FUNC LOCAL DEFAULT 10 _Unwind_ForcedUnwind
152810: 0000000006609900 40 FUNC LOCAL DEFAULT 10 _Unwind_FindEnclosingFunction
152824: 000000000660986c 8 FUNC LOCAL DEFAULT 10 _Unwind_GetCFA
This seems erroneous to me as I would expect these symbols to be GLOBAL. If I run the same command on my main app shared object all the symbols report being GLOBAL as I would expect, however the main application shared object is built via a different process (via ndk-build through Android Studio).
I assume I must be missing something here in my specification to the toolchain file but I don't see it. Any help would be much appreciated.
Environment Details
My prebuilt libraries are all built with CMake 3.14 using the Ninja generator using the following variable definitions:
ANDROID_ABI = "arm64-v8a" (not always, but this was the case I was testing against)
ANDROID_STL = "c++_shared"
ANDROID_TOOLCHAIN = "clang"
ANDROID_NATIVE_API_LEVEL = "16" (we have to support relatively old devices)
ANDROID_CPP_FEATURES = "exceptions" (figured this would force the unwind symbols to be global)
CMAKE_TOOLCHAIN_FILE = "${ANDROID_NDK}/build/cmake/android.toolchain.cmake" (where ANDROID_NDK points to a r18b version of the ndk)
I've been able to reproduce this with both NDK 16b and 18b. I know the prompt says to always try with the newest NDK but as one of my dependencies needs an older NDK I can't use that as my resolution.
If it matters my host os is usually Windows but I can reproduce this issue on Windows 10, macOS Mojave and Ubuntu 18.04 hosts.
Thanks for any help.
The text was updated successfully, but these errors were encountered: