diff --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst --- a/libcxx/docs/UsingLibcxx.rst +++ b/libcxx/docs/UsingLibcxx.rst @@ -155,11 +155,14 @@ 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 @@ -198,20 +201,6 @@ 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 =========================== diff --git a/libcxx/include/__availability b/libcxx/include/__availability --- a/libcxx/include/__availability +++ b/libcxx/include/__availability @@ -171,6 +171,7 @@ // 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 +// # define _LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY #elif defined(__APPLE__) @@ -273,6 +274,8 @@ # define _LIBCPP_AVAILABILITY_DEFAULT_VERBOSE_ABORT \ __attribute__((unavailable)) +# define _LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY + #else // ...New vendors can add availability markup here... diff --git a/libcxx/include/__verbose_abort b/libcxx/include/__verbose_abort --- a/libcxx/include/__verbose_abort +++ b/libcxx/include/__verbose_abort @@ -17,6 +17,27 @@ # 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_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_AVAILABILITY_VERBOSE_ABORT _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) @@ -24,4 +45,6 @@ _LIBCPP_END_NAMESPACE_STD +#endif + #endif // _LIBCPP___VERBOSE_ABORT diff --git a/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp b/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp --- a/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp +++ b/libcxx/test/libcxx/assertions/default_verbose_abort.pass.cpp @@ -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 #include diff --git a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp --- a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp +++ b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp @@ -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 seems to be broken on AIX, which trips up this test. // XFAIL: LIBCXX-AIX-FIXME diff --git a/libcxx/test/libcxx/assertions/single_expression.sh.cpp b/libcxx/test/libcxx/assertions/single_expression.sh.cpp --- a/libcxx/test/libcxx/assertions/single_expression.sh.cpp +++ b/libcxx/test/libcxx/assertions/single_expression.sh.cpp @@ -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 diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml --- a/libcxx/utils/ci/buildkite-pipeline.yml +++ b/libcxx/utils/ci/buildkite-pipeline.yml @@ -725,6 +725,20 @@ 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" diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot --- a/libcxx/utils/ci/run-buildbot +++ b/libcxx/utils/ci/run-buildbot @@ -385,6 +385,48 @@ # 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