From 538fe8f35b26b925e21a4329599c188d48264f0d Mon Sep 17 00:00:00 2001 From: Howard Hinnant Date: Fri, 28 Sep 2012 22:43:50 +0000 Subject: [PATCH] Set up code under _LIBCXX_DYNAMIC_FALLBACK which is off by default. For a full description of _LIBCXX_DYNAMIC_FALLBACK, see src/private_typeinfo.cpp. llvm-svn: 164863 --- libcxxabi/src/private_typeinfo.cpp | 155 ++++++++++++++++++++++++++++--------- libcxxabi/src/private_typeinfo.h | 24 +++--- 2 files changed, 129 insertions(+), 50 deletions(-) diff --git a/libcxxabi/src/private_typeinfo.cpp b/libcxxabi/src/private_typeinfo.cpp index d68e78b..bb15154 100644 --- a/libcxxabi/src/private_typeinfo.cpp +++ b/libcxxabi/src/private_typeinfo.cpp @@ -9,7 +9,34 @@ #include "private_typeinfo.h" -#if __APPLE__ +// 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. +// +// 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 determin 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 approriate message. +// +// The current implementation of _LIBCXX_DYNAMIC_FALLBACK requires a +// printf-like function called syslog: +// +// void syslog(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. + +#if _LIBCXX_DYNAMIC_FALLBACK +#include "abort_message.h" +#include #include #endif @@ -18,6 +45,28 @@ namespace __cxxabiv1 #pragma GCC visibility push(hidden) +#if _LIBCXX_DYNAMIC_FALLBACK + +inline +bool +is_equal(const std::type_info* x, const std::type_info* y, bool use_strcmp) +{ + if (!use_strcmp) + return x == y; + return strcmp(x->name(), y->name()) == 0; +} + +#else // !_LIBCXX_DYNAMIC_FALLBACK + +inline +bool +is_equal(const std::type_info* x, const std::type_info* y, bool) +{ + return x == y; +} + +#endif // _LIBCXX_DYNAMIC_FALLBACK + // __shim_type_info __shim_type_info::~__shim_type_info() @@ -440,8 +489,8 @@ __dynamic_cast(const void* static_ptr, // Using giant short cut. Add that information to info. info.number_of_dst_type = 1; // Do the search - dynamic_type->search_above_dst(&info, dynamic_ptr, dynamic_ptr, public_path); -#if __APPLE__ + dynamic_type->search_above_dst(&info, dynamic_ptr, dynamic_ptr, public_path, false); +#if _LIBCXX_DYNAMIC_FALLBACK // The following if should always be false because we should definitely // find (static_ptr, static_type), either on a public or private path if (info.path_dst_ptr_to_static_ptr == unknown) @@ -451,9 +500,18 @@ __dynamic_cast(const void* static_ptr, syslog(LOG_ERR, "dynamic_cast error 1: Both of the following type_info's " "should have public visibility. At least of of them is hidden. %s" ", %s.\n", static_type->name(), dynamic_type->name()); - info.path_dst_ptr_to_static_ptr = public_path; + // 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; + dynamic_type->search_above_dst(&info, dynamic_ptr, dynamic_ptr, public_path, true); + if (info.path_dst_ptr_to_static_ptr == unknown) + { + abort_message("dynamic_cast error: Unable to compute dynamic " + "cast from %s to %s\n", static_type->name(), + dynamic_type->name()); + } } -#endif // __APPLE__ +#endif // _LIBCXX_DYNAMIC_FALLBACK // Query the search. if (info.path_dst_ptr_to_static_ptr == public_path) dst_ptr = dynamic_ptr; @@ -461,19 +519,30 @@ __dynamic_cast(const void* static_ptr, else { // Not using giant short cut. Do the search - dynamic_type->search_below_dst(&info, dynamic_ptr, public_path); - #if __APPLE__ + dynamic_type->search_below_dst(&info, dynamic_ptr, public_path, false); + #if _LIBCXX_DYNAMIC_FALLBACK // The following if should always be false because we should definitely // find (static_ptr, static_type), either on a public or private path - if (info.path_dst_ptr_to_static_ptr == unknown && + 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); + if (info.path_dst_ptr_to_static_ptr == unknown && + info.path_dynamic_ptr_to_static_ptr == unknown) + { + abort_message("dynamic_cast error: Unable to compute dynamic " + "cast from %s to %s with a dynamic type of %s\n", + static_type->name(), dst_type->name(), + dynamic_type->name()); + } } -#endif // __APPLE__ +#endif // _LIBCXX_DYNAMIC_FALLBACK // Query the search. switch (info.number_to_static_ptr) { @@ -629,12 +698,13 @@ __class_type_info::process_static_type_below_dst(__dynamic_cast_info* info, void __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { typedef const __base_class_type_info* Iter; - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_below_dst(info, current_ptr, path_below); - else if (this == info->dst_type) + else if (is_equal(this, info->dst_type, use_strcmp)) { // We've been here before if we've recorded current_ptr in one of these // two places: @@ -674,7 +744,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, // Zero out found flags info->found_our_static_ptr = false; info->found_any_static_type = false; - p->search_above_dst(info, current_ptr, current_ptr, public_path); + p->search_above_dst(info, current_ptr, current_ptr, public_path, use_strcmp); if (info->search_done) break; if (info->found_any_static_type) @@ -733,7 +803,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, // This is not a static_type and not a dst_type. const Iter e = __base_info + __base_count; Iter p = __base_info; - p->search_below_dst(info, current_ptr, path_below); + p->search_below_dst(info, current_ptr, path_below, use_strcmp); if (++p < e) { if ((__flags & __diamond_shaped_mask) || info->number_to_static_ptr == 1) @@ -746,7 +816,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, { if (info->search_done) break; - p->search_below_dst(info, current_ptr, path_below); + p->search_below_dst(info, current_ptr, path_below, use_strcmp); } while (++p < e); } else if (__flags & __non_diamond_repeat_mask) @@ -765,7 +835,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, if (info->number_to_static_ptr == 1 && info->path_dst_ptr_to_static_ptr == public_path) break; - p->search_below_dst(info, current_ptr, path_below); + p->search_below_dst(info, current_ptr, path_below, use_strcmp); } while (++p < e); } else @@ -788,7 +858,7 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, // and not a dst_type under here. if (info->number_to_static_ptr == 1) break; - p->search_below_dst(info, current_ptr, path_below); + p->search_below_dst(info, current_ptr, path_below, use_strcmp); } while (++p < e); } } @@ -800,11 +870,12 @@ __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, void __si_class_type_info::search_below_dst(__dynamic_cast_info* info, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_below_dst(info, current_ptr, path_below); - else if (this == info->dst_type) + else if (is_equal(this, info->dst_type, use_strcmp)) { // We've been here before if we've recorded current_ptr in one of these // two places: @@ -832,7 +903,7 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info, // Zero out found flags info->found_our_static_ptr = false; info->found_any_static_type = false; - __base_type->search_above_dst(info, current_ptr, current_ptr, public_path); + __base_type->search_above_dst(info, current_ptr, current_ptr, public_path, use_strcmp); if (info->found_any_static_type) { is_dst_type_derived_from_static_type = true; @@ -867,7 +938,7 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info, else { // This is not a static_type and not a dst_type - __base_type->search_below_dst(info, current_ptr, path_below); + __base_type->search_below_dst(info, current_ptr, path_below, use_strcmp); } } @@ -876,12 +947,13 @@ __si_class_type_info::search_below_dst(__dynamic_cast_info* info, void __class_type_info::search_below_dst(__dynamic_cast_info* info, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { typedef const __base_class_type_info* Iter; - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_below_dst(info, current_ptr, path_below); - else if (this == info->dst_type) + else if (is_equal(this, info->dst_type, use_strcmp)) { // We've been here before if we've recorded current_ptr in one of these // two places: @@ -946,9 +1018,10 @@ void __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, const void* dst_ptr, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); else { @@ -971,7 +1044,7 @@ __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, // Zero out found flags info->found_our_static_ptr = false; info->found_any_static_type = false; - p->search_above_dst(info, dst_ptr, current_ptr, path_below); + p->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp); if (++p < e) { do @@ -1000,7 +1073,7 @@ __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, // Zero out found flags info->found_our_static_ptr = false; info->found_any_static_type = false; - p->search_above_dst(info, dst_ptr, current_ptr, path_below); + p->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp); } while (++p < e); } // Restore flags @@ -1015,12 +1088,13 @@ void __si_class_type_info::search_above_dst(__dynamic_cast_info* info, const void* dst_ptr, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); else - __base_type->search_above_dst(info, dst_ptr, current_ptr, path_below); + __base_type->search_above_dst(info, dst_ptr, current_ptr, path_below, use_strcmp); } // This is the same algorithm as __vmi_class_type_info::search_above_dst but @@ -1029,9 +1103,10 @@ void __class_type_info::search_above_dst(__dynamic_cast_info* info, const void* dst_ptr, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { - if (this == info->static_type) + if (is_equal(this, info->static_type, use_strcmp)) process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); } @@ -1043,7 +1118,8 @@ void __base_class_type_info::search_above_dst(__dynamic_cast_info* info, const void* dst_ptr, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { ptrdiff_t offset_to_base = __offset_flags >> __offset_shift; if (__offset_flags & __virtual_mask) @@ -1055,13 +1131,15 @@ __base_class_type_info::search_above_dst(__dynamic_cast_info* info, static_cast(current_ptr) + offset_to_base, (__offset_flags & __public_mask) ? path_below : - not_public_path); + not_public_path, + use_strcmp); } void __base_class_type_info::search_below_dst(__dynamic_cast_info* info, const void* current_ptr, - int path_below) const + int path_below, + bool use_strcmp) const { ptrdiff_t offset_to_base = __offset_flags >> __offset_shift; if (__offset_flags & __virtual_mask) @@ -1073,7 +1151,8 @@ __base_class_type_info::search_below_dst(__dynamic_cast_info* info, static_cast(current_ptr) + offset_to_base, (__offset_flags & __public_mask) ? path_below : - not_public_path); + not_public_path, + use_strcmp); } #pragma GCC visibility pop diff --git a/libcxxabi/src/private_typeinfo.h b/libcxxabi/src/private_typeinfo.h index 0219fa1..c8da814 100644 --- a/libcxxabi/src/private_typeinfo.h +++ b/libcxxabi/src/private_typeinfo.h @@ -76,10 +76,10 @@ struct __dynamic_cast_info { // const data supplied to the search: - const __class_type_info* const dst_type; - const void* const static_ptr; - const __class_type_info* const static_type; - const std::ptrdiff_t src2dst_offset; + const __class_type_info* dst_type; + const void* static_ptr; + const __class_type_info* static_type; + std::ptrdiff_t src2dst_offset; // Data that represents the answer: @@ -133,9 +133,9 @@ public: __attribute__ ((__visibility__("hidden"))) void process_found_base_class(__dynamic_cast_info*, void*, int) const; __attribute__ ((__visibility__("hidden"))) - virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) - virtual void search_below_dst(__dynamic_cast_info*, const void*, int) const; + virtual void search_below_dst(__dynamic_cast_info*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) virtual bool can_catch(const __shim_type_info*, void*&) const; __attribute__ ((__visibility__("hidden"))) @@ -152,9 +152,9 @@ public: __attribute__ ((__visibility__("hidden"))) virtual ~__si_class_type_info(); __attribute__ ((__visibility__("hidden"))) - virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) - virtual void search_below_dst(__dynamic_cast_info*, const void*, int) const; + virtual void search_below_dst(__dynamic_cast_info*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) virtual void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; }; @@ -172,8 +172,8 @@ public: __offset_shift = 8 }; - void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; - void search_below_dst(__dynamic_cast_info*, const void*, int) const; + void search_above_dst(__dynamic_cast_info*, const void*, const void*, int, bool) const; + void search_below_dst(__dynamic_cast_info*, const void*, int, bool) const; void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; }; @@ -197,9 +197,9 @@ public: __attribute__ ((__visibility__("hidden"))) virtual ~__vmi_class_type_info(); __attribute__ ((__visibility__("hidden"))) - virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) - virtual void search_below_dst(__dynamic_cast_info*, const void*, int) const; + virtual void search_below_dst(__dynamic_cast_info*, const void*, int, bool) const; __attribute__ ((__visibility__("hidden"))) virtual void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; }; -- 2.7.4