danalbert updated this revision to Diff 118502.
danalbert edited the summary of this revision.
danalbert added a comment.

Added a (failing) test case. The test case will fail unless the default value 
of `_LIBCXX_DYNAMIC_FALLBACK` is changed.


https://reviews.llvm.org/D38599

Files:
  src/private_typeinfo.cpp
  test/dlopen_dynamic_cast.sh.cpp

Index: test/dlopen_dynamic_cast.sh.cpp
===================================================================
--- /dev/null
+++ test/dlopen_dynamic_cast.sh.cpp
@@ -0,0 +1,87 @@
+//===-------------------- 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
+
+// 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<BaseImpl*>(base) != nullptr;
+}
+#endif
+
+#ifdef BUILD_EXE
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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<test_func>(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: 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 <string.h>
 
-
-#ifdef _LIBCXX_DYNAMIC_FALLBACK
-#include "abort_message.h"
-#include <sys/syslog.h>
-#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);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to