[libc-commits] [libcxx] [compiler-rt] [clang] [lld] [flang] [libcxxabi] [clang-tools-extra] [llvm] [libc] [libc++] Fix the behavior of throwing `operator new` under -fno-exceptions (PR #69498)
Louis Dionne via libc-commits
libc-commits at lists.llvm.org
Wed Nov 8 18:27:10 PST 2023
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/69498
>From 77c0256c3ae99808a8def68bfcf5eee2fad704ca Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 14 Jun 2023 17:49:22 -0700
Subject: [PATCH 1/9] [libc++] Fix the behavior of throwing `operator new`
under -fno-exceptions
In D144319, Clang tried to land a change that would cause some functions
that are not supposed to return nullptr to optimize better. As reported
in https://reviews.llvm.org/D144319#4203982, libc++ started seeing failures
in its CI shortly after this change was landed.
As explained in D146379, the reason for these failures is that libc++'s
throwing `operator new` can in fact return nullptr when compiled with
exceptions disabled. However, this contradicts the Standard, which
clearly says that the throwing version of `operator new(size_t)`
should never return nullptr. This is actually a long standing issue.
I've previously seen a case where LTO would optimize incorrectly based
on the assumption that `operator new` doesn't return nullptr, an
assumption that was violated in that case because libc++.dylib was
compiled with -fno-exceptions.
Unfortunately, fixing this is kind of tricky. The Standard has a few
requirements for the allocation functions, some of which are impossible
to satisfy under -fno-exceptions:
1. `operator new(size_t)` must never return nullptr
2. `operator new(size_t, nothrow_t)` must call the throwing version
and return nullptr on failure to allocate
3. We can't throw exceptions when compiled with -fno-exceptions
In the case where exceptions are enabled, things work nicely. `new(size_t)`
throws and `new(size_t, nothrow_t)` uses a try-catch to return nullptr.
However, when compiling the library with -fno-exceptions, we can't throw
an exception from `new(size_t)`, and we can't catch anything from
`new(size_t, nothrow_t)`. The only thing we can do from `new(size_t)`
is actually abort the program, which does not make it possible for
`new(size_t, nothrow_t)` to catch something and return nullptr.
This patch makes the following changes:
1. When compiled with -fno-exceptions, the throwing version of
`operator new` will now abort on failure instead of returning
nullptr on failure. This resolves the issue that the compiler
could mis-compile based on the assumption that nullptr is never
returned. This constitutes an API and ABI breaking change for
folks compiling the library with -fno-exceptions (which is not
the general public, who merely uses libc++ headers but use a
shared library that has already been compiled). This should mostly
impact vendors and other folks who compile libc++.dylib themselves.
2. When the library is compiled with -fexceptions, the nothrow version
of `operator new` has no change. When the library is compiled with
-fno-exceptions, the nothrow version of `operator new` will now check
whether the throwing version of `operator new` has been overridden.
If it has not been overridden, then it will use an implementation
equivalent to that of the throwing `operator new`, except it will
return nullptr on failure to allocate (instead of terminating).
However, if the throwing `operator new` has been overridden, it is
now an error NOT to also override the nothrow `operator new`. Indeed,
there is no way for us to implement a valid nothrow `operator new`
without knowing the exact implementation of the throwing version.
rdar://103958777
Differential Revision: https://reviews.llvm.org/D150610
---
libcxx/docs/ReleaseNotes/18.rst | 23 +++++
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__overridable_function | 38 ++++++++
libcxx/include/new | 9 +-
libcxx/src/new.cpp | 79 +++++++++++-----
...new_not_overridden_fno_exceptions.pass.cpp | 56 ++++++++++++
.../new_dont_return_nullptr.pass.cpp | 37 ++++++++
libcxx/test/support/check_assertion.h | 6 ++
libcxxabi/src/stdlib_new_delete.cpp | 90 ++++++++++++++-----
9 files changed, 291 insertions(+), 48 deletions(-)
create mode 100644 libcxx/include/__overridable_function
create mode 100644 libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
create mode 100644 libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index ac78563aa73848f..bf017613a01b892 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -118,6 +118,29 @@ LLVM 20
ABI Affecting Changes
---------------------
+- When the shared/static library is built with ``-fno-exceptions``, the behavior of ``operator new`` was changed
+ to make it standards-conforming. In LLVM 17 and before, the throwing versions of ``operator new`` would return
+ ``nullptr`` upon failure to allocate, when the shared/static library was built with exceptions disabled. This
+ was non-conforming, since the throwing versions of ``operator new`` are never expected to return ``nullptr``, and
+ this non-conformance could actually lead to miscompiles in subtle cases.
+
+ Starting in LLVM 18, the throwing versions of ``operator new`` will abort the program when they fail to allocate
+ if the shared/static library has been built with ``-fno-exceptions``. This is consistent with the behavior of all
+ other potentially-throwing functions in the library, which abort the program instead of throwing when ``-fno-exceptions``
+ is used.
+
+ Furthermore, when the shared/static library is built with ``-fno-exceptions``, users who override the throwing
+ version of ``operator new`` will now need to also override the ``std::nothrow_t`` version of ``operator new`` if
+ they want to use it. Indeed, this is because there is no way to implement a conforming ``operator new(nothrow)``
+ from a conforming potentially-throwing ``operator new`` when compiled with ``-fno-exceptions``. In that case, using
+ ``operator new(nothrow)`` without overriding it explicitly but after overriding the throwing ``operator new`` will
+ result in an error.
+
+ Note that this change only impacts vendors/users that build the shared/static library themselves and pass
+ ``-DLIBCXX_ENABLE_EXCEPTIONS=OFF``, which is not the default configuration. If you are using the default
+ configuration of the library, the libc++ shared/static library will be built with exceptions enabled, and
+ there is no change between LLVM 17 and LLVM 18, even for users who build their own code using ``-fno-exceptions``.
+
- The symbol of a non-visible function part of ``std::system_error`` was removed.
This is not a breaking change as the private function ``__init`` was never referenced internally outside of the dylib
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 9b03430a87d8338..064b9cd3ed8973b 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -565,6 +565,7 @@ set(files
__numeric/transform_exclusive_scan.h
__numeric/transform_inclusive_scan.h
__numeric/transform_reduce.h
+ __overridable_function
__random/bernoulli_distribution.h
__random/binomial_distribution.h
__random/cauchy_distribution.h
diff --git a/libcxx/include/__overridable_function b/libcxx/include/__overridable_function
new file mode 100644
index 000000000000000..941a731e7bc2120
--- /dev/null
+++ b/libcxx/include/__overridable_function
@@ -0,0 +1,38 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___OVERRIDABLE_FUNCTION
+#define _LIBCPP___OVERRIDABLE_FUNCTION
+
+#include <__config>
+#include <cstdint>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE \
+ __attribute__((__section__("__TEXT,__lcxx_override,regular,pure_instructions")))
+
+template <class _Ret, class... _Args>
+_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__ptr)(_Args...)) noexcept {
+ // The linker places these at the start/end of the __lcxx_override section,
+ // which is where we put the definitions for any overridable function, via
+ // _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE.
+ //
+ // Placing the function in a separate section allows for this logic to be applied
+ // even in the scenario where clients are statically linking against libc++/libc++abi.
+ extern const uintptr_t __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
+ extern const uintptr_t __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
+ uintptr_t* __uptr = reinterpret_cast<uintptr_t*>(__ptr);
+
+ return __uptr < &__lcxx_override_start || __uptr > &__lcxx_override_end;
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___OVERRIDABLE_FUNCTION
diff --git a/libcxx/include/new b/libcxx/include/new
index 0a97c3e37add574..caeb9a5c8b4b811 100644
--- a/libcxx/include/new
+++ b/libcxx/include/new
@@ -90,6 +90,7 @@ void operator delete[](void* ptr, void*) noexcept;
#include <__availability>
#include <__config>
#include <__exception/exception.h>
+#include <__overridable_function>
#include <__type_traits/alignment_of.h>
#include <__type_traits/is_function.h>
#include <__type_traits/is_same.h>
@@ -214,7 +215,7 @@ inline constexpr destroying_delete_t destroying_delete{};
#if !defined(_LIBCPP_ABI_VCRUNTIME)
-_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz) _THROW_BAD_ALLOC;
+_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE void* operator new(std::size_t __sz) _THROW_BAD_ALLOC;
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz, const std::nothrow_t&) _NOEXCEPT _LIBCPP_NOALIAS;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p) _NOEXCEPT;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, const std::nothrow_t&) _NOEXCEPT;
@@ -222,7 +223,7 @@ _LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, const std::nothrow
_LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_SIZED_NEW_DELETE void operator delete(void* __p, std::size_t __sz) _NOEXCEPT;
#endif
-_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new[](std::size_t __sz) _THROW_BAD_ALLOC;
+_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE void* operator new[](std::size_t __sz) _THROW_BAD_ALLOC;
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new[](std::size_t __sz, const std::nothrow_t&) _NOEXCEPT _LIBCPP_NOALIAS;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete[](void* __p) _NOEXCEPT;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete[](void* __p, const std::nothrow_t&) _NOEXCEPT;
@@ -231,7 +232,7 @@ _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_SIZED_NEW_DELETE void operato
#endif
#ifndef _LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION
-_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC;
+_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE void* operator new(std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC;
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz, std::align_val_t, const std::nothrow_t&) _NOEXCEPT _LIBCPP_NOALIAS;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, std::align_val_t) _NOEXCEPT;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, std::align_val_t, const std::nothrow_t&) _NOEXCEPT;
@@ -239,7 +240,7 @@ _LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, std::align_val_t,
_LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_SIZED_NEW_DELETE void operator delete(void* __p, std::size_t __sz, std::align_val_t) _NOEXCEPT;
#endif
-_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new[](std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC;
+_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE void* operator new[](std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC;
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new[](std::size_t __sz, std::align_val_t, const std::nothrow_t&) _NOEXCEPT _LIBCPP_NOALIAS;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete[](void* __p, std::align_val_t) _NOEXCEPT;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete[](void* __p, std::align_val_t, const std::nothrow_t&) _NOEXCEPT;
diff --git a/libcxx/src/new.cpp b/libcxx/src/new.cpp
index 033bba5c1fc95b6..5cbebf74300f443 100644
--- a/libcxx/src/new.cpp
+++ b/libcxx/src/new.cpp
@@ -7,6 +7,8 @@
//===----------------------------------------------------------------------===//
#include <__memory/aligned_alloc.h>
+#include <__overridable_function>
+#include <cstddef>
#include <cstdlib>
#include <new>
@@ -15,6 +17,10 @@
// The code below is copied as-is into libc++abi's libcxxabi/src/stdlib_new_delete.cpp
// file. The version in this file is the canonical one.
+inline void __throw_bad_alloc_shim() { std::__throw_bad_alloc(); }
+
+# define _LIBCPP_ASSERT_SHIM(expr, str) _LIBCPP_ASSERT(expr, str)
+
// ------------------ BEGIN COPY ------------------
// Implement all new and delete operators as weak definitions
// in this shared library, so that they can be overridden by programs
@@ -38,39 +44,53 @@ static void* operator_new_impl(std::size_t size) noexcept {
_LIBCPP_WEAK void* operator new(std::size_t size) _THROW_BAD_ALLOC {
void* p = operator_new_impl(size);
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
if (p == nullptr)
- throw std::bad_alloc();
-# endif
+ __throw_bad_alloc_shim();
return p;
}
_LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
+# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+ _LIBCPP_ASSERT_SHIM(
+ !std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new)),
+ "libc++ was configured with exceptions disabled and `operator new(size_t)` has been overridden, "
+ "but `operator new(size_t, nothrow_t)` has not been overridden. This is problematic because "
+ "`operator new(size_t, nothrow_t)` must call `operator new(size_t)`, which will terminate in case "
+ "it fails to allocate, making it impossible for `operator new(size_t, nothrow_t)` to fulfill its "
+ "contract (since it should return nullptr upon failure).");
+
+ return operator_new_impl(size);
+# else
void* p = nullptr;
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
p = ::operator new(size);
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
}
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
return p;
+# endif
}
_LIBCPP_WEAK void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
_LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept {
+# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+ _LIBCPP_ASSERT_SHIM(
+ !std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new[])),
+ "libc++ was configured with exceptions disabled and `operator new[](size_t)` has been overridden, "
+ "but `operator new[](size_t, nothrow_t)` has not been overridden. This is problematic because "
+ "`operator new[](size_t, nothrow_t)` must call `operator new[](size_t)`, which will terminate in case "
+ "it fails to allocate, making it impossible for `operator new[](size_t, nothrow_t)` to fulfill its "
+ "contract (since it should return nullptr upon failure).");
+
+ return operator_new_impl(size);
+# else
void* p = nullptr;
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
p = ::operator new[](size);
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
}
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
return p;
+# endif
}
_LIBCPP_WEAK void operator delete(void* ptr) noexcept { std::free(ptr); }
@@ -109,24 +129,30 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
_LIBCPP_WEAK void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
void* p = operator_new_aligned_impl(size, alignment);
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
if (p == nullptr)
- throw std::bad_alloc();
-# endif
+ __throw_bad_alloc_shim();
return p;
}
_LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
+# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+ _LIBCPP_ASSERT_SHIM(
+ !std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new)),
+ "libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, "
+ "but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because "
+ "`operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will "
+ "terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` "
+ "to fulfill its contract (since it should return nullptr upon failure).");
+
+ return operator_new_aligned_impl(size, alignment);
+# else
void* p = nullptr;
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
p = ::operator new(size, alignment);
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
}
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
return p;
+# endif
}
_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
@@ -134,16 +160,25 @@ _LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment) _THRO
}
_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
+# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+ _LIBCPP_ASSERT_SHIM(
+ !std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new[])),
+ "libc++ was configured with exceptions disabled and `operator new[](size_t, align_val_t)` has been overridden, "
+ "but `operator new[](size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because "
+ "`operator new[](size_t, align_val_t, nothrow_t)` must call `operator new[](size_t, align_val_t)`, which will "
+ "terminate in case it fails to allocate, making it impossible for `operator new[](size_t, align_val_t, "
+ "nothrow_t)` "
+ "to fulfill its contract (since it should return nullptr upon failure).");
+
+ return operator_new_aligned_impl(size, alignment);
+# else
void* p = nullptr;
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
p = ::operator new[](size, alignment);
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
}
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
return p;
+# endif
}
_LIBCPP_WEAK void operator delete(void* ptr, std::align_val_t) noexcept { std::__libcpp_aligned_free(ptr); }
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
new file mode 100644
index 000000000000000..82c8bb90b8d8c4c
--- /dev/null
+++ b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// void* operator new(std::size_t, const std::nothrow_t&);
+// void* operator new(std::size_t, std::align_val_t, const std::nothrow_t&);
+// void* operator new[](std::size_t, const std::nothrow_t&);
+// void* operator new[](std::size_t, std::align_val_t, const std::nothrow_t&);
+
+// This test ensures that we catch the case where `new` has been overridden but `new(nothrow)`
+// has not been overridden, and the library is compiled with -fno-exceptions.
+//
+// In that case, it is impossible for libc++ to provide a Standards conforming implementation
+// of `new(nothrow)`, so the only viable option is to terminate the program.
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03
+// XFAIL: availability-verbose_abort-missing
+
+// TODO: We currently don't have a way to express that the built library was
+// compiled with -fno-exceptions, so if the library was built with support
+// for exceptions but we run the test suite without exceptions, this will
+// spuriously fail.
+// REQUIRES: no-exceptions
+
+#include <cstddef>
+#include <new>
+
+#include "check_assertion.h"
+
+// Override the throwing versions of operator new, but not the nothrow versions.
+alignas(32) char DummyData[32 * 3];
+void* operator new(std::size_t) { return DummyData; }
+void* operator new(std::size_t, std::align_val_t) { return DummyData; }
+void* operator new[](std::size_t) { return DummyData; }
+void* operator new[](std::size_t, std::align_val_t) { return DummyData; }
+
+void operator delete(void*) noexcept {}
+void operator delete(void*, std::align_val_t) noexcept {}
+void operator delete[](void*) noexcept {}
+void operator delete[](void*, std::align_val_t) noexcept {}
+
+int main(int, char**) {
+ std::size_t size = 3;
+ std::align_val_t align = static_cast<std::align_val_t>(32);
+ EXPECT_DEATH((void)operator new(size, std::nothrow));
+ EXPECT_DEATH((void)operator new(size, align, std::nothrow));
+ EXPECT_DEATH((void)operator new[](size, std::nothrow));
+ EXPECT_DEATH((void)operator new[](size, align, std::nothrow));
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
new file mode 100644
index 000000000000000..93a5a12349c97e6
--- /dev/null
+++ b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// void* operator new(std::size_t);
+// void* operator new(std::size_t, std::align_val_t);
+// void* operator new[](std::size_t);
+// void* operator new[](std::size_t, std::align_val_t);
+
+// This test ensures that we abort the program instead of returning nullptr
+// when we fail to satisfy the allocation request. The throwing versions of
+// `operator new` must never return nullptr on failure to allocate (per the
+// Standard) and the compiler actually relies on that for optimizations.
+// Returning nullptr from the throwing `operator new` can basically result
+// in miscompiles.
+
+// REQUIRES: has-unix-headers
+// REQUIRES: no-exceptions
+// UNSUPPORTED: c++03, c++11, c++14
+
+#include <cstddef>
+#include <limits>
+#include <new>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ EXPECT_DEATH((void)operator new(std::numeric_limits<std::size_t>::max()));
+ EXPECT_DEATH((void)operator new(std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32)));
+ EXPECT_DEATH((void)operator new[](std::numeric_limits<std::size_t>::max()));
+ EXPECT_DEATH((void)operator new[](std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32)));
+ return 0;
+}
diff --git a/libcxx/test/support/check_assertion.h b/libcxx/test/support/check_assertion.h
index 98dd95b11556e6c..34e41e8f0d8eaf8 100644
--- a/libcxx/test/support/check_assertion.h
+++ b/libcxx/test/support/check_assertion.h
@@ -10,6 +10,7 @@
#define TEST_SUPPORT_CHECK_ASSERTION_H
#include <cassert>
+#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cstdio>
@@ -257,9 +258,14 @@ void std::__libcpp_verbose_abort(char const* format, ...) {
std::exit(DeathTest::RK_Terminate);
}
+[[noreturn]] inline void abort_handler(int) {
+ std::exit(DeathTest::RK_Terminate);
+}
+
template <class Func>
inline bool ExpectDeath(const char* stmt, Func&& func, AssertionInfoMatcher Matcher) {
std::set_terminate(terminate_handler);
+ std::signal(SIGABRT, abort_handler);
DeathTest DT(Matcher);
DeathTest::ResultKind RK = DT.Run(func);
auto OnFailure = [&](const char* msg) {
diff --git a/libcxxabi/src/stdlib_new_delete.cpp b/libcxxabi/src/stdlib_new_delete.cpp
index 6c9990f063dde66..cfb8d78bda9d09b 100644
--- a/libcxxabi/src/stdlib_new_delete.cpp
+++ b/libcxxabi/src/stdlib_new_delete.cpp
@@ -7,7 +7,10 @@
//===----------------------------------------------------------------------===//
#include "__cxxabi_config.h"
+#include "abort_message.h"
#include <__memory/aligned_alloc.h>
+#include <__overridable_function>
+#include <cstddef>
#include <cstdlib>
#include <new>
@@ -25,6 +28,20 @@
# error libc++ and libc++abi seem to disagree on whether exceptions are enabled
#endif
+inline void __throw_bad_alloc_shim() {
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ throw std::bad_alloc();
+#else
+ abort_message("bad_alloc was thrown in -fno-exceptions mode");
+#endif
+}
+
+#define _LIBCPP_ASSERT_SHIM(expr, str) \
+ do { \
+ if (!expr) \
+ abort_message(str); \
+ } while (false)
+
// ------------------ BEGIN COPY ------------------
// Implement all new and delete operators as weak definitions
// in this shared library, so that they can be overridden by programs
@@ -49,25 +66,31 @@ static void* operator_new_impl(std::size_t size) noexcept {
_LIBCPP_WEAK
void* operator new(std::size_t size) _THROW_BAD_ALLOC {
void* p = operator_new_impl(size);
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
if (p == nullptr)
- throw std::bad_alloc();
-#endif
+ __throw_bad_alloc_shim();
return p;
}
_LIBCPP_WEAK
void* operator new(size_t size, const std::nothrow_t&) noexcept {
+#ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+ _LIBCPP_ASSERT_SHIM(
+ !std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new)),
+ "libc++ was configured with exceptions disabled and `operator new(size_t)` has been overridden, "
+ "but `operator new(size_t, nothrow_t)` has not been overridden. This is problematic because "
+ "`operator new(size_t, nothrow_t)` must call `operator new(size_t)`, which will terminate in case "
+ "it fails to allocate, making it impossible for `operator new(size_t, nothrow_t)` to fulfill its "
+ "contract (since it should return nullptr upon failure).");
+
+ return operator_new_impl(size);
+#else
void* p = nullptr;
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
-#endif // _LIBCPP_HAS_NO_EXCEPTIONS
p = ::operator new(size);
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
}
-#endif // _LIBCPP_HAS_NO_EXCEPTIONS
return p;
+#endif
}
_LIBCPP_WEAK
@@ -75,16 +98,24 @@ void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size)
_LIBCPP_WEAK
void* operator new[](size_t size, const std::nothrow_t&) noexcept {
+#ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+ _LIBCPP_ASSERT_SHIM(
+ !std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new[])),
+ "libc++ was configured with exceptions disabled and `operator new[](size_t)` has been overridden, "
+ "but `operator new[](size_t, nothrow_t)` has not been overridden. This is problematic because "
+ "`operator new[](size_t, nothrow_t)` must call `operator new[](size_t)`, which will terminate in case "
+ "it fails to allocate, making it impossible for `operator new[](size_t, nothrow_t)` to fulfill its "
+ "contract (since it should return nullptr upon failure).");
+
+ return operator_new_impl(size);
+#else
void* p = nullptr;
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
-#endif // _LIBCPP_HAS_NO_EXCEPTIONS
p = ::operator new[](size);
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
}
-#endif // _LIBCPP_HAS_NO_EXCEPTIONS
return p;
+#endif
}
_LIBCPP_WEAK
@@ -130,25 +161,31 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
_LIBCPP_WEAK
void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
void* p = operator_new_aligned_impl(size, alignment);
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
if (p == nullptr)
- throw std::bad_alloc();
-# endif
+ __throw_bad_alloc_shim();
return p;
}
_LIBCPP_WEAK
void* operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
+# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+ _LIBCPP_ASSERT_SHIM(
+ !std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new)),
+ "libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, "
+ "but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because "
+ "`operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will "
+ "terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` "
+ "to fulfill its contract (since it should return nullptr upon failure).");
+
+ return operator_new_aligned_impl(size, alignment);
+# else
void* p = nullptr;
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
p = ::operator new(size, alignment);
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
}
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
return p;
+# endif
}
_LIBCPP_WEAK
@@ -158,16 +195,25 @@ void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
_LIBCPP_WEAK
void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
+# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+ _LIBCPP_ASSERT_SHIM(
+ !std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new[])),
+ "libc++ was configured with exceptions disabled and `operator new[](size_t, align_val_t)` has been overridden, "
+ "but `operator new[](size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because "
+ "`operator new[](size_t, align_val_t, nothrow_t)` must call `operator new[](size_t, align_val_t)`, which will "
+ "terminate in case it fails to allocate, making it impossible for `operator new[](size_t, align_val_t, "
+ "nothrow_t)` "
+ "to fulfill its contract (since it should return nullptr upon failure).");
+
+ return operator_new_aligned_impl(size, alignment);
+# else
void* p = nullptr;
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
try {
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
p = ::operator new[](size, alignment);
-# ifndef _LIBCPP_HAS_NO_EXCEPTIONS
} catch (...) {
}
-# endif // _LIBCPP_HAS_NO_EXCEPTIONS
return p;
+# endif
}
_LIBCPP_WEAK
>From 0bf3564567072afc0d0d3f16153b304446d0fd9f Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 19 Oct 2023 10:39:39 -0700
Subject: [PATCH 2/9] Fix generated files issue
---
libcxx/utils/generate_iwyu_mapping.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libcxx/utils/generate_iwyu_mapping.py b/libcxx/utils/generate_iwyu_mapping.py
index 343538a6cae4819..610b784a97d6b1c 100644
--- a/libcxx/utils/generate_iwyu_mapping.py
+++ b/libcxx/utils/generate_iwyu_mapping.py
@@ -65,6 +65,8 @@ def generate_map(include):
continue
elif i == "__node_handle":
public = ["map", "set", "unordered_map", "unordered_set"]
+ elif i == '__overridable_function':
+ continue
elif i == "__pstl_algorithm":
continue
elif i == "__pstl_config_site.in":
>From 669f92677d47783a6856c85f1aef05ad356bb9da Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 19 Oct 2023 10:48:41 -0700
Subject: [PATCH 3/9] Only mark functions as overridable at their definition,
not at their declaration
---
libcxx/include/new | 9 ++--
libcxx/src/new.cpp | 12 +++--
libcxxabi/src/stdlib_new_delete.cpp | 70 +++++++++++++----------------
3 files changed, 42 insertions(+), 49 deletions(-)
diff --git a/libcxx/include/new b/libcxx/include/new
index caeb9a5c8b4b811..0a97c3e37add574 100644
--- a/libcxx/include/new
+++ b/libcxx/include/new
@@ -90,7 +90,6 @@ void operator delete[](void* ptr, void*) noexcept;
#include <__availability>
#include <__config>
#include <__exception/exception.h>
-#include <__overridable_function>
#include <__type_traits/alignment_of.h>
#include <__type_traits/is_function.h>
#include <__type_traits/is_same.h>
@@ -215,7 +214,7 @@ inline constexpr destroying_delete_t destroying_delete{};
#if !defined(_LIBCPP_ABI_VCRUNTIME)
-_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE void* operator new(std::size_t __sz) _THROW_BAD_ALLOC;
+_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz) _THROW_BAD_ALLOC;
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz, const std::nothrow_t&) _NOEXCEPT _LIBCPP_NOALIAS;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p) _NOEXCEPT;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, const std::nothrow_t&) _NOEXCEPT;
@@ -223,7 +222,7 @@ _LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, const std::nothrow
_LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_SIZED_NEW_DELETE void operator delete(void* __p, std::size_t __sz) _NOEXCEPT;
#endif
-_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE void* operator new[](std::size_t __sz) _THROW_BAD_ALLOC;
+_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new[](std::size_t __sz) _THROW_BAD_ALLOC;
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new[](std::size_t __sz, const std::nothrow_t&) _NOEXCEPT _LIBCPP_NOALIAS;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete[](void* __p) _NOEXCEPT;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete[](void* __p, const std::nothrow_t&) _NOEXCEPT;
@@ -232,7 +231,7 @@ _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_SIZED_NEW_DELETE void operato
#endif
#ifndef _LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION
-_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE void* operator new(std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC;
+_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC;
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new(std::size_t __sz, std::align_val_t, const std::nothrow_t&) _NOEXCEPT _LIBCPP_NOALIAS;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, std::align_val_t) _NOEXCEPT;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, std::align_val_t, const std::nothrow_t&) _NOEXCEPT;
@@ -240,7 +239,7 @@ _LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete(void* __p, std::align_val_t,
_LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_AVAILABILITY_SIZED_NEW_DELETE void operator delete(void* __p, std::size_t __sz, std::align_val_t) _NOEXCEPT;
#endif
-_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE void* operator new[](std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC;
+_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new[](std::size_t __sz, std::align_val_t) _THROW_BAD_ALLOC;
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_OVERRIDABLE_FUNC_VIS void* operator new[](std::size_t __sz, std::align_val_t, const std::nothrow_t&) _NOEXCEPT _LIBCPP_NOALIAS;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete[](void* __p, std::align_val_t) _NOEXCEPT;
_LIBCPP_OVERRIDABLE_FUNC_VIS void operator delete[](void* __p, std::align_val_t, const std::nothrow_t&) _NOEXCEPT;
diff --git a/libcxx/src/new.cpp b/libcxx/src/new.cpp
index 5cbebf74300f443..29d0fa4af2734c8 100644
--- a/libcxx/src/new.cpp
+++ b/libcxx/src/new.cpp
@@ -42,7 +42,7 @@ static void* operator_new_impl(std::size_t size) noexcept {
return p;
}
-_LIBCPP_WEAK void* operator new(std::size_t size) _THROW_BAD_ALLOC {
+_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new(std::size_t size) _THROW_BAD_ALLOC {
void* p = operator_new_impl(size);
if (p == nullptr)
__throw_bad_alloc_shim();
@@ -70,7 +70,9 @@ _LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
# endif
}
-_LIBCPP_WEAK void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
+_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new[](size_t size) _THROW_BAD_ALLOC {
+ return ::operator new(size);
+}
_LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept {
# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
@@ -127,7 +129,8 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
return p;
}
-_LIBCPP_WEAK void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void*
+operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
void* p = operator_new_aligned_impl(size, alignment);
if (p == nullptr)
__throw_bad_alloc_shim();
@@ -155,7 +158,8 @@ _LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const s
# endif
}
-_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void*
+operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
return ::operator new(size, alignment);
}
diff --git a/libcxxabi/src/stdlib_new_delete.cpp b/libcxxabi/src/stdlib_new_delete.cpp
index cfb8d78bda9d09b..76da02d09ce06c6 100644
--- a/libcxxabi/src/stdlib_new_delete.cpp
+++ b/libcxxabi/src/stdlib_new_delete.cpp
@@ -63,16 +63,14 @@ static void* operator_new_impl(std::size_t size) noexcept {
return p;
}
-_LIBCPP_WEAK
-void* operator new(std::size_t size) _THROW_BAD_ALLOC {
+_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new(std::size_t size) _THROW_BAD_ALLOC {
void* p = operator_new_impl(size);
if (p == nullptr)
__throw_bad_alloc_shim();
return p;
}
-_LIBCPP_WEAK
-void* operator new(size_t size, const std::nothrow_t&) noexcept {
+_LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
#ifdef _LIBCPP_HAS_NO_EXCEPTIONS
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new)),
@@ -93,11 +91,11 @@ void* operator new(size_t size, const std::nothrow_t&) noexcept {
#endif
}
-_LIBCPP_WEAK
-void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
+_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new[](size_t size) _THROW_BAD_ALLOC {
+ return ::operator new(size);
+}
-_LIBCPP_WEAK
-void* operator new[](size_t size, const std::nothrow_t&) noexcept {
+_LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept {
#ifdef _LIBCPP_HAS_NO_EXCEPTIONS
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new[])),
@@ -118,23 +116,17 @@ void* operator new[](size_t size, const std::nothrow_t&) noexcept {
#endif
}
-_LIBCPP_WEAK
-void operator delete(void* ptr) noexcept { std::free(ptr); }
+_LIBCPP_WEAK void operator delete(void* ptr) noexcept { std::free(ptr); }
-_LIBCPP_WEAK
-void operator delete(void* ptr, const std::nothrow_t&) noexcept { ::operator delete(ptr); }
+_LIBCPP_WEAK void operator delete(void* ptr, const std::nothrow_t&) noexcept { ::operator delete(ptr); }
-_LIBCPP_WEAK
-void operator delete(void* ptr, size_t) noexcept { ::operator delete(ptr); }
+_LIBCPP_WEAK void operator delete(void* ptr, size_t) noexcept { ::operator delete(ptr); }
-_LIBCPP_WEAK
-void operator delete[](void* ptr) noexcept { ::operator delete(ptr); }
+_LIBCPP_WEAK void operator delete[](void* ptr) noexcept { ::operator delete(ptr); }
-_LIBCPP_WEAK
-void operator delete[](void* ptr, const std::nothrow_t&) noexcept { ::operator delete[](ptr); }
+_LIBCPP_WEAK void operator delete[](void* ptr, const std::nothrow_t&) noexcept { ::operator delete[](ptr); }
-_LIBCPP_WEAK
-void operator delete[](void* ptr, size_t) noexcept { ::operator delete[](ptr); }
+_LIBCPP_WEAK void operator delete[](void* ptr, size_t) noexcept { ::operator delete[](ptr); }
#if !defined(_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION)
@@ -158,16 +150,15 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
return p;
}
-_LIBCPP_WEAK
-void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void*
+operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
void* p = operator_new_aligned_impl(size, alignment);
if (p == nullptr)
__throw_bad_alloc_shim();
return p;
}
-_LIBCPP_WEAK
-void* operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
+_LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new)),
@@ -188,13 +179,12 @@ void* operator new(size_t size, std::align_val_t alignment, const std::nothrow_t
# endif
}
-_LIBCPP_WEAK
-void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void*
+operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
return ::operator new(size, alignment);
}
-_LIBCPP_WEAK
-void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
+_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new[])),
@@ -216,27 +206,27 @@ void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow
# endif
}
-_LIBCPP_WEAK
-void operator delete(void* ptr, std::align_val_t) noexcept { std::__libcpp_aligned_free(ptr); }
+_LIBCPP_WEAK void operator delete(void* ptr, std::align_val_t) noexcept { std::__libcpp_aligned_free(ptr); }
-_LIBCPP_WEAK
-void operator delete(void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept {
+_LIBCPP_WEAK void operator delete(void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept {
::operator delete(ptr, alignment);
}
-_LIBCPP_WEAK
-void operator delete(void* ptr, size_t, std::align_val_t alignment) noexcept { ::operator delete(ptr, alignment); }
+_LIBCPP_WEAK void operator delete(void* ptr, size_t, std::align_val_t alignment) noexcept {
+ ::operator delete(ptr, alignment);
+}
-_LIBCPP_WEAK
-void operator delete[](void* ptr, std::align_val_t alignment) noexcept { ::operator delete(ptr, alignment); }
+_LIBCPP_WEAK void operator delete[](void* ptr, std::align_val_t alignment) noexcept {
+ ::operator delete(ptr, alignment);
+}
-_LIBCPP_WEAK
-void operator delete[](void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept {
+_LIBCPP_WEAK void operator delete[](void* ptr, std::align_val_t alignment, const std::nothrow_t&) noexcept {
::operator delete[](ptr, alignment);
}
-_LIBCPP_WEAK
-void operator delete[](void* ptr, size_t, std::align_val_t alignment) noexcept { ::operator delete[](ptr, alignment); }
+_LIBCPP_WEAK void operator delete[](void* ptr, size_t, std::align_val_t alignment) noexcept {
+ ::operator delete[](ptr, alignment);
+}
#endif // !_LIBCPP_HAS_NO_LIBRARY_ALIGNED_ALLOCATION
// ------------------ END COPY ------------------
>From 7a9f17a4c7688958fce8bfbe80dc2e85ff37952f Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 19 Oct 2023 10:52:31 -0700
Subject: [PATCH 4/9] Format added test
---
.../support.dynamic/new_dont_return_nullptr.pass.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
index 93a5a12349c97e6..548046b0ab43d28 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/new_dont_return_nullptr.pass.cpp
@@ -29,9 +29,9 @@
#include "check_assertion.h"
int main(int, char**) {
- EXPECT_DEATH((void)operator new(std::numeric_limits<std::size_t>::max()));
- EXPECT_DEATH((void)operator new(std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32)));
- EXPECT_DEATH((void)operator new[](std::numeric_limits<std::size_t>::max()));
- EXPECT_DEATH((void)operator new[](std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32)));
- return 0;
+ EXPECT_DEATH((void)operator new(std::numeric_limits<std::size_t>::max()));
+ EXPECT_DEATH((void)operator new(std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32)));
+ EXPECT_DEATH((void)operator new[](std::numeric_limits<std::size_t>::max()));
+ EXPECT_DEATH((void)operator new[](std::numeric_limits<std::size_t>::max(), static_cast<std::align_val_t>(32)));
+ return 0;
}
>From 5a2dff7528dc7eb173ceb6b37068410ed310ed1d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 19 Oct 2023 12:31:12 -0700
Subject: [PATCH 5/9] Fix Python string quoting for darker
---
libcxx/utils/generate_iwyu_mapping.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/utils/generate_iwyu_mapping.py b/libcxx/utils/generate_iwyu_mapping.py
index 610b784a97d6b1c..88840eff8f6e293 100644
--- a/libcxx/utils/generate_iwyu_mapping.py
+++ b/libcxx/utils/generate_iwyu_mapping.py
@@ -65,7 +65,7 @@ def generate_map(include):
continue
elif i == "__node_handle":
public = ["map", "set", "unordered_map", "unordered_set"]
- elif i == '__overridable_function':
+ elif i == "__overridable_function":
continue
elif i == "__pstl_algorithm":
continue
>From 87d28d737ed982fb55e2046ef08506138895c308 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 19 Oct 2023 12:35:45 -0700
Subject: [PATCH 6/9] Add missing pragma system header
---
libcxx/include/__overridable_function | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libcxx/include/__overridable_function b/libcxx/include/__overridable_function
index 941a731e7bc2120..b7b05f45013454e 100644
--- a/libcxx/include/__overridable_function
+++ b/libcxx/include/__overridable_function
@@ -13,6 +13,10 @@
#include <__config>
#include <cstdint>
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
_LIBCPP_BEGIN_NAMESPACE_STD
#define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE \
>From 7d9c0766d3e437c02da1bd70df0e4aceaa639f6c Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Sun, 5 Nov 2023 16:12:34 -0700
Subject: [PATCH 7/9] Implement mechanism for ELF and hopefully handle
platforms like Windows that lack a mechanism
---
libcxx/include/__overridable_function | 101 +++++++++++++++---
libcxx/src/new.cpp | 8 ++
...new_not_overridden_fno_exceptions.pass.cpp | 3 +
libcxxabi/src/stdlib_new_delete.cpp | 8 ++
4 files changed, 108 insertions(+), 12 deletions(-)
diff --git a/libcxx/include/__overridable_function b/libcxx/include/__overridable_function
index b7b05f45013454e..fa6fadfa639edef 100644
--- a/libcxx/include/__overridable_function
+++ b/libcxx/include/__overridable_function
@@ -19,24 +19,101 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-#define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE \
- __attribute__((__section__("__TEXT,__lcxx_override,regular,pure_instructions")))
+//
+// This file provides the std::__is_function_overridden utility, which allows checking
+// whether an overridable function (typically a weak symbol) like `operator new`
+// has been overridden by a user or not.
+//
+// This is a low-level utility which does not work on all platforms, since it needs
+// to make assumptions about the object file format in use. Furthermore, it requires
+// the "base definition" of the function (the one we want to check whether it has been
+// overridden) to be annotated with the _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro.
+//
+// This currently works with Mach-O files (used on Darwin) and with ELF files (used on Linux
+// and others). On platforms where we know how to implement this detection, the macro
+// _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION is defined to 1, and it is defined to 0 on
+// other platforms. The _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro is defined to
+// nothing on unsupported platforms so that it can be used to decorate functions regardless
+// of whether detection is actually supported.
+//
+// How does this work?
+// -------------------
+//
+// Let's say we want to check whether a weak function `f` has been overridden by the user.
+// The general mechanism works by placing `f`'s definition (in the libc++ built library)
+// inside a special section, which we do using the `__section__` attribute via the
+// _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro.
+//
+// Then, when comes the time to check whether the function has been overridden, we take
+// the address of the function and we check whether it falls inside the special function
+// we created. This can be done by finding pointers to the start and the end of the section
+// (which is done differently for ELF and Mach-O), and then checking whether `f` falls
+// within those bounds. If it falls within those bounds, then `f` is still inside the
+// special section and so it is the version we defined in the libc++ built library, i.e.
+// it was not overridden. Otherwise, it was overridden by the user because it falls
+// outside of the section.
+//
+// Important note
+// --------------
+//
+// This mechanism should never be used outside of the libc++ built library. In particular,
+// attempting to use this within the libc++ headers will not work at all because we don't
+// want to be defining special sections inside user's executables which use our headers.
+// This is provided inside libc++'s include tree solely to make it easier to share with
+// libc++abi, which needs the same mechanism.
+//
+
+#if _LIBCPP_OBJECT_FORMAT_MACHO
+
+# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
+# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE \
+ __attribute__((__section__("__TEXT,__lcxx_override,regular,pure_instructions")))
template <class _Ret, class... _Args>
-_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__ptr)(_Args...)) noexcept {
- // The linker places these at the start/end of the __lcxx_override section,
- // which is where we put the definitions for any overridable function, via
- // _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE.
+_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) noexcept {
+ // Declare two dummy bytes and give them these special `__asm` values. These values are
+ // defined by the linker, which means that referring to `&__lcxx_override_start` will
+ // effectively refer to the address where the section starts (and same for the end).
+ extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
+ extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
+
+ // Now get a uintptr_t out of these locations, and out of the function pointer.
+ uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
+ uintptr_t __end = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
+ uintptr_t __ptr = reinterpret_cast<uintptr_t>(__fptr);
+
+ // Finally, the function was overridden if it falls outside of the section's bounds.
+ return __ptr < __start || __ptr > __end;
+}
+
+#elif _LIBCPP_OBJECT_FORMAT_ELF
+
+# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
+# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE __attribute__((__section__("__lcxx_override")))
+
+template <class _Ret, class... _Args>
+_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) noexcept {
+ // This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
+ // variables with those names corresponding to the start and the end of the section.
//
- // Placing the function in a separate section allows for this logic to be applied
- // even in the scenario where clients are statically linking against libc++/libc++abi.
- extern const uintptr_t __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
- extern const uintptr_t __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
- uintptr_t* __uptr = reinterpret_cast<uintptr_t*>(__ptr);
+ // See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
+ extern char __start___lcxx_override;
+ extern char __stop___lcxx_override;
- return __uptr < &__lcxx_override_start || __uptr > &__lcxx_override_end;
+ uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
+ uintptr_t __end = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
+ uintptr_t __ptr = reinterpret_cast<uintptr_t>(__fptr);
+
+ return __ptr < __start || __ptr > __end;
}
+#else
+
+# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 0
+# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE /* nothing */
+
+#endif
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___OVERRIDABLE_FUNCTION
diff --git a/libcxx/src/new.cpp b/libcxx/src/new.cpp
index 29d0fa4af2734c8..7a3ad4136010611 100644
--- a/libcxx/src/new.cpp
+++ b/libcxx/src/new.cpp
@@ -51,6 +51,7 @@ _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new(std
_LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new)),
"libc++ was configured with exceptions disabled and `operator new(size_t)` has been overridden, "
@@ -58,6 +59,7 @@ _LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
"`operator new(size_t, nothrow_t)` must call `operator new(size_t)`, which will terminate in case "
"it fails to allocate, making it impossible for `operator new(size_t, nothrow_t)` to fulfill its "
"contract (since it should return nullptr upon failure).");
+# endif
return operator_new_impl(size);
# else
@@ -76,6 +78,7 @@ _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new[](s
_LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept {
# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new[])),
"libc++ was configured with exceptions disabled and `operator new[](size_t)` has been overridden, "
@@ -83,6 +86,7 @@ _LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept {
"`operator new[](size_t, nothrow_t)` must call `operator new[](size_t)`, which will terminate in case "
"it fails to allocate, making it impossible for `operator new[](size_t, nothrow_t)` to fulfill its "
"contract (since it should return nullptr upon failure).");
+# endif
return operator_new_impl(size);
# else
@@ -139,6 +143,7 @@ operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
_LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new)),
"libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, "
@@ -146,6 +151,7 @@ _LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const s
"`operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will "
"terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` "
"to fulfill its contract (since it should return nullptr upon failure).");
+# endif
return operator_new_aligned_impl(size, alignment);
# else
@@ -165,6 +171,7 @@ operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new[])),
"libc++ was configured with exceptions disabled and `operator new[](size_t, align_val_t)` has been overridden, "
@@ -173,6 +180,7 @@ _LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const
"terminate in case it fails to allocate, making it impossible for `operator new[](size_t, align_val_t, "
"nothrow_t)` "
"to fulfill its contract (since it should return nullptr upon failure).");
+# endif
return operator_new_aligned_impl(size, alignment);
# else
diff --git a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
index 82c8bb90b8d8c4c..7aa51b365f74eba 100644
--- a/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
+++ b/libcxx/test/libcxx/language.support/support.dynamic/assert.nothrow_new_not_overridden_fno_exceptions.pass.cpp
@@ -21,6 +21,9 @@
// UNSUPPORTED: c++03
// XFAIL: availability-verbose_abort-missing
+// We only know how to diagnose this on platforms that use the ELF or Mach-O object file formats.
+// XFAIL: target={{.+}}-windows-{{.+}}
+
// TODO: We currently don't have a way to express that the built library was
// compiled with -fno-exceptions, so if the library was built with support
// for exceptions but we run the test suite without exceptions, this will
diff --git a/libcxxabi/src/stdlib_new_delete.cpp b/libcxxabi/src/stdlib_new_delete.cpp
index 76da02d09ce06c6..5bd9b5e58b36c6a 100644
--- a/libcxxabi/src/stdlib_new_delete.cpp
+++ b/libcxxabi/src/stdlib_new_delete.cpp
@@ -72,6 +72,7 @@ _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new(std
_LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
#ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new)),
"libc++ was configured with exceptions disabled and `operator new(size_t)` has been overridden, "
@@ -79,6 +80,7 @@ _LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
"`operator new(size_t, nothrow_t)` must call `operator new(size_t)`, which will terminate in case "
"it fails to allocate, making it impossible for `operator new(size_t, nothrow_t)` to fulfill its "
"contract (since it should return nullptr upon failure).");
+# endif
return operator_new_impl(size);
#else
@@ -97,6 +99,7 @@ _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new[](s
_LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept {
#ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new[])),
"libc++ was configured with exceptions disabled and `operator new[](size_t)` has been overridden, "
@@ -104,6 +107,7 @@ _LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept {
"`operator new[](size_t, nothrow_t)` must call `operator new[](size_t)`, which will terminate in case "
"it fails to allocate, making it impossible for `operator new[](size_t, nothrow_t)` to fulfill its "
"contract (since it should return nullptr upon failure).");
+# endif
return operator_new_impl(size);
#else
@@ -160,6 +164,7 @@ operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
_LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new)),
"libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, "
@@ -167,6 +172,7 @@ _LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const s
"`operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will "
"terminate in case it fails to allocate, making it impossible for `operator new(size_t, align_val_t, nothrow_t)` "
"to fulfill its contract (since it should return nullptr upon failure).");
+# endif
return operator_new_aligned_impl(size, alignment);
# else
@@ -186,6 +192,7 @@ operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
# ifdef _LIBCPP_HAS_NO_EXCEPTIONS
+# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
_LIBCPP_ASSERT_SHIM(
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new[])),
"libc++ was configured with exceptions disabled and `operator new[](size_t, align_val_t)` has been overridden, "
@@ -194,6 +201,7 @@ _LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const
"terminate in case it fails to allocate, making it impossible for `operator new[](size_t, align_val_t, "
"nothrow_t)` "
"to fulfill its contract (since it should return nullptr upon failure).");
+# endif
return operator_new_aligned_impl(size, alignment);
# else
>From 41a650b7875c1528c82082a0b79ed4962a461a70 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Mon, 6 Nov 2023 21:52:19 -1000
Subject: [PATCH 8/9] Fix -Wundef
---
libcxx/include/__overridable_function | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__overridable_function b/libcxx/include/__overridable_function
index fa6fadfa639edef..3b1645273ab6746 100644
--- a/libcxx/include/__overridable_function
+++ b/libcxx/include/__overridable_function
@@ -63,7 +63,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// libc++abi, which needs the same mechanism.
//
-#if _LIBCPP_OBJECT_FORMAT_MACHO
+#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE \
@@ -86,7 +86,7 @@ _LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) no
return __ptr < __start || __ptr > __end;
}
-#elif _LIBCPP_OBJECT_FORMAT_ELF
+#elif defined(_LIBCPP_OBJECT_FORMAT_ELF)
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE __attribute__((__section__("__lcxx_override")))
>From 1bdee43e664845a2b232ac7e436dfbd9c95847c3 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 8 Nov 2023 16:26:36 -1000
Subject: [PATCH 9/9] Fix ELF implementation
---
libcxx/include/__overridable_function | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/libcxx/include/__overridable_function b/libcxx/include/__overridable_function
index 3b1645273ab6746..81dd81bdc0ae8a2 100644
--- a/libcxx/include/__overridable_function
+++ b/libcxx/include/__overridable_function
@@ -17,8 +17,6 @@
# pragma GCC system_header
#endif
-_LIBCPP_BEGIN_NAMESPACE_STD
-
//
// This file provides the std::__is_function_overridden utility, which allows checking
// whether an overridable function (typically a weak symbol) like `operator new`
@@ -69,6 +67,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE \
__attribute__((__section__("__TEXT,__lcxx_override,regular,pure_instructions")))
+_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Ret, class... _Args>
_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) noexcept {
// Declare two dummy bytes and give them these special `__asm` values. These values are
@@ -85,27 +84,30 @@ _LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) no
// Finally, the function was overridden if it falls outside of the section's bounds.
return __ptr < __start || __ptr > __end;
}
+_LIBCPP_END_NAMESPACE_STD
#elif defined(_LIBCPP_OBJECT_FORMAT_ELF)
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE __attribute__((__section__("__lcxx_override")))
+// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
+// variables with those names corresponding to the start and the end of the section.
+//
+// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
+extern char __start___lcxx_override;
+extern char __stop___lcxx_override;
+
+_LIBCPP_BEGIN_NAMESPACE_STD
template <class _Ret, class... _Args>
_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) noexcept {
- // This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
- // variables with those names corresponding to the start and the end of the section.
- //
- // See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
- extern char __start___lcxx_override;
- extern char __stop___lcxx_override;
-
uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
uintptr_t __end = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
uintptr_t __ptr = reinterpret_cast<uintptr_t>(__fptr);
return __ptr < __start || __ptr > __end;
}
+_LIBCPP_END_NAMESPACE_STD
#else
@@ -114,6 +116,4 @@ _LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) no
#endif
-_LIBCPP_END_NAMESPACE_STD
-
#endif // _LIBCPP___OVERRIDABLE_FUNCTION
More information about the libc-commits
mailing list