Skip to content

Commit

Permalink
[libc++] Add a lightweight overridable assertion handler
Browse files Browse the repository at this point in the history
This patch adds a lightweight assertion handler mechanism that can be
overriden at link-time in a fashion similar to `operator new`.

This is a third take on https://llvm.org/D121123 (which allowed customizing
the assertion handler at compile-time), and https://llvm.org/D119969
(which allowed customizing the assertion handler at runtime only).

This approach is, I think, the best of all three explored approaches.
Indeed, replacing the assertion handler in user code is ergonomic,
yet we retain the ability to provide a custom assertion handler when
deploying to older platforms that don't have a default handler in
the dylib.

As-is, this patch provides a pretty good amount of backwards compatibility
with the previous debug mode:

- Code that used to set _LIBCPP_DEBUG=0 in order to get basic assertions
  in their code will still get basic assertions out of the box, but
  those assertions will be using the new assertion handler support.
- Code that was previously compiled with references to __libcpp_debug_function
  and friends will work out-of-the-box, no changes required. This is
  because we provide the same symbols in the dylib as we used to.
- Code that used to set a custom __libcpp_debug_function will stop
  compiling, because we don't provide that declaration anymore. Users
  will have to migrate to the new way of setting a custom assertion
  handler, which is extremely easy. I suspect that pool of users is
  very limited, so breaking them at compile-time is probably acceptable.

The main downside of this approach is that code being compiled with
assertions enabled but deploying to an older platform where the assertion
handler didn't exist yet will fail to compile. However users can easily
fix the problem by providing a custom assertion handler and defining
the _LIBCPP_AVAILABILITY_CUSTOM_ASSERTION_HANDLER_PROVIDED macro to
let the library know about the custom handler. In a way, this is
actually a feature because it avoids a load-time error that one would
otherwise get when trying to run the code on the older target.

Differential Revision: https://reviews.llvm.org/D121478
  • Loading branch information
ldionne committed Mar 23, 2022
1 parent b6efd25 commit b0fd949
Show file tree
Hide file tree
Showing 75 changed files with 550 additions and 229 deletions.
11 changes: 9 additions & 2 deletions libcxx/CMakeLists.txt
Expand Up @@ -82,7 +82,10 @@ include(CMakeDependentOption)
include(HandleCompilerRT)

# Basic options ---------------------------------------------------------------
option(LIBCXX_ENABLE_ASSERTIONS "Enable assertions independent of build mode." OFF)
option(LIBCXX_ENABLE_ASSERTIONS
"Enable assertions inside the compiled library, and at the same time make it the
default when compiling user code. Note that assertions can be enabled or disabled
by users in their own code regardless of this option." OFF)
option(LIBCXX_ENABLE_SHARED "Build libc++ as a shared library." ON)
option(LIBCXX_ENABLE_STATIC "Build libc++ as a static library." ON)
option(LIBCXX_ENABLE_EXPERIMENTAL_LIBRARY "Build libc++experimental.a" ON)
Expand Down Expand Up @@ -694,7 +697,6 @@ if (LIBCXX_BUILD_EXTERNAL_THREAD_LIBRARY AND LIBCXX_ENABLE_SHARED)
endif()

# Assertion flags =============================================================
define_if(LIBCXX_ENABLE_ASSERTIONS -D_LIBCPP_DEBUG=0)
define_if(LIBCXX_DEBUG_BUILD -D_DEBUG)
if (LIBCXX_ENABLE_ASSERTIONS AND NOT LIBCXX_DEBUG_BUILD)
# MSVC doesn't like _DEBUG on release builds. See PR 4379.
Expand Down Expand Up @@ -896,6 +898,11 @@ config_define_if_not(LIBCXX_ENABLE_LOCALIZATION _LIBCPP_HAS_NO_LOCALIZATION)
config_define_if_not(LIBCXX_ENABLE_UNICODE _LIBCPP_HAS_NO_UNICODE)
config_define_if_not(LIBCXX_ENABLE_WIDE_CHARACTERS _LIBCPP_HAS_NO_WIDE_CHARACTERS)
config_define_if_not(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS _LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS)
if (LIBCXX_ENABLE_ASSERTIONS)
config_define(1 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT)
else()
config_define(0 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT)
endif()
# Incomplete features get their own specific disabling flags. This makes it
# easier to grep for target specific flags once the feature is complete.
config_define_if_not(LIBCXX_ENABLE_INCOMPLETE_FEATURES _LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
Expand Down
2 changes: 2 additions & 0 deletions libcxx/cmake/caches/Generic-assertions.cmake
@@ -1 +1,3 @@
set(LIBCXX_ENABLE_ASSERTIONS ON CACHE BOOL "")
set(LIBCXX_TEST_PARAMS "enable_assertions=True" CACHE STRING "")
set(LIBCXXABI_TEST_PARAMS "enable_assertions=True" CACHE STRING "")
5 changes: 4 additions & 1 deletion libcxx/docs/BuildingLibcxx.rst
Expand Up @@ -216,7 +216,10 @@ libc++ specific options

**Default**: ``OFF``

Build libc++ with assertions enabled.
Build libc++ with assertions enabled in the compiled library, and enable assertions
by default when building user code as well. Assertions can be turned off by users
by defining ``_LIBCPP_ENABLE_ASSERTIONS=0``. For details, see
:ref:`the documentation <assertions-mode>`.

.. option:: LIBCXX_ENABLE_SHARED:BOOL

Expand Down
10 changes: 10 additions & 0 deletions libcxx/docs/ReleaseNotes.rst
Expand Up @@ -46,6 +46,12 @@ New Features
"heapsort with bounce" to reduce the number of comparisons, and rearranges
elements using move-assignment instead of `swap`.

- Libc++ now supports a variety of assertions that can be turned on to help catch
undefined behavior in user code. This new support is now separate from the old
(and incomplete) Debug Mode. Vendors can select whether the library they ship
should include assertions or not by default. For details, see
:ref:`the documentation <assertions-mode>` about this new feature.

API Changes
-----------

Expand Down Expand Up @@ -74,6 +80,10 @@ API Changes
- The C++14 function ``std::quoted(const char*)`` is no longer supported in
C++03 or C++11 modes.

- Setting a custom debug handler with ``std::__libcpp_debug_function`` is not
supported anymore. Please migrate to using the new support for
:ref:`assertions <assertions-mode>` instead.

ABI Changes
-----------

Expand Down
86 changes: 86 additions & 0 deletions libcxx/docs/UsingLibcxx.rst
Expand Up @@ -121,6 +121,92 @@ provide pretty-printers itself. Those can be used as:
<args>
.. _assertions-mode:

Enabling the "safe libc++" mode
===============================

Libc++ contains a number of assertions whose goal is to catch undefined behavior in the
library, usually caused by precondition violations. Those assertions do not aim to be
exhaustive -- instead they aim to provide a good balance between safety and performance.
In particular, these assertions do not change the complexity of algorithms. However, they
might, in some cases, interfere with compiler optimizations.

By default, these assertions are turned off. Vendors can decide to turn them on while building
the compiled library by defining ``LIBCXX_ENABLE_ASSERTIONS=ON`` at CMake configuration time.
When ``LIBCXX_ENABLE_ASSERTIONS`` is used, the compiled library will be built with assertions
enabled, **and** user code will be built with assertions enabled by default. If
``LIBCXX_ENABLE_ASSERTIONS=OFF`` at CMake configure time, the compiled library will not contain
assertions and the default when building user code will be to have assertions disabled.
As a user, you can consult your vendor to know whether assertions are enabled by default.

Furthermore, independently of any vendor-selected default, users can always control whether
assertions are enabled in their code by defining ``_LIBCPP_ENABLE_ASSERTIONS=0|1`` before
including any libc++ header (we recommend passing ``-D_LIBCPP_ENABLE_ASSERTIONS=X`` to the
compiler). Note that if the compiled library was built by the vendor without assertions,
functions compiled inside the static or shared library won't have assertions enabled even
if the user defines ``_LIBCPP_ENABLE_ASSERTIONS=1`` (the same is true for the inverse case
where the static or shared library was compiled **with** assertions but the user tries to
disable them). However, most of the code in libc++ is in the headers, so the user-selected
value for ``_LIBCPP_ENABLE_ASSERTIONS`` (if any) will usually be respected.

When an assertion fails, an assertion handler function is called. The library provides a default
assertion handler that prints an error message and calls ``std::abort()``. Note that this assertion
handler is provided by the static or shared library, so it is only available when deploying to a
platform where the compiled library is sufficiently recent. However, users can also override that
assertion handler with their own, which can be useful to provide custom behavior, or when deploying
to older platforms where the default assertion handler isn't available.

Replacing the default assertion handler is done by defining the following function:

.. code-block:: cpp
void __libcpp_assertion_handler(char const* file, int line, char const* expression, char const* message)
This mechanism is similar to how one can replace the default definition of ``operator new``
and ``operator delete``. For example:

.. code-block:: cpp
// In HelloWorldHandler.cpp
#include <__assert> // must include <__assert> before defining the handler
void std::__libcpp_assertion_handler(char const* file, int line, char const* expression, char const* message) {
std::printf("Assertion %s failed at %s:%d, more info: %s", expression, file, line, message);
std::abort();
}
// In HelloWorld.cpp
#include <vector>
int main() {
std::vector<int> v;
int& x = v[0]; // Your assertion handler will be called here if _LIBCPP_ENABLE_ASSERTIONS=1
}
Also note that the assertion handler should usually not return. Since the assertions in libc++
catch undefined behavior, your code will proceed with undefined behavior if your assertion
handler is called and does return.

Furthermore, throwing an exception from the assertion handler is not recommended. Indeed, many
functions in the library are ``noexcept``, and any exception thrown from the assertion handler
will result in ``std::terminate`` being called.

Back-deploying with a custom assertion handler
----------------------------------------------
When deploying to an older platform that does not provide a default assertion handler, the
compiler will diagnose the usage of ``std::__libcpp_assertion_handler`` with an error. This
is done to avoid the load-time error that would otherwise happen if the code was being deployed
on the older system.

If you are providing a custom assertion handler, this error is effectively a false positive.
To let the library know that you are providing a custom assertion handler in back-deployment
scenarios, you must define the ``_LIBCPP_AVAILABILITY_CUSTOM_ASSERTION_HANDLER_PROVIDED`` macro,
and the library will assume that you are providing your own definition. If no definition is
provided and the code is back-deployed to the older platform, it will fail to load when the
dynamic linker fails to find a definition for ``std::__libcpp_assertion_handler``, so you
should only remove the guard rails if you really mean it!

Libc++ Configuration Macros
===========================

Expand Down
55 changes: 21 additions & 34 deletions libcxx/include/__assert
Expand Up @@ -10,52 +10,39 @@
#ifndef _LIBCPP___ASSERT
#define _LIBCPP___ASSERT

#include <__availability>
#include <__config>
#include <iosfwd> // for std::string

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

// This is for backwards compatibility with code that might have been enabling
// assertions through the Debug mode previously.
#if _LIBCPP_DEBUG_LEVEL >= 1
# define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : ::std::__libcpp_debug_function(::std::__libcpp_debug_info(__FILE__, __LINE__, #x, m)))
# ifndef _LIBCPP_ENABLE_ASSERTIONS
# define _LIBCPP_ENABLE_ASSERTIONS 1
# endif
#endif

#ifndef _LIBCPP_ENABLE_ASSERTIONS
# define _LIBCPP_ENABLE_ASSERTIONS _LIBCPP_ENABLE_ASSERTIONS_DEFAULT
#endif

#if _LIBCPP_ENABLE_ASSERTIONS != 0 && _LIBCPP_ENABLE_ASSERTIONS != 1
# error "_LIBCPP_ENABLE_ASSERTIONS must be set to 0 or 1"
#endif

#if _LIBCPP_ENABLE_ASSERTIONS
# define _LIBCPP_ASSERT(expression, message) ((expression) ? (void)0 : ::std::__libcpp_assertion_handler(__FILE__, __LINE__, #expression, message))
#else
# define _LIBCPP_ASSERT(x, m) ((void)0)
# define _LIBCPP_ASSERT(x, m) ((void)0)
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

struct _LIBCPP_TEMPLATE_VIS __libcpp_debug_info {
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
__libcpp_debug_info()
: __file_(nullptr), __line_(-1), __pred_(nullptr), __msg_(nullptr) {}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
__libcpp_debug_info(const char* __f, int __l, const char* __p, const char* __m)
: __file_(__f), __line_(__l), __pred_(__p), __msg_(__m) {}

_LIBCPP_FUNC_VIS string what() const;

const char* __file_;
int __line_;
const char* __pred_;
const char* __msg_;
};

/// __libcpp_debug_function_type - The type of the assertion failure handler.
typedef void(*__libcpp_debug_function_type)(__libcpp_debug_info const&);

/// __libcpp_debug_function - The handler function called when a _LIBCPP_ASSERT
/// fails.
extern _LIBCPP_EXPORTED_FROM_ABI __libcpp_debug_function_type __libcpp_debug_function;

/// __libcpp_abort_debug_function - A debug handler that aborts when called.
_LIBCPP_NORETURN _LIBCPP_FUNC_VIS
void __libcpp_abort_debug_function(__libcpp_debug_info const&);

/// __libcpp_set_debug_function - Set the debug handler to the specified
/// function.
_LIBCPP_FUNC_VIS
bool __libcpp_set_debug_function(__libcpp_debug_function_type __func);
_LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_ASSERTION_HANDLER
void __libcpp_assertion_handler(char const* __file, int __line, char const* __expression, char const* __message);

_LIBCPP_END_NAMESPACE_STD

Expand Down
30 changes: 30 additions & 0 deletions libcxx/include/__availability
Expand Up @@ -159,6 +159,23 @@
# define _LIBCPP_AVAILABILITY_FORMAT
// # define _LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format

// This controls whether the std::__libcpp_assertion_handler default
// assertion handler is provided by the library.
//
// Note that when users provide their own custom assertion handler,
// it doesn't matter whether the dylib provides a default handler,
// and the availability markup can actually give a false positive
// diagnostic (it will think that no handler is provided, when in
// reality the user has provided their own).
//
// Users can pass -D_LIBCPP_AVAILABILITY_CUSTOM_ASSERTION_HANDLER_PROVIDED
// to the compiler to tell the library to ignore the fact that the
// default handler isn't available on their deployment target. Note that
// defining this macro but failing to define a custom assertion handler
// will lead to a load-time error on back-deployment targets, so it
// should be avoided.
# define _LIBCPP_AVAILABILITY_DEFAULT_ASSERTION_HANDLER

#elif defined(__APPLE__)

# define _LIBCPP_AVAILABILITY_SHARED_MUTEX \
Expand Down Expand Up @@ -260,6 +277,9 @@
# define _LIBCPP_AVAILABILITY_FORMAT \
__attribute__((unavailable))
# define _LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format

# define _LIBCPP_AVAILABILITY_DEFAULT_ASSERTION_HANDLER \
__attribute__((unavailable))
#else

// ...New vendors can add availability markup here...
Expand All @@ -283,4 +303,14 @@
# define _LIBCPP_AVAILABILITY_THROW_BAD_VARIANT_ACCESS _LIBCPP_AVAILABILITY_BAD_VARIANT_ACCESS
#endif

// Define the special assertion handler availability attribute, which can be silenced by
// users if they provide their own custom assertion handler. The rest of the code should
// not use the *_DEFAULT_* macro directly, since that would make it ignore the fact that
// the user provided a custom handler.
#if defined(_LIBCPP_AVAILABILITY_CUSTOM_ASSERTION_HANDLER_PROVIDED)
# define _LIBCPP_AVAILABILITY_ASSERTION_HANDLER /* nothing */
#else
# define _LIBCPP_AVAILABILITY_ASSERTION_HANDLER _LIBCPP_AVAILABILITY_DEFAULT_ASSERTION_HANDLER
#endif

#endif // _LIBCPP___AVAILABILITY
1 change: 1 addition & 0 deletions libcxx/include/__config_site.in
Expand Up @@ -32,6 +32,7 @@
#cmakedefine _LIBCPP_HAS_NO_WIDE_CHARACTERS
#cmakedefine _LIBCPP_HAS_NO_INCOMPLETE_FORMAT
#cmakedefine _LIBCPP_HAS_NO_INCOMPLETE_RANGES
#cmakedefine01 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT

// __USE_MINGW_ANSI_STDIO gets redefined on MinGW
#ifdef __clang__
Expand Down
Expand Up @@ -1561,6 +1561,7 @@
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_debug_functionE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'}
Expand Down
Expand Up @@ -1534,6 +1534,7 @@
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'}
Expand Down
Expand Up @@ -1561,6 +1561,7 @@
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_debug_functionE', 'size': 0, 'type': 'OBJECT'}
{'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'}
Expand Down
Expand Up @@ -1534,6 +1534,7 @@
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'}
Expand Down
Expand Up @@ -1252,6 +1252,7 @@
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_debug_functionE', 'size': 8, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'}
Expand Down
Expand Up @@ -1249,6 +1249,7 @@
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_debug_functionE', 'size': 8, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'}
Expand Down
Expand Up @@ -1228,6 +1228,7 @@
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'}
Expand Down
Expand Up @@ -1224,6 +1224,7 @@
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_debug_functionE', 'size': 8, 'type': 'OBJECT'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__126__libcpp_assertion_handlerEPKciS1_S1_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIaaEEPaEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIccEEPcEEbT0_S5_T_', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__127__insertion_sort_incompleteIRNS_6__lessIddEEPdEEbT0_S5_T_', 'type': 'FUNC'}
Expand Down

0 comments on commit b0fd949

Please sign in to comment.