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
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to