|
29 | 29 | // This is a low-level utility which does not work on all platforms, since it needs
|
30 | 30 | // to make assumptions about the object file format in use. Furthermore, it requires
|
31 | 31 | // the "base definition" of the function (the one we want to check whether it has been
|
32 |
| -// overridden) to be annotated with the _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE macro. |
| 32 | +// overridden) to be defined using the _LIBCPP_OVERRIDABLE_FUNCTION macro. |
33 | 33 | //
|
34 | 34 | // This currently works with Mach-O files (used on Darwin) and with ELF files (used on Linux
|
35 | 35 | // and others). On platforms where we know how to implement this detection, the macro
|
36 | 36 | // _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION is defined to 1, and it is defined to 0 on
|
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. |
| 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. |
40 | 40 | //
|
41 | 41 | // How does this work?
|
42 | 42 | // -------------------
|
43 | 43 | //
|
44 | 44 | // Let's say we want to check whether a weak function `f` has been overridden by the user.
|
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. |
| 45 | +// The general mechanism works by defining a symbol `f_impl__` and a weak alias `f` via the |
| 46 | +// _LIBCPP_OVERRIDABLE_FUNCTION macro. |
48 | 47 | //
|
49 | 48 | // Then, when comes the time to check whether the function has been overridden, we take
|
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. |
| 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. |
57 | 51 | //
|
58 | 52 | // Important note
|
59 | 53 | // --------------
|
60 | 54 | //
|
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. |
| 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. |
64 | 57 | //
|
65 | 58 |
|
66 | 59 | #if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
|
67 | 60 |
|
68 |
| -# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1 |
69 |
| -# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE \ |
70 |
| - __attribute__((__section__("__TEXT,__lcxx_override,regular,pure_instructions"))) |
71 |
| - |
72 | 61 | _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 |
| -} |
97 |
| -_LIBCPP_END_NAMESPACE_STD |
98 | 62 |
|
99 |
| -// The NVPTX linker cannot create '__start/__stop' sections. |
100 |
| -#elif defined(_LIBCPP_OBJECT_FORMAT_ELF) && !defined(__NVPTX__) |
| 63 | +template <auto _Func> |
| 64 | +_LIBCPP_HIDE_FROM_ABI constexpr bool __is_function_overridden(); |
101 | 65 |
|
102 |
| -# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1 |
103 |
| -# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE __attribute__((__section__("__lcxx_override"))) |
| 66 | +_LIBCPP_END_NAMESPACE_STD |
104 | 67 |
|
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; |
| 68 | +# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1 |
| 69 | +# define _LIBCPP_OVERRIDABLE_FUNCTION(symbol, type, name, arglist) \ |
| 70 | + static 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 | + 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) |
111 | 83 |
|
112 | 84 | _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 |
| -} |
| 85 | + |
| 86 | +template <auto _Func> |
| 87 | +_LIBCPP_HIDE_FROM_ABI constexpr bool __is_function_overridden(); |
| 88 | + |
126 | 89 | _LIBCPP_END_NAMESPACE_STD
|
127 | 90 |
|
| 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 | + 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 | + |
128 | 103 | #else
|
129 | 104 |
|
130 | 105 | # define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 0
|
131 |
| -# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE /* nothing */ |
| 106 | +# define _LIBCPP_OVERRIDABLE_FUNCTION(symbol, type, name, arglist) _LIBCPP_WEAK type name arglist |
132 | 107 |
|
133 | 108 | #endif
|
134 | 109 |
|
|
0 commit comments