-
Notifications
You must be signed in to change notification settings - Fork 257
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
std::dynamic_pointer_cast always returns a nullptr #533
Comments
Do you have a test case? |
Hi Dan No, I don't have a test case regarding the dynamic casting, I'll try to build one What about the c++ runtime build error?, is this something you know about? thx |
Not supported. |
Hi Dan This is one of the lines that the compiler throws at me while creating an object file Notice that the compiler is enforcing the no rtti and no exceptions flags even though I added this line in the Application.mk APP_CPPFLAGS += -std=c++11 -stdlib=libc++ -fuse-ld=bfd -nodefaultlibs -frtti -fexceptions [armeabi-v7a] Compile++ thumb: medialibrarycore <= UpdateBestArtworkTokenChangeRequest.cpp Will the last two overwrite the first two? is this a problem with clang? |
Yes, the latter rtti/exception flags override former ones.
Very unlikely given that this has persisted across multiple compiler releases, but not impossible. Can't say for sure without a repro case. |
Hi Dan I managed to build a sample app that triggers the dynamic_pointer_cast issue I mentioned earlier.
Those two methods perform the tests and report a boolean if the test passed or failed
And the casting will be evaluated like this:
Noticed that I'm trying to cast down. The sample app can be found here: All shared libs are built separately but with the same compiler flags. Let me know if you can reproduce the problem, when I run the tests the dynamic cast fails and the static cast works. |
Hi Dan I kept doing more tests during the day and I noticed the next:
class Foo {
public:
virtual ~Foo() {};
virtual void init() = 0;
};
class Bar : public Foo{
public:
void init() override;
};
void Bar::init() {
__android_log_print(ANDROID_LOG_DEBUG, "test", "Bar::init()");
} I also created this method inside libtest.so just to drive the test and do the object instantiations JNIEXPORT jboolean JNICALL Java_com_example_twolibs_TwoLibs_testDynamicCast(JNIEnv* env, jobject pObj) {
std::shared_ptr<Foo> foo = std::make_shared<Bar>();
foo->init();
std::shared_ptr<Bar> bar = std::dynamic_pointer_cast<Bar>(foo); The result of the test is that downcast works. If the symbols defined side libtest.so and the objects are created inside libtest.so but not across shared library boundaries dynamic casting will work.
|
hi @alexcohn I just realized that my previous post was describing my experiments really poorly, I added a better description this time thx |
Markdown tip:
void block_of_code() {
foo();
} is a lot more readable (and a lot easier to write) than
|
It seems very similar to #519 |
Removing System.loadLibrary("first");
System.loadLibrary("second"); fixes this for me. |
Repro case for android/ndk#533 that is isolated from gradle and the JVM.
Minimized and isolated from gradle and the JVM: https://github.com/DanAlbert/dynamic-cast-repro |
Repro case for android/ndk#533 that is isolated from gradle and the JVM.
Reduced the test case a bit more. Removing the explicit load of libfirst.so or switching to gnustl will cause it to pass. Not sure why yet. |
The Did you ever test your gabi++ workaround on x86? As far as I can tell your fix only worked for ARM: https://android.googlesource.com/platform/ndk/+/master/sources/cxx-stl/gabi++/src/type_info.cc#50 Amazingly this architecture specific nonsense does appear to be "correct" in the eyes of the standard. The C++ ABI spec states that
The ARM C++ ABI spec, on the other hand, states that this should be performed with a string comparison:
GCC's docs say this is unsupported: https://gcc.gnu.org/faq.html#dso
This is in contrast to their actual implementation though. https://gcc.gnu.org/ml/gcc-patches/2009-07/msg01239.html:
Apparently they gave up on trying to do right by the C++ ABI and decided to take the pragmatic approach of always using There's a flag we can build libc++abi with that will put it in a more permissive more that uses |
https://android-review.googlesource.com/#/c/platform/external/libcxxabi/+/503361 I think I'll cherry-pick this for r16. |
hi Dan If this is true, pls let me know once your changes make it to beta, I would like to test my entire project against it thx |
I cherry-picked it to the r16 branch. It's in build 4380891 (the latest at time of writing) if you want to try pulling a release from the build servers. Otherwise, we're hoping to ship beta 2 next week or maybe the week after (I keep jamming in more fixes; at some point I'll stop and actually ship the thing). From #519, it looks like there is another bug with |
Yep. Though unlike gabi++, it will do it for all architectures, not just ARM32. |
Okay, after having a conversation with some of the LLVM developers while trying to upstream our patches, turns out there actually is a bug in the test case here. I'm reverting the changes we've made since they are not actually correct. @memojedi: the bug in your code is here: class Foo {
public:
virtual ~Foo() {};
virtual void init() = 0;
};
class Bar : public Foo{
public:
void init() override;
}; Neither foo.h class Foo {
public:
virtual ~Foo();
virtual void init() = 0;
};
class Bar : public Foo{
public:
virtual ~Bar();
void init() override;
}; foo.cpp #include "foo.h"
Foo::~Foo() {}
Bar::~Bar() {} If you've done this, then you'll have a strong global symbol for the typeinfo in your library instead of weak symbols in every library, and then |
@memojedi did any of these suggestions solve your problem? I am seeing exact same issue, but cannot fix it. |
The C++ stdlib itself needs to be built with that. It's not something you can change on your end. Do you have a test case you can share? |
@DanAlbert I did few more experiments and found out that may problem is little different: in my case I have definition of |
I would say, you can follow the example of llvm-libc++ which keeps GLOBAL refs for typeinfo for hundreds of types, including void and std::type_info. |
@alexcohn is there a doc/link that I can follow? |
FWIW: the typeinfo objects for both of those types are output in libc++abi ( |
@DanAlbert I'm sorry that I didn't reply to this. I was switched to another project since my last reply and just until today I was switched to this again. You were correct in your previous comment, if I add non-inline destructor for the pure-virtual class the dynamic casting will work. Thx for looking into this. |
Enabling _LIBCXX_DYNAMIC_FALLBACK allows type_info objects to be equal if their strings are equal, not just their addresses. This is very common on Android where libraries are loaded with dlopen via System.loadLibrary. This behavior matches libsupc++, which always does a string compare to preserve compatibility for plugin architectures that behave similarly. Gory details are in the bug. Test: ndk/run_tests.py --filter dynamic_cast_dlopen Bug: android/ndk#533 Change-Id: I26cbf8d260cb7fb924580db6b346b42f39d5c2ed
The user's code was missing key functions on the types in question. This reverts commit dc179ca. Bug: android/ndk#533 Test: ndk/run_tests.py Change-Id: I7125c6dcaf0f87e906682eb46783d2056c2be7db
Test: ./run_tests.py --filter dynamic_cast_dlopen Bug: android/ndk#533 Change-Id: I345ae21639d7ab16ae3487b5ba633b66940b6bb6
There's actually a bug in the test case. Both `MyType` and `MyTypeImpl` are missing their key functions. This reverts commit 8c28ddd. Bug: android/ndk#533 Test: ./run_tests.py
This is still happening for me. I'm on r19-c. Please help!
|
One or more of the following type_info's has hidden visibility or is defined in more than one translation unit. They should all have public visibility. Refer: android/ndk#533 (comment)
One or more of the following type_info's has hidden visibility or is defined in more than one translation unit. They should all have public visibility. Refer: android/ndk#533 (comment) 2a540b8
Required to have dynamic_cast working across library boundaries without introducing key functions. Ref: android/ndk#533 (comment)
I'm using NDK 15.2.4203891 (it was downloaded through android studio SDK manager). My project consists of 4 shared libraries, all of them are built with these cpp flags
LOCAL_CPP_FEATURES += exceptions
LOCAL_CPP_FEATURES += rtti
I'm linking all my shared libraries to a single shared c++ runtime
APP_STL := c++_shared
NDK_TOOLCHAIN_VERSION=clang
I noticed that every time I try to do dynamic casting from parent class to derived class I get a nullptr back from std::dynamic_pointer_cast. The c++ standard allows such casting to take place.
I first noticed this problem back in NDK 10e, I was able to bypass the issue by always rebuilding the c++ runtime with this directive in Application.mk
LIBCXX_USE_GABIXX := true
This would rebuild the c++ runtime and would allow such casting to take place. Is this dynamic casting a common problem among all NDK versions?
Now that I'm trying to move to the latest and noticed the same rtti issue as before I tried to build the c++ runtime using this directive in my Application.mk
LIBCXX_FORCE_REBUILD := true
But I started getting this build error:
Android NDK: /Users/guillermorodriguez/Library/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/Android.mk: Cannot find module with tag 'external/libcxxabi' in import path
Android NDK: Are you sure your NDK_MODULE_PATH variable is properly defined ?
Android NDK: The following directories were searched:
Android NDK:
Does anyone know if these are known issues? (dynamic casting and runtime build error), I don't want to change all my dynamic pointer casting lines into static casting.
The text was updated successfully, but these errors were encountered: