Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -400,6 +400,10 @@ add_definitions(-DLIBCXXABI_BAREMETAL) endif() +if (LIBCXX_DYNAMIC_FALLBACK) + add_definitions(-D_LIBCXX_DYNAMIC_FALLBACK) +endif() + string(REPLACE ";" " " LIBCXXABI_CXX_FLAGS "${LIBCXXABI_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LIBCXXABI_CXX_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${LIBCXXABI_C_FLAGS}") Index: src/private_typeinfo.cpp =================================================================== --- src/private_typeinfo.cpp +++ src/private_typeinfo.cpp @@ -10,39 +10,19 @@ #include "private_typeinfo.h" // The flag _LIBCXX_DYNAMIC_FALLBACK is used to make dynamic_cast more -// forgiving when type_info's mistakenly have hidden visibility and thus -// multiple type_infos can exist for a single type. -// +// forgiving when multiple type_infos exist for a single type. This happens if +// the libraries are mistakenly built with the type_infos having hidden +// visibility, but also occurs when the libraries are loaded with dlopen. +// // When _LIBCXX_DYNAMIC_FALLBACK is defined, and only in the case where // there is a detected inconsistency in the type_info hierarchy during a // dynamic_cast, then the equality operation will fall back to using strcmp // on type_info names to determine type_info equality. -// -// This change happens *only* under dynamic_cast, and only when -// dynamic_cast is faced with the choice: abort, or possibly give back the -// wrong answer. If when the dynamic_cast is done with this fallback -// algorithm and an inconsistency is still detected, dynamic_cast will call -// abort with an appropriate message. -// -// The current implementation of _LIBCXX_DYNAMIC_FALLBACK requires a -// printf-like function called syslog: -// -// void syslog(int facility_priority, const char* format, ...); -// -// If you want this functionality but your platform doesn't have syslog, -// just implement it in terms of fprintf(stderr, ...). -// +// // _LIBCXX_DYNAMIC_FALLBACK is currently off by default. - #include - -#ifdef _LIBCXX_DYNAMIC_FALLBACK -#include "abort_message.h" -#include -#endif - // On Windows, typeids are different between DLLs and EXEs, so comparing // type_info* will work for typeids from the same compiled file but fail // for typeids from a DLL and an executable. Among other things, exceptions @@ -647,11 +627,11 @@ // find (static_ptr, static_type), either on a public or private path if (info.path_dst_ptr_to_static_ptr == unknown) { - // We get here only if there is some kind of visibility problem - // in client code. - syslog(LOG_ERR, "dynamic_cast error 1: Both of the following type_info's " - "should have public visibility. At least one of them is hidden. %s" - ", %s.\n", static_type->name(), dynamic_type->name()); + // We get here only if there is some kind of visibility problem in + // client code. Possibly because the binaries were built + // incorrectly, but possibly because the library was loaded with + // dlopen. + // // Redo the search comparing type_info's using strcmp info = {dst_type, static_ptr, static_type, src2dst_offset, 0}; info.number_of_dst_type = 1; @@ -672,10 +652,6 @@ if (info.path_dst_ptr_to_static_ptr == unknown && info.path_dynamic_ptr_to_static_ptr == unknown) { - syslog(LOG_ERR, "dynamic_cast error 2: One or more of the following type_info's " - " has hidden visibility. They should all have public visibility. " - " %s, %s, %s.\n", static_type->name(), dynamic_type->name(), - dst_type->name()); // Redo the search comparing type_info's using strcmp info = {dst_type, static_ptr, static_type, src2dst_offset, 0}; dynamic_type->search_below_dst(&info, dynamic_ptr, public_path, true); Index: test/CMakeLists.txt =================================================================== --- test/CMakeLists.txt +++ test/CMakeLists.txt @@ -18,6 +18,7 @@ pythonize_bool(LIBCXXABI_ENABLE_EXCEPTIONS) pythonize_bool(LIBCXXABI_USE_LLVM_UNWINDER) pythonize_bool(LIBCXXABI_BUILD_EXTERNAL_THREAD_LIBRARY) +pythonize_bool(LIBCXX_DYNAMIC_FALLBACK) set(LIBCXXABI_TARGET_INFO "libcxx.test.target_info.LocalTI" CACHE STRING "TargetInfo to use when setting up test environment.") set(LIBCXXABI_EXECUTOR "None" CACHE STRING Index: test/dlopen_dynamic_cast.sh.cpp =================================================================== --- /dev/null +++ test/dlopen_dynamic_cast.sh.cpp @@ -0,0 +1,89 @@ +//===-------------------- dlopen_dynamic_cast.sh.cpp ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// type_infos are not coalesced across dlopen boundaries when RTLD_LOCAL is +// used, as is common in plugin interfaces and JNI libraries. For dynamic_cast +// to work across a dlopen boundary, we must use a string comparison of the type +// names instead of a pointer comparison of the type_infos. +// https://reviews.llvm.org/D38599 + +// XFAIL: libcxx-no-dynamic-fallback + +// RUN: %cxx %flags %compile_flags -DBUILD_BASE -fPIC -c %s -o %T/base.o +// RUN: %cxx %flags %link_flags -shared %T/base.o -o %T/libbase.so +// RUN: %cxx %flags %compile_flags -DBUILD_TEST -fPIC -c %s -o %T/test.o +// RUN: %cxx %flags %link_flags -shared %T/test.o -o %T/libtest.so -L%T -lbase +// RUN: %cxx %flags %compile_flags -DBUILD_EXE -c %s -o %t.o +// RUN: %cxx %flags %link_flags %t.o -o %t.exe -ldl +// RUN: LD_LIBRARY_PATH=%T %t.exe + +class Base { +public: + virtual ~Base(){}; +}; + +class BaseImpl : public Base { +public: + BaseImpl(); +}; + +#ifdef BUILD_BASE +BaseImpl::BaseImpl() {} +#endif + +#ifdef BUILD_TEST +extern "C" bool do_test() { + BaseImpl base_impl; + Base* base = &base_impl; + return dynamic_cast(base) != nullptr; +} +#endif + +#ifdef BUILD_EXE +#include +#include +#include + +typedef bool (*test_func)(); + +void* load_library(const char* name) { + void* lib = dlopen(name, RTLD_NOW | RTLD_LOCAL); + if (lib == nullptr) { + fprintf(stderr, "dlopen %s failed: %s\n", name, dlerror()); + abort(); + } + return lib; +} + +test_func load_func(void* lib, const char* name) { + test_func sym = reinterpret_cast(dlsym(lib, name)); + if (sym == nullptr) { + fprintf(stderr, "dlsym %s failed: %s\n", name, dlerror()); + abort(); + } + return sym; +} + +int main(int argc, char**) { + // Explicitly loading libbase.so before libtest.so causes the test to fail + // because the type_infos do not get coalesced. + load_library("libbase.so"); + + void* libtest = load_library("libtest.so"); + test_func do_test = load_func(libtest, "do_test"); + + if (!do_test()) { + fprintf(stderr, "do_test() failed!\n"); + return EXIT_FAILURE; + } else { + fprintf(stderr, "do_test() passed!\n"); + return EXIT_SUCCESS; + } +} +#endif Index: test/libcxxabi/test/config.py =================================================================== --- test/libcxxabi/test/config.py +++ test/libcxxabi/test/config.py @@ -48,6 +48,11 @@ if not self.get_lit_bool('llvm_unwinder', False): self.config.available_features.add('libcxxabi-has-system-unwinder') + if self.get_lit_bool('dynamic_fallback', False): + self.config.available_features.add('libcxx-dynamic-fallback') + else: + self.config.available_features.add('libcxx-no-dynamic-fallback') + def configure_compile_flags(self): self.cxx.compile_flags += ['-DLIBCXXABI_NO_TIMER'] if self.get_lit_bool('enable_exceptions', True): Index: test/lit.site.cfg.in =================================================================== --- test/lit.site.cfg.in +++ test/lit.site.cfg.in @@ -16,6 +16,7 @@ config.executor = "@LIBCXXABI_EXECUTOR@" config.libcxxabi_shared = "@LIBCXXABI_ENABLE_SHARED@" config.enable_shared = "@LIBCXX_ENABLE_SHARED@" +config.dynamic_fallback = "@LIBCXX_DYNAMIC_FALLBACK@" config.enable_exceptions = "@LIBCXXABI_ENABLE_EXCEPTIONS@" config.host_triple = "@LLVM_HOST_TRIPLE@" config.target_triple = "@TARGET_TRIPLE@"