[libcxx-commits] [libcxx] [libcxxabi] [libcxx] Partially revert #208330 (PR #209928)
Petr Hosek via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jul 16 00:55:00 PDT 2026
https://github.com/petrhosek updated https://github.com/llvm/llvm-project/pull/209928
>From 95bddae719c2df2320c19637706ccc1bc689d0f2 Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Wed, 15 Jul 2026 13:29:14 -0700
Subject: [PATCH 1/2] [libcxx] Partially revert #208330
We found out that the new mechanism for detecting overriden functions
does not support Arm Pointer Authentication (PAuth). Addressing this
limitation is going to require changes to Clang. In the meantime, we
switched back to the old mechanism when PAuth is enabled.
---
libcxx/src/include/overridable_function.h | 75 ++++++++++++++++++++---
libcxx/src/new.cpp | 8 +--
libcxxabi/src/stdlib_new_delete.cpp | 8 +--
3 files changed, 75 insertions(+), 16 deletions(-)
diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index 83439ad363208..e115b9483b727 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -21,12 +21,17 @@
// 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. 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.
+// 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 defined using the OVERRIDABLE_FUNCTION 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 OVERRIDABLE_FUNCTION macro is defined to perform a normal
+// function definition on unsupported platforms so that it can be used to define functions
+// regardless of whether detection is actually supported.
//
// How does this work?
// -------------------
@@ -38,6 +43,14 @@
// compares the address of `__impl_ref<f>::__impl_` with the address of `f` loaded from
// GOT: if `f` was overridden by the user in another TU, the addresses will be different.
//
+// When pointer authentication is used, the above mechanism doesn't work (yet) so we use
+// a different strategy placing `f`'s definition (in the libc++ built library) inside
+// a special section, which we do using the `__section__` attribute via the
+// OVERRIDABLE_FUNCTION 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 section we created. This can be done by finding pointers to the start and
+// the end of the section, and then checking whether `f` falls within those bounds.
+//
// Important note
// --------------
//
@@ -50,6 +63,10 @@
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
+# if !__has_feature(ptrauth_calls)
+
+# define OVERRIDABLE_FUNCTION [[gnu::weak]]
+
_LIBCPP_BEGIN_NAMESPACE_STD
namespace {
@@ -95,10 +112,52 @@ _LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
_LIBCPP_END_NAMESPACE_STD
-#else
+# else // __has_feature(ptrauth_calls)
+
+# include <ptrauth.h>
+
+# if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
+# define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__TEXT,__lcxx_override,regular,pure_instructions")]]
+// 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 __start___lcxx_override __asm("section$start$__TEXT$__lcxx_override");
+extern char __stop___lcxx_override __asm("section$end$__TEXT$__lcxx_override");
+# elif defined(_LIBCPP_OBJECT_FORMAT_ELF)
+// 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
+# define OVERRIDABLE_FUNCTION [[gnu::weak, gnu::section("__lcxx_override")]]
+extern char __start___lcxx_override;
+extern char __stop___lcxx_override;
+# endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+template <typename T, T* _Func>
+_LIBCPP_HIDE_FROM_ABI inline bool __is_function_overridden() noexcept {
+ 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>(_Func);
+
+ // We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
+ // we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
+ // to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
+ // stripped the function pointer. See rdar://122927845.
+ __ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
+
+ // Finally, the function was overridden if it falls outside of the section's bounds.
+ return __ptr < __start || __ptr > __end;
+}
+_LIBCPP_END_NAMESPACE_STD
+
+# endif // __has_feature(ptrauth_calls)
+
+#else // defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 0
+# define OVERRIDABLE_FUNCTION [[gnu::weak]]
-#endif
+#endif // defined(_LIBCPP_OBJECT_FORMAT_MACHO) || (defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__))
#endif // _LIBCPP_SRC_INCLUDE_OVERRIDABLE_FUNCTION_H
diff --git a/libcxx/src/new.cpp b/libcxx/src/new.cpp
index 0ccc3acad06aa..25bdaea32f57a 100644
--- a/libcxx/src/new.cpp
+++ b/libcxx/src/new.cpp
@@ -43,7 +43,7 @@ static void* operator_new_impl(std::size_t size) {
return p;
}
-[[gnu::weak]] void* operator new(std::size_t size) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size) _THROW_BAD_ALLOC {
void* p = operator_new_impl(size);
if (p == nullptr)
__throw_bad_alloc_shim();
@@ -74,7 +74,7 @@ static void* operator_new_impl(std::size_t size) {
# endif
}
-[[gnu::weak]] void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
+OVERRIDABLE_FUNCTION void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
[[gnu::weak]] void* operator new[](size_t size, const std::nothrow_t&) noexcept {
# if !_LIBCPP_HAS_EXCEPTIONS
@@ -134,7 +134,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
return p;
}
-[[gnu::weak]] void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION 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();
@@ -165,7 +165,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
# endif
}
-[[gnu::weak]] void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION 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 9f538948542c3..164a26335eb38 100644
--- a/libcxxabi/src/stdlib_new_delete.cpp
+++ b/libcxxabi/src/stdlib_new_delete.cpp
@@ -59,7 +59,7 @@ static void* operator_new_impl(std::size_t size) {
return p;
}
-[[gnu::weak]] void* operator new(std::size_t size) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new(std::size_t size) _THROW_BAD_ALLOC {
void* p = operator_new_impl(size);
if (p == nullptr)
__throw_bad_alloc_shim();
@@ -90,7 +90,7 @@ static void* operator_new_impl(std::size_t size) {
#endif
}
-[[gnu::weak]] void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
+OVERRIDABLE_FUNCTION void* operator new[](size_t size) _THROW_BAD_ALLOC { return ::operator new(size); }
[[gnu::weak]] void* operator new[](size_t size, const std::nothrow_t&) noexcept {
#if !_LIBCPP_HAS_EXCEPTIONS
@@ -150,7 +150,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
return p;
}
-[[gnu::weak]] void* operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION 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();
@@ -181,7 +181,7 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
# endif
}
-[[gnu::weak]] void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
+OVERRIDABLE_FUNCTION void* operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
return ::operator new(size, alignment);
}
>From b4008e3b2413286a8ee85119c1dfa8ac0e25f7f4 Mon Sep 17 00:00:00 2001
From: Petr Hosek <phosek at google.com>
Date: Thu, 16 Jul 2026 00:54:41 -0700
Subject: [PATCH 2/2] Formatting
---
libcxx/src/include/overridable_function.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/src/include/overridable_function.h b/libcxx/src/include/overridable_function.h
index e115b9483b727..e430ccda29f65 100644
--- a/libcxx/src/include/overridable_function.h
+++ b/libcxx/src/include/overridable_function.h
@@ -44,7 +44,7 @@
// GOT: if `f` was overridden by the user in another TU, the addresses will be different.
//
// When pointer authentication is used, the above mechanism doesn't work (yet) so we use
-// a different strategy placing `f`'s definition (in the libc++ built library) inside
+// a different strategy placing `f`'s definition (in the libc++ built library) inside
// a special section, which we do using the `__section__` attribute via the
// OVERRIDABLE_FUNCTION 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
More information about the libcxx-commits
mailing list