[llvm-branch-commits] [libcxx] [libcxxabi] release/23.x: [libc++] Partially revert #208330 (#209928) (PR #210364)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jul 17 09:15:46 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: llvmbot
<details>
<summary>Changes</summary>
Backport db7356dde8312a86e79ec0d7b7f844878a1a743c
Requested by: @<!-- -->petrhosek
---
Full diff: https://github.com/llvm/llvm-project/pull/210364.diff
3 Files Affected:
- (modified) libcxx/src/include/overridable_function.h (+67-8)
- (modified) libcxx/src/new.cpp (+4-4)
- (modified) libcxxabi/src/stdlib_new_delete.cpp (+4-4)
``````````diff
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);
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/210364
More information about the llvm-branch-commits
mailing list