Skip to content

Commit 4167ea2

Browse files
authored
Revert "[libcxx] Use alias for detecting overriden function" (#124431)
Reverts #120805 This change while desirable has two issues we discovered: - It is incompatible with `-funique-internal-linkage-names`, see #120805 (comment) - It is incompatible with `-fvisibility-global-new-delete=force-hidden`, see #123224 (comment) We were hoping to address both of these issues with #122983, but that change has other issues we haven't yet managed to resolve. For now, we have decided to revert the change to avoid shipping a broken feature in LLVM 20, and we plan to follow up with a new approach post branch.
1 parent 952685a commit 4167ea2

File tree

3 files changed

+94
-65
lines changed

3 files changed

+94
-65
lines changed

libcxx/src/include/overridable_function.h

+70-45
Original file line numberDiff line numberDiff line change
@@ -29,81 +29,106 @@
2929
// This is a low-level utility which does not work on all platforms, since it needs
3030
// to make assumptions about the object file format in use. Furthermore, it requires
3131
// the "base definition" of the function (the one we want to check whether it has been
32-
// overridden) to be defined using the _LIBCPP_OVERRIDABLE_FUNCTION macro.
32+
// overridden) to be annotated with the _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro.
3333
//
3434
// This currently works with Mach-O files (used on Darwin) and with ELF files (used on Linux
3535
// and others). On platforms where we know how to implement this detection, the macro
3636
// _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION is defined to 1, and it is defined to 0 on
37-
// other platforms. The _LIBCPP_OVERRIDABLE_FUNCTION macro expands to regular function
38-
// definition on unsupported platforms so that it can be used to decorate functions
39-
// regardless of whether detection is actually supported.
37+
// other platforms. The _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro is defined to
38+
// nothing on unsupported platforms so that it can be used to decorate functions regardless
39+
// of whether detection is actually supported.
4040
//
4141
// How does this work?
4242
// -------------------
4343
//
4444
// Let's say we want to check whether a weak function `f` has been overridden by the user.
45-
// The general mechanism works by defining a symbol `f_impl__` and a weak alias `f` via the
46-
// _LIBCPP_OVERRIDABLE_FUNCTION macro.
45+
// The general mechanism works by placing `f`'s definition (in the libc++ built library)
46+
// inside a special section, which we do using the `__section__` attribute via the
47+
// _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro.
4748
//
4849
// Then, when comes the time to check whether the function has been overridden, we take
49-
// the address of the function `f` and we check whether it is different from `f_impl__`.
50-
// If so it means the function was overriden by the user.
50+
// the address of the function and we check whether it falls inside the special function
51+
// we created. This can be done by finding pointers to the start and the end of the section
52+
// (which is done differently for ELF and Mach-O), and then checking whether `f` falls
53+
// within those bounds. If it falls within those bounds, then `f` is still inside the
54+
// special section and so it is the version we defined in the libc++ built library, i.e.
55+
// it was not overridden. Otherwise, it was overridden by the user because it falls
56+
// outside of the section.
5157
//
5258
// Important note
5359
// --------------
5460
//
55-
// This mechanism should never be used outside of the libc++ built library. Functions defined
56-
// with this macro must be defined at global scope.
61+
// This mechanism should never be used outside of the libc++ built library. In particular,
62+
// attempting to use this within the libc++ headers will not work at all because we don't
63+
// want to be defining special sections inside user's executables which use our headers.
5764
//
5865

5966
#if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
6067

61-
_LIBCPP_BEGIN_NAMESPACE_STD
62-
63-
template <auto _Func>
64-
_LIBCPP_HIDE_FROM_ABI constexpr bool __is_function_overridden();
68+
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
69+
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE \
70+
__attribute__((__section__("__TEXT,__lcxx_override,regular,pure_instructions")))
6571

72+
_LIBCPP_BEGIN_NAMESPACE_STD
73+
template <class _Ret, class... _Args>
74+
_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) noexcept {
75+
// Declare two dummy bytes and give them these special `__asm` values. These values are
76+
// defined by the linker, which means that referring to `&__lcxx_override_start` will
77+
// effectively refer to the address where the section starts (and same for the end).
78+
extern char __lcxx_override_start __asm("section$start$__TEXT$__lcxx_override");
79+
extern char __lcxx_override_end __asm("section$end$__TEXT$__lcxx_override");
80+
81+
// Now get a uintptr_t out of these locations, and out of the function pointer.
82+
uintptr_t __start = reinterpret_cast<uintptr_t>(&__lcxx_override_start);
83+
uintptr_t __end = reinterpret_cast<uintptr_t>(&__lcxx_override_end);
84+
uintptr_t __ptr = reinterpret_cast<uintptr_t>(__fptr);
85+
86+
# if __has_feature(ptrauth_calls)
87+
// We must pass a void* to ptrauth_strip since it only accepts a pointer type. Also, in particular,
88+
// we must NOT pass a function pointer, otherwise we will strip the function pointer, and then attempt
89+
// to authenticate and re-sign it when casting it to a uintptr_t again, which will fail because we just
90+
// stripped the function pointer. See rdar://122927845.
91+
__ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
92+
# endif
93+
94+
// Finally, the function was overridden if it falls outside of the section's bounds.
95+
return __ptr < __start || __ptr > __end;
96+
}
6697
_LIBCPP_END_NAMESPACE_STD
6798

68-
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
69-
# define _LIBCPP_OVERRIDABLE_FUNCTION(symbol, type, name, arglist) \
70-
static __attribute__((used)) type symbol##_impl__ arglist __asm__("_" _LIBCPP_TOSTRING(symbol)); \
71-
__asm__(".globl _" _LIBCPP_TOSTRING(symbol)); \
72-
__asm__(".weak_definition _" _LIBCPP_TOSTRING(symbol)); \
73-
extern __typeof(symbol##_impl__) name __attribute__((weak_import)); \
74-
_LIBCPP_BEGIN_NAMESPACE_STD \
75-
template <> \
76-
inline bool __is_function_overridden<static_cast<type(*) arglist>(name)>() { \
77-
return static_cast<type(*) arglist>(name) != symbol##_impl__; \
78-
} \
79-
_LIBCPP_END_NAMESPACE_STD \
80-
static type symbol##_impl__ arglist
81-
82-
#elif defined(_LIBCPP_OBJECT_FORMAT_ELF)
99+
// The NVPTX linker cannot create '__start/__stop' sections.
100+
#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__)
83101

84-
_LIBCPP_BEGIN_NAMESPACE_STD
102+
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
103+
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE __attribute__((__section__("__lcxx_override")))
85104

86-
template <auto _Func>
87-
_LIBCPP_HIDE_FROM_ABI constexpr bool __is_function_overridden();
105+
// This is very similar to what we do for Mach-O above. The ELF linker will implicitly define
106+
// variables with those names corresponding to the start and the end of the section.
107+
//
108+
// See https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
109+
extern char __start___lcxx_override;
110+
extern char __stop___lcxx_override;
88111

112+
_LIBCPP_BEGIN_NAMESPACE_STD
113+
template <class _Ret, class... _Args>
114+
_LIBCPP_HIDE_FROM_ABI bool __is_function_overridden(_Ret (*__fptr)(_Args...)) noexcept {
115+
uintptr_t __start = reinterpret_cast<uintptr_t>(&__start___lcxx_override);
116+
uintptr_t __end = reinterpret_cast<uintptr_t>(&__stop___lcxx_override);
117+
uintptr_t __ptr = reinterpret_cast<uintptr_t>(__fptr);
118+
119+
# if __has_feature(ptrauth_calls)
120+
// We must pass a void* to ptrauth_strip since it only accepts a pointer type. See full explanation above.
121+
__ptr = reinterpret_cast<uintptr_t>(ptrauth_strip(reinterpret_cast<void*>(__ptr), ptrauth_key_function_pointer));
122+
# endif
123+
124+
return __ptr < __start || __ptr > __end;
125+
}
89126
_LIBCPP_END_NAMESPACE_STD
90127

91-
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1
92-
# define _LIBCPP_OVERRIDABLE_FUNCTION(symbol, type, name, arglist) \
93-
static type symbol##_impl__ arglist __asm__(_LIBCPP_TOSTRING(symbol##_impl__)); \
94-
[[gnu::weak, gnu::alias(_LIBCPP_TOSTRING(symbol##_impl__))]] type name arglist; \
95-
_LIBCPP_BEGIN_NAMESPACE_STD \
96-
template <> \
97-
inline bool __is_function_overridden<static_cast<type(*) arglist>(name)>() { \
98-
return static_cast<type(*) arglist>(name) != symbol##_impl__; \
99-
} \
100-
_LIBCPP_END_NAMESPACE_STD \
101-
static type symbol##_impl__ arglist
102-
103128
#else
104129

105130
# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 0
106-
# define _LIBCPP_OVERRIDABLE_FUNCTION(symbol, type, name, arglist) _LIBCPP_WEAK type name arglist
131+
# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE /* nothing */
107132

108133
#endif
109134

libcxx/src/new.cpp

+12-10
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ static void* operator_new_impl(std::size_t size) {
4343
return p;
4444
}
4545

46-
_LIBCPP_OVERRIDABLE_FUNCTION(_Znwm, void*, operator new, (std::size_t size)) _THROW_BAD_ALLOC {
46+
_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new(std::size_t size) _THROW_BAD_ALLOC {
4747
void* p = operator_new_impl(size);
4848
if (p == nullptr)
4949
__throw_bad_alloc_shim();
@@ -54,7 +54,7 @@ _LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
5454
# if !_LIBCPP_HAS_EXCEPTIONS
5555
# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
5656
_LIBCPP_ASSERT_SHIM(
57-
!std::__is_function_overridden<static_cast<void* (*)(std::size_t)>(&operator new)>(),
57+
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new)),
5858
"libc++ was configured with exceptions disabled and `operator new(size_t)` has been overridden, "
5959
"but `operator new(size_t, nothrow_t)` has not been overridden. This is problematic because "
6060
"`operator new(size_t, nothrow_t)` must call `operator new(size_t)`, which will terminate in case "
@@ -74,15 +74,15 @@ _LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
7474
# endif
7575
}
7676

77-
_LIBCPP_OVERRIDABLE_FUNCTION(_Znam, void*, operator new[], (size_t size)) _THROW_BAD_ALLOC {
77+
_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new[](size_t size) _THROW_BAD_ALLOC {
7878
return ::operator new(size);
7979
}
8080

8181
_LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept {
8282
# if !_LIBCPP_HAS_EXCEPTIONS
8383
# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
8484
_LIBCPP_ASSERT_SHIM(
85-
!std::__is_function_overridden<static_cast<void* (*)(std::size_t)>(&operator new[])>(),
85+
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new[])),
8686
"libc++ was configured with exceptions disabled and `operator new[](size_t)` has been overridden, "
8787
"but `operator new[](size_t, nothrow_t)` has not been overridden. This is problematic because "
8888
"`operator new[](size_t, nothrow_t)` must call `operator new[](size_t)`, which will terminate in case "
@@ -136,8 +136,8 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
136136
return p;
137137
}
138138

139-
_LIBCPP_OVERRIDABLE_FUNCTION(_ZnwmSt11align_val_t, void*, operator new, (std::size_t size, std::align_val_t alignment))
140-
_THROW_BAD_ALLOC {
139+
_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void*
140+
operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
141141
void* p = operator_new_aligned_impl(size, alignment);
142142
if (p == nullptr)
143143
__throw_bad_alloc_shim();
@@ -148,7 +148,7 @@ _LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const s
148148
# if !_LIBCPP_HAS_EXCEPTIONS
149149
# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
150150
_LIBCPP_ASSERT_SHIM(
151-
!std::__is_function_overridden<static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new)>(),
151+
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new)),
152152
"libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, "
153153
"but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because "
154154
"`operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will "
@@ -168,14 +168,16 @@ _LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const s
168168
# endif
169169
}
170170

171-
_LIBCPP_OVERRIDABLE_FUNCTION(_ZnamSt11align_val_t, void*, operator new[], (size_t size, std::align_val_t alignment))
172-
_THROW_BAD_ALLOC { return ::operator new(size, alignment); }
171+
_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void*
172+
operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
173+
return ::operator new(size, alignment);
174+
}
173175

174176
_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
175177
# if !_LIBCPP_HAS_EXCEPTIONS
176178
# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
177179
_LIBCPP_ASSERT_SHIM(
178-
!std::__is_function_overridden<static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new[])>(),
180+
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new[])),
179181
"libc++ was configured with exceptions disabled and `operator new[](size_t, align_val_t)` has been overridden, "
180182
"but `operator new[](size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because "
181183
"`operator new[](size_t, align_val_t, nothrow_t)` must call `operator new[](size_t, align_val_t)`, which will "

libcxxabi/src/stdlib_new_delete.cpp

+12-10
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ static void* operator_new_impl(std::size_t size) {
6363
return p;
6464
}
6565

66-
_LIBCPP_OVERRIDABLE_FUNCTION(_Znwm, void*, operator new, (std::size_t size)) _THROW_BAD_ALLOC {
66+
_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new(std::size_t size) _THROW_BAD_ALLOC {
6767
void* p = operator_new_impl(size);
6868
if (p == nullptr)
6969
__throw_bad_alloc_shim();
@@ -74,7 +74,7 @@ _LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
7474
#if !_LIBCPP_HAS_EXCEPTIONS
7575
# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
7676
_LIBCPP_ASSERT_SHIM(
77-
!std::__is_function_overridden<static_cast<void* (*)(std::size_t)>(&operator new)>(),
77+
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new)),
7878
"libc++ was configured with exceptions disabled and `operator new(size_t)` has been overridden, "
7979
"but `operator new(size_t, nothrow_t)` has not been overridden. This is problematic because "
8080
"`operator new(size_t, nothrow_t)` must call `operator new(size_t)`, which will terminate in case "
@@ -94,15 +94,15 @@ _LIBCPP_WEAK void* operator new(size_t size, const std::nothrow_t&) noexcept {
9494
#endif
9595
}
9696

97-
_LIBCPP_OVERRIDABLE_FUNCTION(_Znam, void*, operator new[], (size_t size)) _THROW_BAD_ALLOC {
97+
_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void* operator new[](size_t size) _THROW_BAD_ALLOC {
9898
return ::operator new(size);
9999
}
100100

101101
_LIBCPP_WEAK void* operator new[](size_t size, const std::nothrow_t&) noexcept {
102102
#if !_LIBCPP_HAS_EXCEPTIONS
103103
# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
104104
_LIBCPP_ASSERT_SHIM(
105-
!std::__is_function_overridden<static_cast<void* (*)(std::size_t)>(&operator new[])>(),
105+
!std::__is_function_overridden(static_cast<void* (*)(std::size_t)>(&operator new[])),
106106
"libc++ was configured with exceptions disabled and `operator new[](size_t)` has been overridden, "
107107
"but `operator new[](size_t, nothrow_t)` has not been overridden. This is problematic because "
108108
"`operator new[](size_t, nothrow_t)` must call `operator new[](size_t)`, which will terminate in case "
@@ -156,8 +156,8 @@ static void* operator_new_aligned_impl(std::size_t size, std::align_val_t alignm
156156
return p;
157157
}
158158

159-
_LIBCPP_OVERRIDABLE_FUNCTION(_ZnwmSt11align_val_t, void*, operator new, (std::size_t size, std::align_val_t alignment))
160-
_THROW_BAD_ALLOC {
159+
_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void*
160+
operator new(std::size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
161161
void* p = operator_new_aligned_impl(size, alignment);
162162
if (p == nullptr)
163163
__throw_bad_alloc_shim();
@@ -168,7 +168,7 @@ _LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const s
168168
# if !_LIBCPP_HAS_EXCEPTIONS
169169
# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
170170
_LIBCPP_ASSERT_SHIM(
171-
!std::__is_function_overridden<static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new)>(),
171+
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new)),
172172
"libc++ was configured with exceptions disabled and `operator new(size_t, align_val_t)` has been overridden, "
173173
"but `operator new(size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because "
174174
"`operator new(size_t, align_val_t, nothrow_t)` must call `operator new(size_t, align_val_t)`, which will "
@@ -188,14 +188,16 @@ _LIBCPP_WEAK void* operator new(size_t size, std::align_val_t alignment, const s
188188
# endif
189189
}
190190

191-
_LIBCPP_OVERRIDABLE_FUNCTION(_ZnamSt11align_val_t, void*, operator new[], (size_t size, std::align_val_t alignment))
192-
_THROW_BAD_ALLOC { return ::operator new(size, alignment); }
191+
_LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE _LIBCPP_WEAK void*
192+
operator new[](size_t size, std::align_val_t alignment) _THROW_BAD_ALLOC {
193+
return ::operator new(size, alignment);
194+
}
193195

194196
_LIBCPP_WEAK void* operator new[](size_t size, std::align_val_t alignment, const std::nothrow_t&) noexcept {
195197
# if !_LIBCPP_HAS_EXCEPTIONS
196198
# if _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION
197199
_LIBCPP_ASSERT_SHIM(
198-
!std::__is_function_overridden<static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new[])>(),
200+
!std::__is_function_overridden(static_cast<void* (*)(std::size_t, std::align_val_t)>(&operator new[])),
199201
"libc++ was configured with exceptions disabled and `operator new[](size_t, align_val_t)` has been overridden, "
200202
"but `operator new[](size_t, align_val_t, nothrow_t)` has not been overridden. This is problematic because "
201203
"`operator new[](size_t, align_val_t, nothrow_t)` must call `operator new[](size_t, align_val_t)`, which will "

0 commit comments

Comments
 (0)