https://github.com/llvmbot created 
https://github.com/llvm/llvm-project/pull/210364

Backport db7356dde8312a86e79ec0d7b7f844878a1a743c

Requested by: @petrhosek

>From 4118936e0fa37deadeda8b651130f30fc5a8b402 Mon Sep 17 00:00:00 2001
From: Petr Hosek <[email protected]>
Date: Thu, 16 Jul 2026 05:15:30 -0700
Subject: [PATCH] [libc++] Partially revert #208330 (#209928)

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.

(cherry picked from commit db7356dde8312a86e79ec0d7b7f844878a1a743c)
---
 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..e430ccda29f65 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);
 }
 

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to