A single-file header-file library for debugging RTTI problems with dynamic linking.
It is able to:
Get the address of the std::type_info
object for an arbitrary pointer to an object of polymorphic class type.
Dump information about an std::type_info
object, including the name of the shared object (or executable) where it is located.
Dump an std::type_info
class hierarchy.
Dump information about the current exception from within a catch block, specifically the exception's std::type_info
object and the shared object where it is located.
The rtti_dump.h
header is a standalone one-file header library. It assumes the use of the “Itanium” C++ ABI (http://itanium-cxx-abi.github.io/cxx-abi/), which is used across many CPU architectures and operating systems. It should work with GCC or Clang and should work with only C++03 language support.
C++ source: #include "rtti_dump.h"
Link against these libraries: -llog -ldl
On Android, rtti_dump writes to logcat by default. Define RTTI_DUMP_USE_PRINTF
to instead use printf
.
See jni/demo/upper.cc
for example usage. These APIs are defined:
const std::type_info *rtti_dump::__cxa_current_exception_type()
const std::type_info *rtti_dump::runtime_typeid(const volatile T *dynptr)
std::string rtti_dump::dladdr_fname(const void *ptr)
void rtti_dump::dump_type(const std::type_info *type, const char *label=..., int indent=...)
void rtti_dump::dump_current_exception(const char *label=...)
void rtti_dump::dump_class_hierarchy(const std::type_info *info, const char *label=...)
The demo illustrates problems that occur when an RTTI std::type_info
object is duplicated between two dynamic shared objects (DSOs).
The demo expects an armeabi-v7a device running android-16 or newer:
export NDK=$HOME/Android/Sdk/ndk-bundle export ANDROID_SERIAL=... # if you have multiple devices ./test-ndk-build.sh
Alternatively, it runs on a Unix host:
./test-unix-host.sh
Duplication of the std::type_info
objects can break RTTI-based features, including:
dynamic_cast
std::type_info::operator==
Frequently, there is no single place to output a type_info
object, so the compiler outputs a copy everywhere one is needed, and the copies are later merged. An object like this has “vague linkage”. This deduplication is reliable when multiple object files are linked into a single DSO at build-time. At run-time, copies across multiple DSOs cannot literally be merged, but the system linker can sometimes resolve references to a type_info
object, from multiple DSOs, to a single copy from one DSO. This capability is sometimes called “dynamic vague linkage”.
Android's system linker implements dynamic vague linkage, but with two caveats:
System.loadLibrary
or dlopen
).The demo consists of three binaries that resemble a typical Android app that uses JNI:
A main
executable. Using dlopen
, main
first loads liblower.so
, then libupper.so
. The dlopen
calls are equivalent to System.loadLibrary
calls for the demo's purposes.
A liblower.so
shared library that defines a few functions.
A libupper.so
shared library that needs liblower.so
and contains the bulk of the test code.
liblower.so
and libupper.so
contain some duplicated copies of some type_info
objects.
The project's Application.mk
uses APP_STL := c++_shared
, and the main jni/demo
test currently fails (as of NDK r16). The test passes with APP_STL := gnustl_shared
. libc++/libc++abi assumes that vague linkage is working, whereas gnustl assumes instead that it must use strcmp
to decide whether two type_info
objects are equal.
The jni/fallback
directory demonstrates cases where compiling libc++abi with _LIBCXX_DYNAMIC_FALLBACK
is an insufficent fix.
The jni/anon
test ignores the DSO issue and instead demonstrates handling of classes in anonymous namespaces. Equality of these classes' type_info
must not be determined using strcmp
. GCC marks the type_info
name with an asterisk prefix to indicate this. The test passes with Clang+libc++ and with GCC+gnustl, but fails with Clang+gnustl (https://bugs.llvm.org/show_bug.cgi?id=34907).
==== demo_dynamic_cast (jni/demo/upper.cpp,25) dynamic_cast<Derived*>(lower_ptr): NULL (FAILURE) *runtime_typeid(lower_ptr) == *runtime_typeid(upper_ptr): false (FAILURE) lower_type: type N4demo7DerivedE: lower_type: type_info obj: 0xf1a4ad20 (in /data/local/tmp/rtti_dump/liblower.so) lower_type: type_info name: 0xf1a48f99 (in /data/local/tmp/rtti_dump/liblower.so) lower_type: base classes: lower_type: type N4demo4BaseE: lower_type: type_info obj: 0xf1a4ad18 (in /data/local/tmp/rtti_dump/liblower.so) lower_type: type_info name: 0xf1a48fa9 (in /data/local/tmp/rtti_dump/liblower.so) upper_type: type N4demo7DerivedE: upper_type: type_info obj: 0xf1a16ac8 (in /data/local/tmp/rtti_dump/libupper.so) upper_type: type_info name: 0xf1a14f06 (in /data/local/tmp/rtti_dump/libupper.so) upper_type: base classes: upper_type: type N4demo4BaseE: upper_type: type_info obj: 0xf1a16ac0 (in /data/local/tmp/rtti_dump/libupper.so) upper_type: type_info name: 0xf1a14ef9 (in /data/local/tmp/rtti_dump/libupper.so) ==== demo_catch_string (jni/demo/upper.cpp,54) typeid(std::string): type NSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE: typeid(std::string): type_info obj: 0xf1a16b00 (in /data/local/tmp/rtti_dump/libupper.so) typeid(std::string): type_info name: 0xf1a14f20 (in /data/local/tmp/rtti_dump/libupper.so) current_exception: type NSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE: current_exception: type_info obj: 0xf1a4acf0 (in /data/local/tmp/rtti_dump/liblower.so) current_exception: type_info name: 0xf1a48f20 (in /data/local/tmp/rtti_dump/liblower.so) unable to catch std::string object (FAILURE) ==== fallback_do_test (jni/fallback/upper.cpp,58) dynamic_cast from Src to BaseDup: non-NULL (FAILURE) dynamic_cast from Src to BaseUniq: NULL (FAILURE) typeid(BaseDup): type N8fallback7BaseDupE: typeid(BaseDup): type_info obj: 0xf1a16b20 (in /data/local/tmp/rtti_dump/libupper.so) typeid(BaseDup): type_info name: 0xf1a14fb0 (in /data/local/tmp/rtti_dump/libupper.so) typeid(BaseUniq): type N8fallback8BaseUniqE: typeid(BaseUniq): type_info obj: 0xf1a16b28 (in /data/local/tmp/rtti_dump/libupper.so) typeid(BaseUniq): type_info name: 0xf1a14fd0 (in /data/local/tmp/rtti_dump/libupper.so) typeid(MidLoc): type N8fallback6MidLocE: typeid(MidLoc): type_info obj: 0xf1a16b30 (in /data/local/tmp/rtti_dump/libupper.so) typeid(MidLoc): type_info name: 0xf1a14ff0 (in /data/local/tmp/rtti_dump/libupper.so) typeid(MidExt): type N8fallback6MidExtE: typeid(MidExt): type_info obj: 0xf1a4ad70 (in /data/local/tmp/rtti_dump/liblower.so) typeid(MidExt): type_info name: 0xf1a48fc0 (in /data/local/tmp/rtti_dump/liblower.so) typeid(Src): type N8fallback3SrcE: typeid(Src): type_info obj: 0xf1a16b18 (in /data/local/tmp/rtti_dump/libupper.so) typeid(Src): type_info name: 0xf1a14f99 (in /data/local/tmp/rtti_dump/libupper.so) typeid(Full) hierarchy: type N8fallback4FullE: typeid(Full) hierarchy: type_info obj: 0xf1a16b40 (in /data/local/tmp/rtti_dump/libupper.so) typeid(Full) hierarchy: type_info name: 0xf1a15010 (in /data/local/tmp/rtti_dump/libupper.so) typeid(Full) hierarchy: base classes: typeid(Full) hierarchy: type N8fallback6MidLocE: typeid(Full) hierarchy: type_info obj: 0xf1a16b30 (in /data/local/tmp/rtti_dump/libupper.so) typeid(Full) hierarchy: type_info name: 0xf1a14ff0 (in /data/local/tmp/rtti_dump/libupper.so) typeid(Full) hierarchy: base classes: typeid(Full) hierarchy: type N8fallback7BaseDupE: typeid(Full) hierarchy: type_info obj: 0xf1a16b20 (in /data/local/tmp/rtti_dump/libupper.so) typeid(Full) hierarchy: type_info name: 0xf1a14fb0 (in /data/local/tmp/rtti_dump/libupper.so) typeid(Full) hierarchy: type N8fallback6MidExtE: typeid(Full) hierarchy: type_info obj: 0xf1a4ad70 (in /data/local/tmp/rtti_dump/liblower.so) typeid(Full) hierarchy: type_info name: 0xf1a48fc0 (in /data/local/tmp/rtti_dump/liblower.so) typeid(Full) hierarchy: base classes: typeid(Full) hierarchy: type N8fallback7BaseDupE: typeid(Full) hierarchy: type_info obj: 0xf1a4ad90 (in /data/local/tmp/rtti_dump/liblower.so) typeid(Full) hierarchy: type_info name: 0xf1a48fe0 (in /data/local/tmp/rtti_dump/liblower.so) typeid(Full) hierarchy: type N8fallback8BaseUniqE: typeid(Full) hierarchy: type_info obj: 0xf1a4ad98 (in /data/local/tmp/rtti_dump/liblower.so) typeid(Full) hierarchy: type_info name: 0xf1a49000 (in /data/local/tmp/rtti_dump/liblower.so) typeid(Full) hierarchy: type N8fallback3SrcE: typeid(Full) hierarchy: type_info obj: 0xf1a16b18 (in /data/local/tmp/rtti_dump/libupper.so) typeid(Full) hierarchy: type_info name: 0xf1a14f99 (in /data/local/tmp/rtti_dump/libupper.so) ==== anon_do_test (jni/anon/upper2.cpp,34) dynamic_cast of upper1:Anon from Base* to upper2:Anon*: NULL (PASS) typeid(upper1:Anon) == typeid(upper2:Anon): false (PASS) upper1: typeid(Anon): type N4anon12_GLOBAL__N_14AnonE: upper1: typeid(Anon): type_info obj: 0xf1a16bf4 (in /data/local/tmp/rtti_dump/libupper.so) upper1: typeid(Anon): type_info name: 0xf1a15030 (in /data/local/tmp/rtti_dump/libupper.so) upper1: typeid(Anon): base classes: upper1: typeid(Anon): type N4anon4BaseE: upper1: typeid(Anon): type_info obj: 0xf1a16bec (in /data/local/tmp/rtti_dump/libupper.so) upper1: typeid(Anon): type_info name: 0xf1a1504b (in /data/local/tmp/rtti_dump/libupper.so) upper2: typeid(Anon): type N4anon12_GLOBAL__N_14AnonE: upper2: typeid(Anon): type_info obj: 0xf1a16c20 (in /data/local/tmp/rtti_dump/libupper.so) upper2: typeid(Anon): type_info name: 0xf1a15060 (in /data/local/tmp/rtti_dump/libupper.so) upper2: typeid(Anon): base classes: upper2: typeid(Anon): type N4anon4BaseE: upper2: typeid(Anon): type_info obj: 0xf1a16bec (in /data/local/tmp/rtti_dump/libupper.so) upper2: typeid(Anon): type_info name: 0xf1a1504b (in /data/local/tmp/rtti_dump/libupper.so)
Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.