https://github.com/dyung updated https://github.com/llvm/llvm-project/pull/210364
>From 521b0ff9a6a85409f55264d92d838044c4e3b814 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
