[libc++][hardening] Introduce assertion semantics. (#149459)
Assertion semantics closely mimic C++26 Contracts evaluation semantics. This brings our implementation closer in line with C++26 Library Hardening (one particular benefit is that using the `observe` semantic makes adopting hardening easier for projects).
This commit is contained in:
parent
59013d4405
commit
3eee9fc2c4
1
.github/workflows/libcxx-build-and-test.yaml
vendored
1
.github/workflows/libcxx-build-and-test.yaml
vendored
@ -128,6 +128,7 @@ jobs:
|
|||||||
'generic-abi-unstable',
|
'generic-abi-unstable',
|
||||||
'generic-hardening-mode-debug',
|
'generic-hardening-mode-debug',
|
||||||
'generic-hardening-mode-extensive',
|
'generic-hardening-mode-extensive',
|
||||||
|
'generic-hardening-mode-extensive-observe-semantic',
|
||||||
'generic-hardening-mode-fast',
|
'generic-hardening-mode-fast',
|
||||||
'generic-hardening-mode-fast-with-abi-breaks',
|
'generic-hardening-mode-fast-with-abi-breaks',
|
||||||
'generic-merged',
|
'generic-merged',
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
set(LIBCXX_HARDENING_MODE "extensive" CACHE STRING "")
|
||||||
|
set(LIBCXX_TEST_PARAMS "assertion_semantic=observe" CACHE STRING "")
|
@ -39,6 +39,8 @@ modes are:
|
|||||||
|
|
||||||
Enabling hardening has no impact on the ABI.
|
Enabling hardening has no impact on the ABI.
|
||||||
|
|
||||||
|
.. _notes-for-users:
|
||||||
|
|
||||||
Notes for users
|
Notes for users
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
@ -72,6 +74,10 @@ to control the level by passing **one** of the following options to the compiler
|
|||||||
pre-built components. Most libc++ code is header-based, so a user-provided
|
pre-built components. Most libc++ code is header-based, so a user-provided
|
||||||
value for ``_LIBCPP_HARDENING_MODE`` will be mostly respected.
|
value for ``_LIBCPP_HARDENING_MODE`` will be mostly respected.
|
||||||
|
|
||||||
|
In some cases, users might want to override the assertion semantic used by the
|
||||||
|
library. This can be done similarly to setting the hardening mode; please refer
|
||||||
|
to the :ref:`relevant section <assertion-semantics>`.
|
||||||
|
|
||||||
Notes for vendors
|
Notes for vendors
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
@ -260,6 +266,68 @@ output. This is less secure and increases the size of the binary (among other
|
|||||||
things, it has to store the error message strings) but makes the failure easier
|
things, it has to store the error message strings) but makes the failure easier
|
||||||
to debug. It also allows testing the error messages in our test suite.
|
to debug. It also allows testing the error messages in our test suite.
|
||||||
|
|
||||||
|
This default behavior can be customized by users via :ref:`assertion semantics
|
||||||
|
<assertion-semantics>`; it can also be completely overridden by vendors by
|
||||||
|
providing a :ref:`custom assertion failure handler
|
||||||
|
<override-assertion-handler>`.
|
||||||
|
|
||||||
|
.. _assertion-semantics:
|
||||||
|
|
||||||
|
Assertion semantics
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Assertion semantics are currently an experimental feature.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Assertion semantics are not available in the C++03 mode.
|
||||||
|
|
||||||
|
What happens when an assertion fails depends on the assertion semantic being
|
||||||
|
used. Four assertion semantics are available, based on C++26 Contracts
|
||||||
|
evaluation semantics:
|
||||||
|
|
||||||
|
- ``ignore`` evaluates the assertion but has no effect if it fails (note that it
|
||||||
|
differs from the Contracts ``ignore`` semantic which would not evaluate
|
||||||
|
the assertion at all);
|
||||||
|
- ``observe`` logs an error (indicating, if possible on the platform, that the
|
||||||
|
error is fatal) but continues execution;
|
||||||
|
- ``quick-enforce`` terminates the program as fast as possible via a trap
|
||||||
|
instruction. It is the default semantic for the production modes (``fast`` and
|
||||||
|
``extensive``);
|
||||||
|
- ``enforce`` logs an error and then terminates the program. It is the default
|
||||||
|
semantic for the ``debug`` mode.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- Continuing execution after a hardening check fails results in undefined
|
||||||
|
behavior; the ``observe`` semantic is meant to make adopting hardening easier
|
||||||
|
but should not be used outside of the adoption period;
|
||||||
|
- C++26 wording for Library Hardening precludes a conforming Hardened
|
||||||
|
implementation from using the Contracts ``ignore`` semantic when evaluating
|
||||||
|
hardened preconditions in the Library. Libc++ allows using this semantic for
|
||||||
|
hardened preconditions, but please be aware that using ``ignore`` does not
|
||||||
|
produce a conforming "Hardened" implementation, unlike the other semantics
|
||||||
|
above.
|
||||||
|
|
||||||
|
The default assertion semantics are as follows:
|
||||||
|
|
||||||
|
- ``fast``: ``quick-enforce``;
|
||||||
|
- ``extensive``: ``quick-enforce``;
|
||||||
|
- ``debug``: ``enforce``.
|
||||||
|
|
||||||
|
The default assertion semantics can be overridden by passing **one** of the
|
||||||
|
following options to the compiler:
|
||||||
|
|
||||||
|
- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_IGNORE``
|
||||||
|
- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_OBSERVE``
|
||||||
|
- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE``
|
||||||
|
- ``-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE``
|
||||||
|
|
||||||
|
All the :ref:`same notes <notes-for-users>` apply to setting this macro as for
|
||||||
|
setting ``_LIBCPP_HARDENING_MODE``.
|
||||||
|
|
||||||
.. _override-assertion-handler:
|
.. _override-assertion-handler:
|
||||||
|
|
||||||
Overriding the assertion failure handler
|
Overriding the assertion failure handler
|
||||||
|
@ -88,6 +88,12 @@ Improvements and New Features
|
|||||||
|
|
||||||
- ``ctype::tolower`` and ``ctype::toupper`` have been optimized, resulting in a 2x performance improvement.
|
- ``ctype::tolower`` and ``ctype::toupper`` have been optimized, resulting in a 2x performance improvement.
|
||||||
|
|
||||||
|
- As an experimental feature, Hardening now supports assertion semantics that allow customizing how a hardening
|
||||||
|
assertion failure is handled. The four available semantics, modeled on C++26 Contracts, are ``ignore``, ``observe``,
|
||||||
|
``quick-enforce`` and ``enforce``. The ``observe`` semantic is intended to make it easier to adopt Hardening in
|
||||||
|
production but should not be used outside of this scenario. Please refer to the :ref:`Hardening documentation
|
||||||
|
<hardening>` for details.
|
||||||
|
|
||||||
Deprecations and Removals
|
Deprecations and Removals
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
@ -72,6 +72,11 @@ when ``-fexperimental-library`` is passed:
|
|||||||
* ``std::chrono::tzdb`` and related time zone functionality
|
* ``std::chrono::tzdb`` and related time zone functionality
|
||||||
* ``<syncstream>``
|
* ``<syncstream>``
|
||||||
|
|
||||||
|
Additionally, assertion semantics are an experimental feature that can be used
|
||||||
|
to customize the behavior of Hardening (see :ref:`here <assertion-semantics>`).
|
||||||
|
Assertion semantics mirror the evaluation semantics of C++26 Contracts but are
|
||||||
|
not a standard feature.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Experimental libraries are experimental.
|
Experimental libraries are experimental.
|
||||||
* The contents of the ``<experimental/...>`` headers and the associated static
|
* The contents of the ``<experimental/...>`` headers and the associated static
|
||||||
|
@ -38,6 +38,30 @@
|
|||||||
# define _LIBCPP_FREESTANDING
|
# define _LIBCPP_FREESTANDING
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(libcpp-cpp-version-check)
|
||||||
|
# if __cplusplus < 201103L
|
||||||
|
# define _LIBCPP_CXX03_LANG
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# if __has_feature(experimental_library)
|
||||||
|
# ifndef _LIBCPP_ENABLE_EXPERIMENTAL
|
||||||
|
# define _LIBCPP_ENABLE_EXPERIMENTAL
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
// Incomplete features get their own specific disabling flags. This makes it
|
||||||
|
// easier to grep for target specific flags once the feature is complete.
|
||||||
|
# if defined(_LIBCPP_ENABLE_EXPERIMENTAL) || defined(_LIBCPP_BUILDING_LIBRARY)
|
||||||
|
# define _LIBCPP_HAS_EXPERIMENTAL_LIBRARY 1
|
||||||
|
# else
|
||||||
|
# define _LIBCPP_HAS_EXPERIMENTAL_LIBRARY 0
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# define _LIBCPP_HAS_EXPERIMENTAL_PSTL _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
|
||||||
|
# define _LIBCPP_HAS_EXPERIMENTAL_TZDB _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
|
||||||
|
# define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
|
||||||
|
# define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
|
||||||
|
|
||||||
// HARDENING {
|
// HARDENING {
|
||||||
|
|
||||||
// TODO: Remove in LLVM 21. We're making this an error to catch folks who might not have migrated.
|
// TODO: Remove in LLVM 21. We're making this an error to catch folks who might not have migrated.
|
||||||
@ -147,16 +171,53 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \
|
|||||||
_LIBCPP_HARDENING_MODE_DEBUG
|
_LIBCPP_HARDENING_MODE_DEBUG
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
// Hardening assertion semantics generally mirror the evaluation semantics of C++26 Contracts:
|
||||||
|
// - `ignore` evaluates the assertion but doesn't do anything if it fails (note that it differs from the Contracts
|
||||||
|
// `ignore` semantic which wouldn't evaluate the assertion at all);
|
||||||
|
// - `observe` logs an error (indicating, if possible, that the error is fatal) and continues execution;
|
||||||
|
// - `quick-enforce` terminates the program as fast as possible (via trapping);
|
||||||
|
// - `enforce` logs an error and then terminates the program.
|
||||||
|
//
|
||||||
|
// Notes:
|
||||||
|
// - Continuing execution after a hardening check fails results in undefined behavior; the `observe` semantic is meant
|
||||||
|
// to make adopting hardening easier but should not be used outside of this scenario;
|
||||||
|
// - C++26 wording for Library Hardening precludes a conforming Hardened implementation from using the Contracts
|
||||||
|
// `ignore` semantic when evaluating hardened preconditions in the Library. Libc++ allows using this semantic for
|
||||||
|
// hardened preconditions, however, be aware that using `ignore` does not produce a conforming "Hardened"
|
||||||
|
// implementation, unlike the other semantics above.
|
||||||
|
// clang-format off
|
||||||
|
# define _LIBCPP_ASSERTION_SEMANTIC_IGNORE (1 << 1)
|
||||||
|
# define _LIBCPP_ASSERTION_SEMANTIC_OBSERVE (1 << 2)
|
||||||
|
# define _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE (1 << 3)
|
||||||
|
# define _LIBCPP_ASSERTION_SEMANTIC_ENFORCE (1 << 4)
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
// Allow users to define an arbitrary assertion semantic; otherwise, use the default mapping from modes to semantics.
|
||||||
|
// The default is for production-capable modes to use `quick-enforce` (i.e., trap) and for the `debug` mode to use
|
||||||
|
// `enforce` (i.e., log and abort).
|
||||||
|
# ifndef _LIBCPP_ASSERTION_SEMANTIC
|
||||||
|
|
||||||
|
# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
|
||||||
|
# define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
|
||||||
|
# else
|
||||||
|
# define _LIBCPP_ASSERTION_SEMANTIC _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# else
|
||||||
|
# if !_LIBCPP_HAS_EXPERIMENTAL_LIBRARY
|
||||||
|
# error "Assertion semantics are an experimental feature."
|
||||||
|
# endif
|
||||||
|
# if defined(_LIBCPP_CXX03_LANG)
|
||||||
|
# error "Assertion semantics are not available in the C++03 mode."
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# endif // _LIBCPP_ASSERTION_SEMANTIC
|
||||||
|
|
||||||
// } HARDENING
|
// } HARDENING
|
||||||
|
|
||||||
# define _LIBCPP_TOSTRING2(x) #x
|
# define _LIBCPP_TOSTRING2(x) #x
|
||||||
# define _LIBCPP_TOSTRING(x) _LIBCPP_TOSTRING2(x)
|
# define _LIBCPP_TOSTRING(x) _LIBCPP_TOSTRING2(x)
|
||||||
|
|
||||||
// NOLINTNEXTLINE(libcpp-cpp-version-check)
|
|
||||||
# if __cplusplus < 201103L
|
|
||||||
# define _LIBCPP_CXX03_LANG
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# ifndef __has_constexpr_builtin
|
# ifndef __has_constexpr_builtin
|
||||||
# define __has_constexpr_builtin(x) 0
|
# define __has_constexpr_builtin(x) 0
|
||||||
# endif
|
# endif
|
||||||
@ -190,25 +251,6 @@ _LIBCPP_HARDENING_MODE_DEBUG
|
|||||||
# define _LIBCPP_ABI_VCRUNTIME
|
# define _LIBCPP_ABI_VCRUNTIME
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
# if __has_feature(experimental_library)
|
|
||||||
# ifndef _LIBCPP_ENABLE_EXPERIMENTAL
|
|
||||||
# define _LIBCPP_ENABLE_EXPERIMENTAL
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
|
|
||||||
// Incomplete features get their own specific disabling flags. This makes it
|
|
||||||
// easier to grep for target specific flags once the feature is complete.
|
|
||||||
# if defined(_LIBCPP_ENABLE_EXPERIMENTAL) || defined(_LIBCPP_BUILDING_LIBRARY)
|
|
||||||
# define _LIBCPP_HAS_EXPERIMENTAL_LIBRARY 1
|
|
||||||
# else
|
|
||||||
# define _LIBCPP_HAS_EXPERIMENTAL_LIBRARY 0
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# define _LIBCPP_HAS_EXPERIMENTAL_PSTL _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
|
|
||||||
# define _LIBCPP_HAS_EXPERIMENTAL_TZDB _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
|
|
||||||
# define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
|
|
||||||
# define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
|
|
||||||
|
|
||||||
# if defined(__MVS__)
|
# if defined(__MVS__)
|
||||||
# include <features.h> // for __NATIVE_ASCII_F
|
# include <features.h> // for __NATIVE_ASCII_F
|
||||||
# endif
|
# endif
|
||||||
|
@ -152,6 +152,10 @@ _LIBCPP_HARDENING_MODE_EXTENSIVE, \
|
|||||||
_LIBCPP_HARDENING_MODE_DEBUG
|
_LIBCPP_HARDENING_MODE_DEBUG
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
# ifdef _LIBCPP_ASSERTION_SEMANTIC
|
||||||
|
# error "Assertion semantics are not available in the C++03 mode."
|
||||||
|
# endif
|
||||||
|
|
||||||
// } HARDENING
|
// } HARDENING
|
||||||
|
|
||||||
# define _LIBCPP_TOSTRING2(x) #x
|
# define _LIBCPP_TOSTRING2(x) #x
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
// UNSUPPORTED: no-threads
|
// UNSUPPORTED: no-threads
|
||||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||||
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
|
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
|
||||||
|
// Without the assertion, the test will most likely time out.
|
||||||
|
// UNSUPPORTED: libcpp-assertion-semantic={{ignore|observe}}
|
||||||
|
|
||||||
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
|
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
// REQUIRES: has-unix-headers
|
// REQUIRES: has-unix-headers
|
||||||
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
|
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
|
||||||
|
// Without the assertion, the test will most likely time out.
|
||||||
|
// UNSUPPORTED: libcpp-assertion-semantic={{ignore|observe}}
|
||||||
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
|
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
|
||||||
|
|
||||||
#include <latch>
|
#include <latch>
|
||||||
|
@ -44,15 +44,32 @@ static constexpr const char* Marker = "###";
|
|||||||
using MatchResult = std::pair<bool, std::string>;
|
using MatchResult = std::pair<bool, std::string>;
|
||||||
using Matcher = std::function<MatchResult(const std::string& /*text*/)>;
|
using Matcher = std::function<MatchResult(const std::string& /*text*/)>;
|
||||||
|
|
||||||
MatchResult MatchAssertionMessage(const std::string& text, std::string_view expected_message) {
|
// Using the marker makes matching more precise, but we cannot output the marker when the `observe` semantic is used
|
||||||
|
// (because it doesn't allow customizing the logging function). If the marker is not available, fall back to using less
|
||||||
|
// precise matching by just the error message.
|
||||||
|
MatchResult MatchAssertionMessage(const std::string& text, std::string_view expected_message, bool use_marker) {
|
||||||
// Extract information from the error message. This has to stay synchronized with how we format assertions in the
|
// Extract information from the error message. This has to stay synchronized with how we format assertions in the
|
||||||
// library.
|
// library.
|
||||||
std::regex assertion_format(".*###\\n(.*):(\\d+): assertion (.*) failed: (.*)\\n###");
|
std::string assertion_format_string = [&] {
|
||||||
|
if (use_marker)
|
||||||
|
return (".*###\\n(.*):(\\d+): assertion (.*) failed: (.*)\\n###");
|
||||||
|
return ("(.*):(\\d+): assertion (.*) failed: (.*)\\n");
|
||||||
|
}();
|
||||||
|
std::regex assertion_format(assertion_format_string);
|
||||||
|
|
||||||
std::smatch match_result;
|
std::smatch match_result;
|
||||||
bool has_match = std::regex_match(text, match_result, assertion_format);
|
// If a non-terminating assertion semantic is used, more than one assertion might be triggered before the process
|
||||||
assert(has_match);
|
// dies, so we cannot expect the entire target string to match.
|
||||||
assert(match_result.size() == 5);
|
bool has_match = std::regex_search(text, match_result, assertion_format);
|
||||||
|
if (!has_match || match_result.size() != 5) {
|
||||||
|
std::stringstream matching_error;
|
||||||
|
matching_error //
|
||||||
|
<< "Failed to parse the assertion message.\n" //
|
||||||
|
<< "Using marker: " << use_marker << "\n" //
|
||||||
|
<< "Expected message: '" << expected_message.data() << "'\n" //
|
||||||
|
<< "Stderr contents: '" << text.c_str() << "'\n";
|
||||||
|
return MatchResult(/*success=*/false, matching_error.str());
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& file = match_result[1];
|
const std::string& file = match_result[1];
|
||||||
int line = std::stoi(match_result[2]);
|
int line = std::stoi(match_result[2]);
|
||||||
@ -72,9 +89,9 @@ MatchResult MatchAssertionMessage(const std::string& text, std::string_view expe
|
|||||||
return MatchResult(/*success=*/true, /*maybe_error=*/"");
|
return MatchResult(/*success=*/true, /*maybe_error=*/"");
|
||||||
}
|
}
|
||||||
|
|
||||||
Matcher MakeAssertionMessageMatcher(std::string_view assertion_message) {
|
Matcher MakeAssertionMessageMatcher(std::string_view assertion_message, bool use_marker = true) {
|
||||||
return [=](const std::string& text) { //
|
return [=](const std::string& text) { //
|
||||||
return MatchAssertionMessage(text, assertion_message);
|
return MatchAssertionMessage(text, assertion_message, use_marker);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,13 +102,17 @@ Matcher MakeAnyMatcher() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class DeathCause {
|
enum class DeathCause {
|
||||||
// Valid causes
|
// Valid causes.
|
||||||
VerboseAbort = 1,
|
VerboseAbort = 1,
|
||||||
StdAbort,
|
StdAbort,
|
||||||
StdTerminate,
|
StdTerminate,
|
||||||
Trap,
|
Trap,
|
||||||
// Invalid causes
|
// Causes that might be invalid or might stem from undefined behavior (relevant for non-terminating assertion
|
||||||
|
// semantics).
|
||||||
DidNotDie,
|
DidNotDie,
|
||||||
|
Segfault,
|
||||||
|
ArithmeticError,
|
||||||
|
// Always invalid causes.
|
||||||
SetupFailure,
|
SetupFailure,
|
||||||
Unknown
|
Unknown
|
||||||
};
|
};
|
||||||
@ -108,6 +129,16 @@ bool IsValidCause(DeathCause cause) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsTestSetupErrorCause(DeathCause cause) {
|
||||||
|
switch (cause) {
|
||||||
|
case DeathCause::SetupFailure:
|
||||||
|
case DeathCause::Unknown:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string ToString(DeathCause cause) {
|
std::string ToString(DeathCause cause) {
|
||||||
switch (cause) {
|
switch (cause) {
|
||||||
case DeathCause::VerboseAbort:
|
case DeathCause::VerboseAbort:
|
||||||
@ -120,10 +151,14 @@ std::string ToString(DeathCause cause) {
|
|||||||
return "trap";
|
return "trap";
|
||||||
case DeathCause::DidNotDie:
|
case DeathCause::DidNotDie:
|
||||||
return "<invalid cause (child did not die)>";
|
return "<invalid cause (child did not die)>";
|
||||||
|
case DeathCause::Segfault:
|
||||||
|
return "<invalid cause (segmentation fault)>";
|
||||||
|
case DeathCause::ArithmeticError:
|
||||||
|
return "<invalid cause (fatal arithmetic error)>";
|
||||||
case DeathCause::SetupFailure:
|
case DeathCause::SetupFailure:
|
||||||
return "<invalid cause (child failed to set up test environment)>";
|
return "<test setup error (child failed to set up test environment)>";
|
||||||
case DeathCause::Unknown:
|
case DeathCause::Unknown:
|
||||||
return "<invalid cause (cause unknown)>";
|
return "<test setup error (test doesn't know how to interpret the death cause)>";
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(false && "Unreachable");
|
assert(false && "Unreachable");
|
||||||
@ -225,9 +260,38 @@ public:
|
|||||||
return DeathTestResult(Outcome::Success, cause);
|
return DeathTestResult(Outcome::Success, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintFailureDetails(std::string_view failure_description, std::string_view stmt, DeathCause cause) const {
|
// When non-terminating assertion semantics are used, the program will invoke UB which might or might not crash the
|
||||||
std::fprintf(
|
// process; we make sure that the execution produces the expected error message but otherwise consider the test run
|
||||||
stderr, "Failure: EXPECT_DEATH( %s ) failed!\n(reason: %s)\n\n", stmt.data(), failure_description.data());
|
// successful whether the child process dies or not.
|
||||||
|
template <class Func>
|
||||||
|
DeathTestResult RunWithoutGuaranteedDeath(Func&& func, const Matcher& matcher) {
|
||||||
|
std::signal(SIGABRT, [](int) { StopChildProcess(DeathCause::StdAbort); });
|
||||||
|
std::set_terminate([] { StopChildProcess(DeathCause::StdTerminate); });
|
||||||
|
|
||||||
|
DeathCause cause = Run(func);
|
||||||
|
|
||||||
|
if (IsTestSetupErrorCause(cause)) {
|
||||||
|
return DeathTestResult(Outcome::InvalidCause, cause, ToString(cause));
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchResult match_result = matcher(GetChildStdErr());
|
||||||
|
if (!match_result.first) {
|
||||||
|
auto failure_description = std::string("Child produced a different error message\n") + match_result.second;
|
||||||
|
return DeathTestResult(Outcome::UnexpectedErrorMessage, cause, failure_description);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DeathTestResult(Outcome::Success, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintFailureDetails(std::string_view invocation,
|
||||||
|
std::string_view failure_description,
|
||||||
|
std::string_view stmt,
|
||||||
|
DeathCause cause) const {
|
||||||
|
std::fprintf(stderr,
|
||||||
|
"Failure: %s( %s ) failed!\n(reason: %s)\n\n",
|
||||||
|
invocation.data(),
|
||||||
|
stmt.data(),
|
||||||
|
failure_description.data());
|
||||||
|
|
||||||
if (cause != DeathCause::Unknown) {
|
if (cause != DeathCause::Unknown) {
|
||||||
std::fprintf(stderr, "child exit code: %d\n", GetChildExitCode());
|
std::fprintf(stderr, "child exit code: %d\n", GetChildExitCode());
|
||||||
@ -311,10 +375,16 @@ private:
|
|||||||
|
|
||||||
if (WIFSIGNALED(status_value)) {
|
if (WIFSIGNALED(status_value)) {
|
||||||
exit_code_ = WTERMSIG(status_value);
|
exit_code_ = WTERMSIG(status_value);
|
||||||
// `__builtin_trap` generqtes `SIGILL` on x86 and `SIGTRAP` on ARM.
|
// `__builtin_trap` generates `SIGILL` on x86 and `SIGTRAP` on ARM.
|
||||||
if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
|
if (exit_code_ == SIGILL || exit_code_ == SIGTRAP) {
|
||||||
return DeathCause::Trap;
|
return DeathCause::Trap;
|
||||||
}
|
}
|
||||||
|
if (exit_code_ == SIGSEGV) {
|
||||||
|
return DeathCause::Segfault;
|
||||||
|
}
|
||||||
|
if (exit_code_ == SIGFPE) {
|
||||||
|
return DeathCause::ArithmeticError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return DeathCause::Unknown;
|
return DeathCause::Unknown;
|
||||||
@ -357,7 +427,7 @@ bool ExpectDeath(
|
|||||||
DeathTest test_case;
|
DeathTest test_case;
|
||||||
DeathTestResult test_result = test_case.Run(expected_causes, func, matcher);
|
DeathTestResult test_result = test_case.Run(expected_causes, func, matcher);
|
||||||
if (!test_result.success()) {
|
if (!test_result.success()) {
|
||||||
test_case.PrintFailureDetails(test_result.failure_description(), stmt, test_result.cause());
|
test_case.PrintFailureDetails("EXPECT_DEATH", test_result.failure_description(), stmt, test_result.cause());
|
||||||
}
|
}
|
||||||
|
|
||||||
return test_result.success();
|
return test_result.success();
|
||||||
@ -378,6 +448,22 @@ bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
|
|||||||
return ExpectDeath(std::array<DeathCause, 1>{expected_cause}, stmt, func, MakeAnyMatcher());
|
return ExpectDeath(std::array<DeathCause, 1>{expected_cause}, stmt, func, MakeAnyMatcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class Func>
|
||||||
|
bool ExpectLog(const char* stmt, Func&& func, const Matcher& matcher) {
|
||||||
|
DeathTest test_case;
|
||||||
|
DeathTestResult test_result = test_case.RunWithoutGuaranteedDeath(func, matcher);
|
||||||
|
if (!test_result.success()) {
|
||||||
|
test_case.PrintFailureDetails("EXPECT_LOG", test_result.failure_description(), stmt, test_result.cause());
|
||||||
|
}
|
||||||
|
|
||||||
|
return test_result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Func>
|
||||||
|
bool ExpectLog(const char* stmt, Func&& func) {
|
||||||
|
return ExpectLog(stmt, func, MakeAnyMatcher());
|
||||||
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
||||||
/// Assert that the specified expression aborts with the expected cause and, optionally, error message.
|
/// Assert that the specified expression aborts with the expected cause and, optionally, error message.
|
||||||
@ -392,13 +478,28 @@ bool ExpectDeath(DeathCause expected_cause, const char* stmt, Func&& func) {
|
|||||||
#define EXPECT_STD_TERMINATE(...) \
|
#define EXPECT_STD_TERMINATE(...) \
|
||||||
assert( ExpectDeath(DeathCause::StdTerminate, #__VA_ARGS__, __VA_ARGS__) )
|
assert( ExpectDeath(DeathCause::StdTerminate, #__VA_ARGS__, __VA_ARGS__) )
|
||||||
|
|
||||||
#if defined(_LIBCPP_HARDENING_MODE) && _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
|
#if defined(_LIBCPP_ASSERTION_SEMANTIC)
|
||||||
|
|
||||||
|
#if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
|
||||||
#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
|
#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
|
||||||
assert(( ExpectDeath(DeathCause::VerboseAbort, #expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message)) ))
|
assert(( ExpectDeath(DeathCause::VerboseAbort, #expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message)) ))
|
||||||
|
#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
|
||||||
|
#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
|
||||||
|
assert(( ExpectDeath(DeathCause::Trap, #expr, [&]() { (void)(expr); }) ))
|
||||||
|
#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
|
||||||
|
#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
|
||||||
|
assert(( ExpectLog(#expr, [&]() { (void)(expr); }, MakeAssertionMessageMatcher(message, /*use_marker=*/false)) ))
|
||||||
|
#elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
|
||||||
|
#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
|
||||||
|
assert(( ExpectLog(#expr, [&]() { (void)(expr); }) ))
|
||||||
|
#else
|
||||||
|
#error "Unknown value for _LIBCPP_ASSERTION_SEMANTIC"
|
||||||
|
#endif // _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
|
#define TEST_LIBCPP_ASSERT_FAILURE(expr, message) \
|
||||||
assert(( ExpectDeath(DeathCause::Trap, #expr, [&]() { (void)(expr); }) ))
|
assert(( ExpectDeath(DeathCause::Trap, #expr, [&]() { (void)(expr); }) ))
|
||||||
#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
|
#endif // defined(_LIBCPP_ASSERTION_SEMANTIC)
|
||||||
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ bool TestDeathTest(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!maybe_failure_description.empty()) {
|
if (!maybe_failure_description.empty()) {
|
||||||
test_case.PrintFailureDetails(maybe_failure_description, stmt, test_result.cause());
|
test_case.PrintFailureDetails("EXPECT_DEATH", maybe_failure_description, stmt, test_result.cause());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,9 +76,9 @@ DeathCause assertion_death_cause = DeathCause::Trap;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
int main(int, char**) {
|
int main(int, char**) {
|
||||||
auto fail_assert = [] { _LIBCPP_ASSERT(false, "Some message"); };
|
[[maybe_unused]] auto fail_assert = [] { _LIBCPP_ASSERT(false, "Some message"); };
|
||||||
Matcher good_matcher = MakeAssertionMessageMatcher("Some message");
|
Matcher good_matcher = MakeAssertionMessageMatcher("Some message");
|
||||||
Matcher bad_matcher = MakeAssertionMessageMatcher("Bad expected message");
|
Matcher bad_matcher = MakeAssertionMessageMatcher("Bad expected message");
|
||||||
|
|
||||||
// Test the implementation of death tests. We're bypassing the assertions added by the actual `EXPECT_DEATH` macros
|
// Test the implementation of death tests. We're bypassing the assertions added by the actual `EXPECT_DEATH` macros
|
||||||
// which allows us to test failure cases (where the assertion would fail) as well.
|
// which allows us to test failure cases (where the assertion would fail) as well.
|
||||||
@ -89,16 +89,22 @@ int main(int, char**) {
|
|||||||
// Success -- trapping.
|
// Success -- trapping.
|
||||||
TEST_DEATH_TEST(Outcome::Success, DeathCause::Trap, __builtin_trap());
|
TEST_DEATH_TEST(Outcome::Success, DeathCause::Trap, __builtin_trap());
|
||||||
|
|
||||||
|
// `_LIBCPP_ASSERT` does not terminate the program if the `observe` semantic is used, so these tests would fail with
|
||||||
|
// `DidNotDie` cause.
|
||||||
|
#if _LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
|
||||||
|
|
||||||
// Success -- assertion failure with any matcher.
|
// Success -- assertion failure with any matcher.
|
||||||
TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, MakeAnyMatcher(), fail_assert());
|
TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, MakeAnyMatcher(), fail_assert());
|
||||||
|
|
||||||
// Success -- assertion failure with a specific matcher.
|
// Success -- assertion failure with a specific matcher.
|
||||||
TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, good_matcher, fail_assert());
|
TEST_DEATH_TEST_MATCHES(Outcome::Success, assertion_death_cause, good_matcher, fail_assert());
|
||||||
|
|
||||||
#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
|
# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
|
||||||
// Failure -- error message doesn't match.
|
// Failure -- error message doesn't match.
|
||||||
TEST_DEATH_TEST_MATCHES(Outcome::UnexpectedErrorMessage, assertion_death_cause, bad_matcher, fail_assert());
|
TEST_DEATH_TEST_MATCHES(Outcome::UnexpectedErrorMessage, assertion_death_cause, bad_matcher, fail_assert());
|
||||||
#endif
|
# endif
|
||||||
|
|
||||||
|
#endif // _LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
|
||||||
|
|
||||||
// Invalid cause -- child did not die.
|
// Invalid cause -- child did not die.
|
||||||
TEST_DEATH_TEST(Outcome::InvalidCause, DeathCause::DidNotDie, ((void)0));
|
TEST_DEATH_TEST(Outcome::InvalidCause, DeathCause::DidNotDie, ((void)0));
|
||||||
@ -125,7 +131,9 @@ int main(int, char**) {
|
|||||||
EXPECT_DEATH_MATCHES(simple_matcher, invoke_verbose_abort());
|
EXPECT_DEATH_MATCHES(simple_matcher, invoke_verbose_abort());
|
||||||
EXPECT_STD_ABORT(invoke_abort());
|
EXPECT_STD_ABORT(invoke_abort());
|
||||||
EXPECT_STD_TERMINATE([] { std::terminate(); });
|
EXPECT_STD_TERMINATE([] { std::terminate(); });
|
||||||
|
#if _LIBCPP_ASSERTION_SEMANTIC != _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
|
||||||
TEST_LIBCPP_ASSERT_FAILURE(fail_assert(), "Some message");
|
TEST_LIBCPP_ASSERT_FAILURE(fail_assert(), "Some message");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -442,6 +442,12 @@ generic-hardening-mode-extensive)
|
|||||||
check-runtimes
|
check-runtimes
|
||||||
check-abi-list
|
check-abi-list
|
||||||
;;
|
;;
|
||||||
|
generic-hardening-mode-extensive-observe-semantic)
|
||||||
|
clean
|
||||||
|
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-hardening-mode-extensive-observe-semantic.cmake"
|
||||||
|
check-runtimes
|
||||||
|
check-abi-list
|
||||||
|
;;
|
||||||
generic-hardening-mode-debug)
|
generic-hardening-mode-debug)
|
||||||
clean
|
clean
|
||||||
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-hardening-mode-debug.cmake"
|
generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-hardening-mode-debug.cmake"
|
||||||
|
@ -455,5 +455,24 @@ DEFAULT_PARAMETERS = [
|
|||||||
help="Whether to test the main or C++03-specific headers. Only changes behaviour when std=c++03.",
|
help="Whether to test the main or C++03-specific headers. Only changes behaviour when std=c++03.",
|
||||||
actions=lambda enabled: [] if not enabled else [AddFlag("-D_LIBCPP_USE_FROZEN_CXX03_HEADERS"), AddFeature("FROZEN-CXX03-HEADERS-FIXME")],
|
actions=lambda enabled: [] if not enabled else [AddFlag("-D_LIBCPP_USE_FROZEN_CXX03_HEADERS"), AddFeature("FROZEN-CXX03-HEADERS-FIXME")],
|
||||||
),
|
),
|
||||||
|
Parameter(
|
||||||
|
name='assertion_semantic',
|
||||||
|
choices=["ignore", "observe", "quick_enforce", "enforce", "undefined"],
|
||||||
|
type=str,
|
||||||
|
default="undefined",
|
||||||
|
help="Whether to override the assertion semantic used by hardening. This is only meaningful when running the "
|
||||||
|
"tests against libc++ with hardening enabled. By default, no assertion semantic is specified explicitly, so "
|
||||||
|
"the default one will be used (depending on the hardening mode).",
|
||||||
|
actions=lambda assertion_semantic: filter(
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
AddCompileFlag("-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_IGNORE") if assertion_semantic == "ignore" else None,
|
||||||
|
AddCompileFlag("-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_OBSERVE") if assertion_semantic == "observe" else None,
|
||||||
|
AddCompileFlag("-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE") if assertion_semantic == "quick_enforce" else None,
|
||||||
|
AddCompileFlag("-D_LIBCPP_ASSERTION_SEMANTIC=_LIBCPP_ASSERTION_SEMANTIC_ENFORCE") if assertion_semantic == "enforce" else None,
|
||||||
|
AddFeature("libcpp-assertion-semantic={}".format(assertion_semantic)) if assertion_semantic != "undefined" else None,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
35
libcxx/vendor/llvm/default_assertion_handler.in
vendored
35
libcxx/vendor/llvm/default_assertion_handler.in
vendored
@ -16,6 +16,7 @@
|
|||||||
# include <__cxx03/__verbose_trap>
|
# include <__cxx03/__verbose_trap>
|
||||||
#else
|
#else
|
||||||
# include <__config>
|
# include <__config>
|
||||||
|
# include <__log_hardening_failure>
|
||||||
# include <__verbose_abort>
|
# include <__verbose_abort>
|
||||||
# include <__verbose_trap>
|
# include <__verbose_trap>
|
||||||
#endif
|
#endif
|
||||||
@ -24,14 +25,40 @@
|
|||||||
# pragma GCC system_header
|
# pragma GCC system_header
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
|
#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
|
||||||
|
|
||||||
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
|
// Keep the old implementation that doesn't support assertion semantics for backward compatibility with the frozen C++03
|
||||||
|
// mode.
|
||||||
|
# if _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
|
||||||
|
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
|
||||||
|
# else
|
||||||
|
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
|
||||||
|
# endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
|
# if _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
|
||||||
|
# define _LIBCPP_ASSERTION_HANDLER(message) ((void)0)
|
||||||
|
|
||||||
#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_DEBUG
|
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_OBSERVE
|
||||||
|
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_LOG_HARDENING_FAILURE(message)
|
||||||
|
|
||||||
|
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE
|
||||||
|
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_TRAP(message)
|
||||||
|
|
||||||
|
# elif _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_ENFORCE
|
||||||
|
# define _LIBCPP_ASSERTION_HANDLER(message) _LIBCPP_VERBOSE_ABORT("%s", message)
|
||||||
|
|
||||||
|
# else
|
||||||
|
|
||||||
|
# error _LIBCPP_ASSERTION_SEMANTIC must be set to one of the following values: \
|
||||||
|
_LIBCPP_ASSERTION_SEMANTIC_IGNORE, \
|
||||||
|
_LIBCPP_ASSERTION_SEMANTIC_OBSERVE, \
|
||||||
|
_LIBCPP_ASSERTION_SEMANTIC_QUICK_ENFORCE, \
|
||||||
|
_LIBCPP_ASSERTION_SEMANTIC_ENFORCE
|
||||||
|
|
||||||
|
# endif // _LIBCPP_ASSERTION_SEMANTIC == _LIBCPP_ASSERTION_SEMANTIC_IGNORE
|
||||||
|
|
||||||
|
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
|
||||||
|
|
||||||
#endif // _LIBCPP___ASSERTION_HANDLER
|
#endif // _LIBCPP___ASSERTION_HANDLER
|
||||||
|
@ -19,6 +19,14 @@
|
|||||||
#include "../abort_message.h"
|
#include "../abort_message.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef _LIBCPP_LOG_HARDENING_FAILURE
|
||||||
|
// Libc++abi does not have any functionality to log and continue, so we drop
|
||||||
|
// error messages when we build the demangler with `observe` assertion semantic.
|
||||||
|
// Once the layering with libc++ is improved, this could use the libc++
|
||||||
|
// functionality to log hardening failures.
|
||||||
|
#define _LIBCPP_LOG_HARDENING_FAILURE(message) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <version>
|
#include <version>
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
Loading…
x
Reference in New Issue
Block a user