Skip to content

Commit 2405bd6

Browse files
committed
Rework std::type_info definition to support systems without fully
merged type info names. Previously std::type_info always expected type info string to be unique. But this isn't always the case. Like when -Bsymbolic is passed to the linker or due to llvm.org/PR37398. This patch adds the LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT CMake option which, when specified, overrides the default configuration for the library. The current defaults still assume unique names even though this isn't strictly correct for ELF binaries. We should consider changing the default in a follow up commit. llvm-svn: 361913
1 parent 818c652 commit 2405bd6

File tree

5 files changed

+187
-83
lines changed

5 files changed

+187
-83
lines changed

libcxx/CMakeLists.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,18 @@ set(LIBCXX_ABI_NAMESPACE "" CACHE STRING "The inline ABI namespace used by libc+
123123
option(LIBCXX_ABI_UNSTABLE "Unstable ABI of libc++." OFF)
124124
option(LIBCXX_ABI_FORCE_ITANIUM "Ignore auto-detection and force use of the Itanium ABI.")
125125
option(LIBCXX_ABI_FORCE_MICROSOFT "Ignore auto-detection and force use of the Microsoft ABI.")
126+
127+
128+
set(LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT "" CACHE STRING
129+
"Whether typeinfo names are expected to be unique. Defining this option overrides the default configuration in the library.")
130+
set(MERGED_TYPEINFO_VALUES ";ON;OFF")
131+
set_property(CACHE LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT PROPERTY STRINGS ${MERGED_TYPEINFO_DEFAULTS})
132+
list(FIND MERGED_TYPEINFO_VALUES "${LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT}" IS_VALID_DEFAULT)
133+
if (${IS_VALID_DEFAULT} EQUAL -1)
134+
message(FATAL_ERROR "Value '${LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT}' is not a valid value for
135+
LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT")
136+
endif()
137+
126138
option(LIBCXX_HIDE_FROM_ABI_PER_TU_BY_DEFAULT "Enable per TU ABI insulation by default. To be used by vendors." OFF)
127139
set(LIBCXX_ABI_DEFINES "" CACHE STRING "A semicolon separated list of ABI macros to define in the site config header.")
128140
option(LIBCXX_USE_COMPILER_RT "Use compiler-rt instead of libgcc" OFF)
@@ -701,13 +713,15 @@ config_define_if(LIBCXX_ABI_UNSTABLE _LIBCPP_ABI_UNSTABLE)
701713
config_define_if(LIBCXX_ABI_FORCE_ITANIUM _LIBCPP_ABI_FORCE_ITANIUM)
702714
config_define_if(LIBCXX_ABI_FORCE_MICROSOFT _LIBCPP_ABI_FORCE_MICROSOFT)
703715
config_define_if(LIBCXX_HIDE_FROM_ABI_PER_TU_BY_DEFAULT _LIBCPP_HIDE_FROM_ABI_PER_TU_BY_DEFAULT)
704-
705716
config_define_if_not(LIBCXX_ENABLE_GLOBAL_FILESYSTEM_NAMESPACE _LIBCPP_HAS_NO_GLOBAL_FILESYSTEM_NAMESPACE)
706717
config_define_if_not(LIBCXX_ENABLE_STDIN _LIBCPP_HAS_NO_STDIN)
707718
config_define_if_not(LIBCXX_ENABLE_STDOUT _LIBCPP_HAS_NO_STDOUT)
708719
config_define_if_not(LIBCXX_ENABLE_THREADS _LIBCPP_HAS_NO_THREADS)
709720
config_define_if_not(LIBCXX_ENABLE_MONOTONIC_CLOCK _LIBCPP_HAS_NO_MONOTONIC_CLOCK)
710721
config_define_if_not(LIBCXX_ENABLE_THREAD_UNSAFE_C_FUNCTIONS _LIBCPP_HAS_NO_THREAD_UNSAFE_C_FUNCTIONS)
722+
if (NOT LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT STREQUAL "")
723+
config_define("${LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT}" _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT)
724+
endif()
711725

712726
config_define_if(LIBCXX_HAS_PTHREAD_API _LIBCPP_HAS_THREAD_API_PTHREAD)
713727
config_define_if(LIBCXX_HAS_EXTERNAL_THREAD_API _LIBCPP_HAS_THREAD_API_EXTERNAL)

libcxx/docs/BuildingLibcxx.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,21 @@ The following options allow building libc++ for a different ABI version.
369369
A semicolon-separated list of ABI macros to persist in the site config header.
370370
See ``include/__config`` for the list of ABI macros.
371371

372+
373+
.. option:: LIBCXX_HAS_MERGED_TYPEINFO_NAMES_DEFAULT
374+
375+
**Default**: ``None``. When defined this option overrides the libraries default configuration
376+
for whether merged type info names are present.
377+
378+
379+
Build ``std::type_info`` with the assumption that type info names for a type have been fully
380+
merged are unique across the entire program. This may not be the case for libraries built with
381+
``-Bsymbolic`` or due to compiler or linker bugs (Ex. llvm.org/PR37398).
382+
383+
When the value is ``ON`` typeinfo comparisons compare only the pointer value, otherwise ``strcmp``
384+
is used as a fallback.
385+
386+
372387
.. _LLVM-specific variables:
373388

374389
LLVM-specific options

libcxx/include/__config

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,16 @@ typedef __char32_t char32_t;
775775
# endif
776776
#endif
777777

778+
#ifndef _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT
779+
# ifdef _LIBCPP_OBJECT_FORMAT_COFF // Windows binaries can't merge typeinfos.
780+
# define _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT 0
781+
#else
782+
// TODO: This isn't strictly correct on ELF platforms due to llvm.org/PR37398
783+
// And we should consider defaulting to OFF.
784+
# define _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT 1
785+
#endif
786+
#endif
787+
778788
#ifndef _LIBCPP_HIDE_FROM_ABI
779789
# if _LIBCPP_HIDE_FROM_ABI_PER_TU
780790
# define _LIBCPP_HIDE_FROM_ABI _LIBCPP_HIDDEN _LIBCPP_INTERNAL_LINKAGE
@@ -936,10 +946,6 @@ template <unsigned> struct __static_assert_check {};
936946
#define _LIBCPP_EXTERN_TEMPLATE2(...) extern template __VA_ARGS__;
937947
#endif
938948

939-
#if defined(__APPLE__) && defined(__LP64__) && !defined(__x86_64__)
940-
#define _LIBCPP_NONUNIQUE_RTTI_BIT (1ULL << 63)
941-
#endif
942-
943949
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(_LIBCPP_MSVCRT_LIKE) || \
944950
defined(__sun__) || defined(__NetBSD__) || defined(__CloudABI__)
945951
#define _LIBCPP_LOCALE__L_EXTENSIONS 1

libcxx/include/__config_site.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#cmakedefine _LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL
2828
#cmakedefine _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS
2929
#cmakedefine _LIBCPP_NO_VCRUNTIME
30+
#cmakedefine01 _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT
3031
#cmakedefine _LIBCPP_ABI_NAMESPACE @_LIBCPP_ABI_NAMESPACE@
3132

3233
@_LIBCPP_ABI_DEFINES@

libcxx/include/typeinfo

Lines changed: 146 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,10 @@ public:
7272
#include <vcruntime_typeinfo.h>
7373
#else
7474

75-
#if defined(_LIBCPP_NONUNIQUE_RTTI_BIT) && !defined(_LIBCPP_ABI_MICROSOFT)
76-
# define _LIBCPP_HAS_NONUNIQUE_TYPEINFO
77-
#endif
78-
7975
namespace std // purposefully not using versioning namespace
8076
{
8177

78+
8279
#if defined(_LIBCPP_ABI_MICROSOFT)
8380

8481
class _LIBCPP_EXCEPTION_ABI type_info
@@ -116,8 +113,32 @@ public:
116113
{ return !operator==(__arg); }
117114
};
118115

119-
#elif defined(_LIBCPP_HAS_NONUNIQUE_TYPEINFO)
116+
#else // !defined(_LIBCPP_ABI_MICROSOFT)
120117

118+
// ========================================================================== //
119+
// Implementations
120+
// ========================================================================== //
121+
// ------------------------------------------------------------------------- //
122+
// Unique
123+
// ------------------------------------------------------------------------- //
124+
// This implementation of type_info assumes a unique copy of the RTTI for a
125+
// given type inside a program. This is a valid assumption when abiding to
126+
// Itanium ABI (http://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-components).
127+
// Under this assumption, we can always compare the addresses of the type names
128+
// to implement equality-comparison of type_infos instead of having to perform
129+
// a deep string comparison.
130+
// -------------------------------------------------------------------------- //
131+
// NonUnique
132+
// -------------------------------------------------------------------------- //
133+
// This implementation of type_info does not assume there is always a unique
134+
// copy of the RTTI for a given type inside a program. For various reasons
135+
// the linker may have failed to merge every copy of a types RTTI
136+
// (For example: -Bsymbolic or llvm.org/PR37398). Under this assumption, two
137+
// type_infos are equal if their addresses are equal or if a deep string
138+
// comparison is equal.
139+
// -------------------------------------------------------------------------- //
140+
// NonUniqueARMRTTIBit
141+
// -------------------------------------------------------------------------- //
121142
// This implementation of type_info does not assume always a unique copy of
122143
// the RTTI for a given type inside a program. It packs the pointer to the
123144
// type name into a uintptr_t and reserves the high bit of that pointer (which
@@ -129,135 +150,182 @@ public:
129150
// faster. If at least one of the type_infos can't guarantee uniqueness, we
130151
// have no choice but to fall back to a deep string comparison.
131152
//
153+
// This implementation is specific to ARM64 on Apple platforms.
154+
//
132155
// Note that the compiler is the one setting (or unsetting) the high bit of
133156
// the pointer when it constructs the type_info, depending on whether it can
134157
// guarantee uniqueness for that specific type_info.
135-
class _LIBCPP_EXCEPTION_ABI type_info
136-
{
137-
type_info& operator=(const type_info&);
138-
type_info(const type_info&);
139-
140-
_LIBCPP_INLINE_VISIBILITY
141-
int __compare_nonunique_names(const type_info &__arg) const _NOEXCEPT
142-
{ return __builtin_strcmp(name(), __arg.name()); }
143-
144-
protected:
145-
uintptr_t __type_name;
146-
147-
_LIBCPP_INLINE_VISIBILITY
148-
explicit type_info(const char* __n)
149-
: __type_name(reinterpret_cast<uintptr_t>(__n)) {}
150158

151-
public:
152-
_LIBCPP_AVAILABILITY_TYPEINFO_VTABLE
153-
virtual ~type_info();
154-
155-
_LIBCPP_INLINE_VISIBILITY
156-
const char* name() const _NOEXCEPT
157-
{
158-
return reinterpret_cast<const char*>(__type_name &
159-
~_LIBCPP_NONUNIQUE_RTTI_BIT);
159+
struct __type_info_implementations {
160+
struct __string_impl_base {
161+
typedef const char* __type_name_t;
162+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
163+
_LIBCPP_CONSTEXPR static const char* __type_name_to_string(__type_name_t __v) _NOEXCEPT {
164+
return __v;
160165
}
161-
162-
_LIBCPP_INLINE_VISIBILITY
163-
bool before(const type_info& __arg) const _NOEXCEPT
164-
{
165-
if (!((__type_name & __arg.__type_name) & _LIBCPP_NONUNIQUE_RTTI_BIT))
166-
return __type_name < __arg.__type_name;
167-
return __compare_nonunique_names(__arg) < 0;
166+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
167+
_LIBCPP_CONSTEXPR static __type_name_t __string_to_type_name(const char* __v) _NOEXCEPT {
168+
return __v;
168169
}
170+
};
169171

170-
_LIBCPP_INLINE_VISIBILITY
171-
size_t hash_code() const _NOEXCEPT
172-
{
173-
if (!(__type_name & _LIBCPP_NONUNIQUE_RTTI_BIT))
174-
return __type_name;
172+
struct __unique_impl : __string_impl_base {
173+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
174+
static size_t __hash(__type_name_t __v) _NOEXCEPT {
175+
return reinterpret_cast<size_t>(__v);
176+
}
177+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
178+
static bool __eq(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
179+
return __lhs == __rhs;
180+
}
181+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
182+
static bool __lt(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
183+
return __lhs < __rhs;
184+
}
185+
};
175186

176-
const char* __ptr = name();
187+
struct __non_unique_impl : __string_impl_base {
188+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
189+
static size_t __hash(__type_name_t __ptr) _NOEXCEPT {
177190
size_t __hash = 5381;
178191
while (unsigned char __c = static_cast<unsigned char>(*__ptr++))
179192
__hash = (__hash * 33) ^ __c;
180193
return __hash;
181194
}
195+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
196+
static bool __eq(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
197+
return __lhs == __rhs || __builtin_strcmp(__lhs, __rhs) == 0;
198+
}
199+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
200+
static bool __lt(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
201+
return __builtin_strcmp(__lhs, __rhs) < 0;
202+
}
203+
};
182204

183-
_LIBCPP_INLINE_VISIBILITY
184-
bool operator==(const type_info& __arg) const _NOEXCEPT
185-
{
186-
if (__type_name == __arg.__type_name)
187-
return true;
205+
struct __non_unique_arm_rtti_bit_impl {
206+
typedef uintptr_t __type_name_t;
207+
208+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
209+
static const char* __type_name_to_string(__type_name_t __v) _NOEXCEPT {
210+
return reinterpret_cast<const char*>(__v &
211+
~__non_unique_rtti_bit::value);
212+
}
213+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
214+
static __type_name_t __string_to_type_name(const char* __v) _NOEXCEPT {
215+
return reinterpret_cast<__type_name_t>(__v);
216+
}
188217

189-
if (!((__type_name & __arg.__type_name) & _LIBCPP_NONUNIQUE_RTTI_BIT))
218+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
219+
static size_t __hash(__type_name_t __v) _NOEXCEPT {
220+
if (__is_type_name_unique(__v))
221+
return reinterpret_cast<size_t>(__v);
222+
return __non_unique_impl::__hash(__type_name_to_string(__v));
223+
}
224+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
225+
static bool __eq(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
226+
if (__lhs == __rhs)
227+
return true;
228+
if (__is_type_name_unique(__lhs, __rhs))
190229
return false;
191-
return __compare_nonunique_names(__arg) == 0;
230+
return __builtin_strcmp(__type_name_to_string(__lhs), __type_name_to_string(__rhs)) == 0;
192231
}
232+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_ALWAYS_INLINE
233+
static bool __lt(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
234+
if (__is_type_name_unique(__lhs, __rhs))
235+
return __lhs < __rhs;
236+
return __builtin_strcmp(__type_name_to_string(__lhs), __type_name_to_string(__rhs)) < 0;
237+
}
238+
239+
private:
240+
typedef std::integral_constant<__type_name_t, (1ULL << 63)> __non_unique_rtti_bit;
193241

194242
_LIBCPP_INLINE_VISIBILITY
195-
bool operator!=(const type_info& __arg) const _NOEXCEPT
196-
{ return !operator==(__arg); }
243+
static bool __is_type_name_unique(__type_name_t __lhs) _NOEXCEPT {
244+
return !(__lhs & __non_unique_rtti_bit::value);
245+
}
246+
_LIBCPP_INLINE_VISIBILITY
247+
static bool __is_type_name_unique(__type_name_t __lhs, __type_name_t __rhs) _NOEXCEPT {
248+
return !((__lhs & __rhs) & __non_unique_rtti_bit::value);
249+
}
250+
};
251+
252+
typedef
253+
#if defined(__APPLE__) && defined(__LP64__) && !defined(__x86_64__)
254+
__non_unique_arm_rtti_bit_impl
255+
#elif _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT == 0
256+
__non_unique_impl
257+
#elif _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT == 1
258+
__unique_impl
259+
#else
260+
# error invalid configuration for _LIBCPP_HAS_MERGED_TYPEINFO_NAMES_DEFAULT
261+
#endif
262+
__impl;
197263
};
198264

199-
#else // !_LIBCPP_ABI_MICROSOFT && !_LIBCPP_HAS_NONUNIQUE_TYPEINFO
200-
201-
// This implementation of type_info assumes a unique copy of the RTTI for a
202-
// given type inside a program. This is a valid assumption when abiding to
203-
// Itanium ABI (http://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-components).
204-
// Under this assumption, we can always compare the addresses of the type names
205-
// to implement equality-comparison of type_infos instead of having to perform
206-
// a deep string comparison.
207265
class _LIBCPP_EXCEPTION_ABI type_info
208266
{
209-
type_info& operator=(const type_info&);
210-
type_info(const type_info&);
267+
type_info& operator=(const type_info&);
268+
type_info(const type_info&);
269+
270+
protected:
271+
typedef __type_info_implementations::__impl __impl;
211272

212-
protected:
213-
const char *__type_name;
273+
__impl::__type_name_t __type_name;
214274

215275
_LIBCPP_INLINE_VISIBILITY
216-
explicit type_info(const char* __n) : __type_name(__n) {}
276+
explicit type_info(const char* __n)
277+
: __type_name(__impl::__string_to_type_name(__n)) {}
217278

218279
public:
219280
_LIBCPP_AVAILABILITY_TYPEINFO_VTABLE
220281
virtual ~type_info();
221282

222283
_LIBCPP_INLINE_VISIBILITY
223284
const char* name() const _NOEXCEPT
224-
{ return __type_name; }
285+
{
286+
return __impl::__type_name_to_string(__type_name);
287+
}
225288

226289
_LIBCPP_INLINE_VISIBILITY
227290
bool before(const type_info& __arg) const _NOEXCEPT
228-
{ return __type_name < __arg.__type_name; }
291+
{
292+
return __impl::__lt(__type_name, __arg.__type_name);
293+
}
229294

230295
_LIBCPP_INLINE_VISIBILITY
231296
size_t hash_code() const _NOEXCEPT
232-
{ return reinterpret_cast<size_t>(__type_name); }
297+
{
298+
return __impl::__hash(__type_name);
299+
}
233300

234301
_LIBCPP_INLINE_VISIBILITY
235302
bool operator==(const type_info& __arg) const _NOEXCEPT
236-
{ return __type_name == __arg.__type_name; }
303+
{
304+
return __impl::__eq(__type_name, __arg.__type_name);
305+
}
237306

238307
_LIBCPP_INLINE_VISIBILITY
239308
bool operator!=(const type_info& __arg) const _NOEXCEPT
240309
{ return !operator==(__arg); }
241310
};
242-
243-
#endif
311+
#endif // defined(_LIBCPP_ABI_MICROSOFT)
244312

245313
class _LIBCPP_EXCEPTION_ABI bad_cast
246314
: public exception
247315
{
248-
public:
249-
bad_cast() _NOEXCEPT;
250-
virtual ~bad_cast() _NOEXCEPT;
251-
virtual const char* what() const _NOEXCEPT;
316+
public:
317+
bad_cast() _NOEXCEPT;
318+
virtual ~bad_cast() _NOEXCEPT;
319+
virtual const char* what() const _NOEXCEPT;
252320
};
253321

254322
class _LIBCPP_EXCEPTION_ABI bad_typeid
255323
: public exception
256324
{
257-
public:
258-
bad_typeid() _NOEXCEPT;
259-
virtual ~bad_typeid() _NOEXCEPT;
260-
virtual const char* what() const _NOEXCEPT;
325+
public:
326+
bad_typeid() _NOEXCEPT;
327+
virtual ~bad_typeid() _NOEXCEPT;
328+
virtual const char* what() const _NOEXCEPT;
261329
};
262330

263331
} // std

0 commit comments

Comments
 (0)