Skip to content

Commit

Permalink
[libc++] Allow enabling assertions when back-deploying
Browse files Browse the repository at this point in the history
When back-deploying to older platforms, we can still provide assertions,
but we might not be able to provide a great implementation for the verbose
handler. Instead, we can just call ::abort().

Differential Revision: https://reviews.llvm.org/D131199
  • Loading branch information
ldionne committed Aug 8, 2022
1 parent 7dc410c commit e36f9e1
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 68 deletions.
25 changes: 7 additions & 18 deletions libcxx/docs/UsingLibcxx.rst
Expand Up @@ -155,11 +155,14 @@ value for ``_LIBCPP_ENABLE_ASSERTIONS`` (if any) will usually be respected.
When an assertion fails, the program is aborted through a special verbose termination function. The
library provides a default function that prints an error message and calls ``std::abort()``. Note
that this function 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
function with their own, which can be useful to provide custom behavior, or when deploying to older
platforms where the default function isn't available.
to a platform where the compiled library is sufficiently recent. On older platforms, the program will
terminate in an unspecified unsuccessful manner, but the quality of diagnostics won't be great.
However, users can also override that function with their own, which can be useful to either provide
custom behavior or when deploying to an older platform where the default function isn't available.

Replacing the default verbose termination function is done by defining the following function:
Replacing the default verbose termination function is done by defining the
``_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED`` macro in all translation units of your program
and defining the following function in exactly one translation unit:

.. code-block:: cpp
Expand Down Expand Up @@ -198,20 +201,6 @@ Furthermore, exceptions should not be thrown from the function. Indeed, many fun
library are ``noexcept``, and any exception thrown from the termination function will result
in ``std::terminate`` being called.

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

If you are providing a custom verbose termination function, this error is effectively a false positive.
To let the library know that you are providing a custom function in back-deployment scenarios, you must
define the ``_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_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 an older
platform, it will fail to load when the dynamic linker fails to find a definition of the function, so you
should only remove the guard rails if you really mean it!

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

Expand Down
24 changes: 7 additions & 17 deletions libcxx/include/__availability
Expand Up @@ -166,11 +166,11 @@
// user has provided their own).
//
// Users can pass -D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED
// to the compiler to tell the library to ignore the fact that the
// default function isn't available on their deployment target. Note that
// defining this macro but failing to define a custom function will lead to
// a load-time error on back-deployment targets, so it should be avoided.
# define _LIBCPP_AVAILABILITY_DEFAULT_VERBOSE_ABORT
// to the compiler to tell the library not to define its own verbose abort.
// Note that defining this macro but failing to define a custom function
// will lead to a load-time error on back-deployment targets, so it should
// be avoided.
// # define _LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY

#elif defined(__APPLE__)

Expand Down Expand Up @@ -271,8 +271,8 @@
__attribute__((unavailable))
# define _LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format

# define _LIBCPP_AVAILABILITY_DEFAULT_VERBOSE_ABORT \
__attribute__((unavailable))
# define _LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY

#else

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

// Define the special verbose termination function availability attribute, which can be silenced by
// users if they provide their own custom function. 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 function.
#if defined(_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED)
# define _LIBCPP_AVAILABILITY_VERBOSE_ABORT /* nothing */
#else
# define _LIBCPP_AVAILABILITY_VERBOSE_ABORT _LIBCPP_AVAILABILITY_DEFAULT_VERBOSE_ABORT
#endif

#endif // _LIBCPP___AVAILABILITY
25 changes: 24 additions & 1 deletion libcxx/include/__verbose_abort
Expand Up @@ -17,11 +17,34 @@
# pragma GCC system_header
#endif

// Provide a default implementation of __libcpp_verbose_abort if we know that neither the built
// library not the user is providing one. Otherwise, just declare it and use the one from the
// built library or the one provided by the user.
//
// We can't provide a great implementation because it needs to be pretty much
// dependency-free (this is included everywhere else in the library).
#if defined(_LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY) && !defined(_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED)

extern "C" void abort();

_LIBCPP_BEGIN_NAMESPACE_STD

_LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_VERBOSE_ABORT _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2)
_LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) _LIBCPP_HIDE_FROM_ABI inline
void __libcpp_verbose_abort(const char *, ...) {
::abort();
}

_LIBCPP_END_NAMESPACE_STD

#else

_LIBCPP_BEGIN_NAMESPACE_STD

_LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2)
void __libcpp_verbose_abort(const char *__format, ...);

_LIBCPP_END_NAMESPACE_STD

#endif

#endif // _LIBCPP___VERBOSE_ABORT

This file was deleted.

4 changes: 0 additions & 4 deletions libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp
Expand Up @@ -10,10 +10,6 @@

// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1

// We flag uses of the verbose termination function in older dylibs at compile-time to avoid runtime
// failures when back-deploying.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}}

#include <csignal>
#include <cstdlib>

Expand Down
Expand Up @@ -8,10 +8,6 @@

// Test that all public C++ headers define the verbose termination function.

// We flag uses of the verbose termination function in older dylibs at compile-time to avoid runtime
// failures when back-deploying.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}}

// The system-provided <uchar.h> seems to be broken on AIX, which trips up this test.
// XFAIL: LIBCXX-AIX-FIXME

Expand Down
4 changes: 0 additions & 4 deletions libcxx/test/libcxx/assertions/single_expression.sh.cpp
Expand Up @@ -18,10 +18,6 @@
// RUN: %{build} -Wno-macro-redefined -D_LIBCPP_ENABLE_ASSERTIONS=0 -D_LIBCPP_ASSERTIONS_DISABLE_ASSUME
// RUN: %{run}

// We flag uses of the assertion handler in older dylibs at compile-time to avoid runtime
// failures when back-deploying.
// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx{{10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0}}

#include <__assert>
#include <cassert>

Expand Down
14 changes: 14 additions & 0 deletions libcxx/utils/ci/buildkite-pipeline.yml
Expand Up @@ -725,6 +725,20 @@ steps:
limit: 2
timeout_in_minutes: 120

- label: "Apple back-deployment with assertions enabled"
command: "libcxx/utils/ci/run-buildbot apple-system-backdeployment-assertions-11.0"
artifact_paths:
- "**/test-results.xml"
- "**/*.abilist"
agents:
queue: "libcxx-builders"
os: "macos"
retry:
automatic:
- exit_status: -1 # Agent was lost
limit: 2
timeout_in_minutes: 120

- group: "ARM"
steps:
- label: "AArch64"
Expand Down
42 changes: 42 additions & 0 deletions libcxx/utils/ci/run-buildbot
Expand Up @@ -385,6 +385,48 @@ apple-system)
# TODO: It would be better to run the tests against the fake-installed version of libc++ instead
xcrun --sdk macosx ninja -vC "${BUILD_DIR}/${arch}" check-cxx check-cxxabi check-cxx-abilist
;;
apple-system-backdeployment-assertions-*)
clean

if [[ "${OSX_ROOTS}" == "" ]]; then
echo "--- Downloading previous macOS dylibs"
PREVIOUS_DYLIBS_URL="https://dl.dropboxusercontent.com/s/gmcfxwgl9f9n6pu/libcxx-roots.tar.gz"
OSX_ROOTS="${BUILD_DIR}/macos-roots"
mkdir -p "${OSX_ROOTS}"
curl "${PREVIOUS_DYLIBS_URL}" | tar -xz --strip-components=1 -C "${OSX_ROOTS}"
fi

DEPLOYMENT_TARGET="${BUILDER#apple-system-backdeployment-assertions-}"

# TODO: On Apple platforms, we never produce libc++abi.1.dylib or libunwind.1.dylib,
# only libc++abi.dylib and libunwind.dylib. Fix that in the build so that the
# tests stop searching for @rpath/libc++abi.1.dylib and @rpath/libunwind.1.dylib.
cp "${OSX_ROOTS}/macOS/libc++abi/${DEPLOYMENT_TARGET}/libc++abi.dylib" \
"${OSX_ROOTS}/macOS/libc++abi/${DEPLOYMENT_TARGET}/libc++abi.1.dylib"
cp "${OSX_ROOTS}/macOS/libunwind/${DEPLOYMENT_TARGET}/libunwind.dylib" \
"${OSX_ROOTS}/macOS/libunwind/${DEPLOYMENT_TARGET}/libunwind.1.dylib"

arch="$(uname -m)"
PARAMS="target_triple=${arch}-apple-macosx${DEPLOYMENT_TARGET}"
PARAMS+=";cxx_runtime_root=${OSX_ROOTS}/macOS/libc++/${DEPLOYMENT_TARGET}"
PARAMS+=";abi_runtime_root=${OSX_ROOTS}/macOS/libc++abi/${DEPLOYMENT_TARGET}"
PARAMS+=";unwind_runtime_root=${OSX_ROOTS}/macOS/libunwind/${DEPLOYMENT_TARGET}"
PARAMS+=";use_system_cxx_lib=True"
PARAMS+=";enable_assertions=True"
# TODO: Enable experimental features during back-deployment -- right now some of the availability
# annotations are incorrect, leading to test failures that could be avoided.
PARAMS+=";enable_experimental=False"

generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Apple.cmake" \
-DLIBCXX_TEST_CONFIG="apple-libc++-backdeployment.cfg.in" \
-DLIBCXXABI_TEST_CONFIG="apple-libc++abi-backdeployment.cfg.in" \
-DLIBUNWIND_TEST_CONFIG="apple-libunwind-backdeployment.cfg.in" \
-DLIBCXX_TEST_PARAMS="${PARAMS}" \
-DLIBCXXABI_TEST_PARAMS="${PARAMS}" \
-DLIBUNWIND_TEST_PARAMS="${PARAMS}"

check-runtimes
;;
apple-system-backdeployment-*)
clean

Expand Down

0 comments on commit e36f9e1

Please sign in to comment.