From 17c05a44d9f0ab08342c7310bd0e22114e932882 Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Mon, 9 Jan 2023 16:59:59 -0500 Subject: [libc++] Introduce a compile-time mechanism to override __libcpp_verbose_abort This changes the mechanism for verbose termination (again!) to make it support compile-time customization in addition to link-time customization, which is important for users who need fine-grained control over what code gets generated around sites that call the verbose termination handler. This concern had been raised to me both privately by prospecting users and in https://llvm.org/D140944, so I think it is clearly worth fixing. We still support _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED for a limited time since the same functionality can be achieved by overriding the _LIBCPP_VERBOSE_ABORT macro. Differential Revision: https://reviews.llvm.org/D141326 --- libcxx/docs/ReleaseNotes.rst | 4 ++ libcxx/docs/UsingLibcxx.rst | 19 +++++--- libcxx/include/__assert | 2 +- libcxx/include/__availability | 17 ++------ libcxx/include/__verbose_abort | 51 +++++++++++++--------- ...customize_verbose_abort.backdeployment.pass.cpp | 26 ----------- .../customize_verbose_abort.compile-time.pass.cpp | 27 ++++++++++++ .../customize_verbose_abort.link-time.pass.cpp | 26 +++++++++++ .../assertions/customize_verbose_abort.pass.cpp | 26 ----------- .../deprecated-link-time-custom-handler.pass.cpp | 26 +++++++++++ .../headers_declare_verbose_abort.sh.cpp | 4 +- 11 files changed, 133 insertions(+), 95 deletions(-) delete mode 100644 libcxx/test/libcxx/assertions/customize_verbose_abort.backdeployment.pass.cpp create mode 100644 libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp create mode 100644 libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp delete mode 100644 libcxx/test/libcxx/assertions/customize_verbose_abort.pass.cpp create mode 100644 libcxx/test/libcxx/assertions/deprecated-link-time-custom-handler.pass.cpp (limited to 'libcxx') diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst index f22e076..060ab38 100644 --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -140,6 +140,10 @@ Upcoming Deprecations and Removals and such a base template is bound to be incorrect for some types, which could currently cause unexpected behavior while going undetected. +- The ``_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED`` macro will not be honored anymore in LLVM 18. + Please see the updated documentation about the safe libc++ mode and in particular the ``_LIBCPP_VERBOSE_ABORT`` + macro for details. + API Changes ----------- - The comparison operators on ``thread::id`` are now defined as free-standing diff --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst index 6fac013b..3b64adb 100644 --- a/libcxx/docs/UsingLibcxx.rst +++ b/libcxx/docs/UsingLibcxx.rst @@ -172,12 +172,19 @@ library provides a default function that prints an error message and calls ``std 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. 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 -``_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED`` macro in all translation units of your program -and defining the following function in exactly one translation unit: +However, users can also override that mechanism at two different levels. First, the mechanism can be +overriden at compile-time by defining the ``_LIBCPP_VERBOSE_ABORT(format, args...)`` variadic macro. +When that macro is defined, it will be called with a format string as the first argument, followed by +a series of arguments to format using printf-style formatting. Compile-time customization may be +interesting to get precise control over code generation, however it is also inconvenient to use in +some cases. Indeed, compile-time customization of the verbose termination function requires that all +translation units be compiled with a consistent definition for ``_LIBCPP_VERBOSE_ABORT`` to avoid ODR +violations, which can add complexity in the build system of users. + +Otherwise, if compile-time customization is not necessary, link-time customization of the handler is also +possible, similarly to how replacing ``operator new`` works. This mechanism trades off fine-grained control +over the call site where the termination is initiated in exchange for more ergonomics. Link-time customization +is done by simply defining the following function in exactly one translation unit of your program: .. code-block:: cpp diff --git a/libcxx/include/__assert b/libcxx/include/__assert index afe7304..9c0cd1b 100644 --- a/libcxx/include/__assert +++ b/libcxx/include/__assert @@ -41,7 +41,7 @@ # define _LIBCPP_ASSERT(expression, message) \ (__builtin_expect(static_cast(expression), 1) ? \ (void)0 : \ - ::std::__libcpp_verbose_abort("%s:%d: assertion %s failed: %s", __FILE__, __LINE__, #expression, message)) + _LIBCPP_VERBOSE_ABORT("%s:%d: assertion %s failed: %s", __FILE__, __LINE__, #expression, message)) #elif !defined(_LIBCPP_ASSERTIONS_DISABLE_ASSUME) && __has_builtin(__builtin_assume) # define _LIBCPP_ASSERT(expression, message) \ (_LIBCPP_DIAGNOSTIC_PUSH \ diff --git a/libcxx/include/__availability b/libcxx/include/__availability index 72ff663..6dfca3f 100644 --- a/libcxx/include/__availability +++ b/libcxx/include/__availability @@ -156,20 +156,9 @@ # define _LIBCPP_AVAILABILITY_FORMAT // # define _LIBCPP_AVAILABILITY_DISABLE_FTM___cpp_lib_format - // This controls whether the default verbose termination function is - // provided by the library. - // - // Note that when users provide their own custom function, it doesn't - // matter whether the dylib provides a default function, and the - // availability markup can actually give a false positive diagnostic - // (it will think that no function is provided, when in reality the - // user has provided their own). - // - // Users can pass -D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED - // 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. + // This controls whether the library claims to provide a default verbose + // termination function, and consequently whether the headers will try + // to use it when the mechanism isn't overriden at compile-time. // # define _LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY #elif defined(__APPLE__) diff --git a/libcxx/include/__verbose_abort b/libcxx/include/__verbose_abort index 3559c52..a16d75d 100644 --- a/libcxx/include/__verbose_abort +++ b/libcxx/include/__verbose_abort @@ -17,32 +17,41 @@ # pragma GCC system_header #endif -// Provide a default implementation of __libcpp_verbose_abort if we know that neither the built -// library nor 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) - -_LIBCPP_BEGIN_NAMESPACE_STD - -_LIBCPP_NORETURN _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) _LIBCPP_HIDE_FROM_ABI inline -void __libcpp_verbose_abort(const char *, ...) { - __builtin_abort(); -} - -_LIBCPP_END_NAMESPACE_STD - -#else - _LIBCPP_BEGIN_NAMESPACE_STD +// This function should never be called directly from the code -- it should only be called through +// the _LIBCPP_VERBOSE_ABORT macro. _LIBCPP_NORETURN _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) void __libcpp_verbose_abort(const char *__format, ...); -_LIBCPP_END_NAMESPACE_STD +// _LIBCPP_VERBOSE_ABORT(format, args...) +// +// This macro is used to abort the program abnormally while providing additional diagnostic information. +// +// The first argument is a printf-style format string, and the remaining arguments are values to format +// into the format-string. This macro can be customized by users to provide fine-grained control over +// how verbose termination is triggered. +// +// If the user does not supply their own version of the _LIBCPP_VERBOSE_ABORT macro, we pick the default +// behavior based on whether we know the built library we're running against provides support for the +// verbose termination handler or not. If it does, we call it. If it doesn't, we call __builtin_abort to +// make sure that the program terminates but without taking any complex dependencies in this header. +#if !defined(_LIBCPP_VERBOSE_ABORT) + +// Support _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED until LLVM 18, but tell people +// to move to customizing _LIBCPP_VERBOSE_ABORT instead. +# if defined(_LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY) && defined(_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED) +# undef _LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY +# warning _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED is deprecated, please customize _LIBCPP_VERBOSE_ABORT instead +# endif + +# if defined(_LIBCPP_HAS_NO_VERBOSE_ABORT_IN_LIBRARY) +# define _LIBCPP_VERBOSE_ABORT(...) __builtin_abort() +# else +# define _LIBCPP_VERBOSE_ABORT(...) ::std::__libcpp_verbose_abort(__VA_ARGS__) +# endif +#endif // !defined(_LIBCPP_VERBOSE_ABORT) -#endif +_LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___VERBOSE_ABORT diff --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.backdeployment.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.backdeployment.pass.cpp deleted file mode 100644 index 1596267..0000000 --- a/libcxx/test/libcxx/assertions/customize_verbose_abort.backdeployment.pass.cpp +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// Make sure that we can enable assertions when we back-deploy to older platforms -// if we define _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED. -// -// Note that this test isn't really different from customize_verbose_abort.pass.cpp when -// run outside of back-deployment scenarios, but we always want to run this test. - -// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 -D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED - -#include - -void std::__libcpp_verbose_abort(char const*, ...) { - std::exit(EXIT_SUCCESS); -} - -int main(int, char**) { - _LIBCPP_ASSERT(false, "message"); - return EXIT_FAILURE; -} diff --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp new file mode 100644 index 0000000..1ea6c36 --- /dev/null +++ b/libcxx/test/libcxx/assertions/customize_verbose_abort.compile-time.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// This compile-time customization requires cross-file macros, which doesn't work with modules. +// UNSUPPORTED: modules-build + +// Make sure that we can customize the verbose termination function at compile-time by +// defining _LIBCPP_VERBOSE_ABORT ourselves. Note that this does not have any +// deployment target requirements. + +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 -D_LIBCPP_VERBOSE_ABORT(...)=my_abort(__VA_ARGS__) + +#include + +void my_abort(char const*, ...) { + std::exit(EXIT_SUCCESS); +} + +int main(int, char**) { + _LIBCPP_ASSERT(false, "message"); + return EXIT_FAILURE; +} diff --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp new file mode 100644 index 0000000..c0422dc --- /dev/null +++ b/libcxx/test/libcxx/assertions/customize_verbose_abort.link-time.pass.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Test that we can set a custom verbose termination function at link-time. + +// 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 + +void std::__libcpp_verbose_abort(char const*, ...) { + std::exit(EXIT_SUCCESS); +} + +int main(int, char**) { + _LIBCPP_ASSERT(false, "message"); + return EXIT_FAILURE; +} diff --git a/libcxx/test/libcxx/assertions/customize_verbose_abort.pass.cpp b/libcxx/test/libcxx/assertions/customize_verbose_abort.pass.cpp deleted file mode 100644 index 3ee9b78..0000000 --- a/libcxx/test/libcxx/assertions/customize_verbose_abort.pass.cpp +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// Test that we can set a custom verbose termination function. - -// 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 - -void std::__libcpp_verbose_abort(char const*, ...) { - std::exit(EXIT_SUCCESS); -} - -int main(int, char**) { - _LIBCPP_ASSERT(false, "message"); - return EXIT_FAILURE; -} diff --git a/libcxx/test/libcxx/assertions/deprecated-link-time-custom-handler.pass.cpp b/libcxx/test/libcxx/assertions/deprecated-link-time-custom-handler.pass.cpp new file mode 100644 index 0000000..b699a3d --- /dev/null +++ b/libcxx/test/libcxx/assertions/deprecated-link-time-custom-handler.pass.cpp @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Make sure that we still support _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED for folks +// who customize the verbose termination function at link-time in back-deployment environments. + +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1 -D_LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED + +// We emit a #warning about the deprecation of this setting, so make sure we don't turn that into an error. +// ADDITIONAL_COMPILE_FLAGS: -Wno-error + +#include + +void std::__libcpp_verbose_abort(char const*, ...) { + std::exit(EXIT_SUCCESS); +} + +int main(int, char**) { + _LIBCPP_ASSERT(false, "message"); + return EXIT_FAILURE; +} diff --git a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp index 7ca6d91..80f3f25 100644 --- a/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp +++ b/libcxx/test/libcxx/assertions/headers_declare_verbose_abort.sh.cpp @@ -6,7 +6,9 @@ // //===----------------------------------------------------------------------===// -// Test that all public C++ headers define the verbose termination function. +// Test that all public C++ headers define the verbose termination function, which +// is required for users to be able to include any public header and then override +// the function using a strong definition. // The system-provided seems to be broken on AIX, which trips up this test. // XFAIL: LIBCXX-AIX-FIXME -- cgit v1.1