|
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 defined using the _LIBCPP_OVERRIDABLE_FUNCTION macro. |
| 32 | +// overridden) to be annotated with the _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE 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_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. |
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 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. |
47 | 48 | //
|
48 | 49 | // 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. |
51 | 57 | //
|
52 | 58 | // Important note
|
53 | 59 | // --------------
|
54 | 60 | //
|
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. |
57 | 64 | //
|
58 | 65 |
|
59 | 66 | #if defined(_LIBCPP_OBJECT_FORMAT_MACHO)
|
60 | 67 |
|
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"))) |
65 | 71 |
|
| 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 | +} |
66 | 97 | _LIBCPP_END_NAMESPACE_STD
|
67 | 98 |
|
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__) |
83 | 101 |
|
84 |
| -_LIBCPP_BEGIN_NAMESPACE_STD |
| 102 | +# define _LIBCPP_CAN_DETECT_OVERRIDDEN_FUNCTION 1 |
| 103 | +# define _LIBCPP_MAKE_OVERRIDABLE_FUNCTION_DETECTABLE __attribute__((__section__("__lcxx_override"))) |
85 | 104 |
|
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; |
88 | 111 |
|
| 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 | +} |
89 | 126 | _LIBCPP_END_NAMESPACE_STD
|
90 | 127 |
|
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 |
| - |
103 | 128 | #else
|
104 | 129 |
|
105 | 130 | # 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 */ |
107 | 132 |
|
108 | 133 | #endif
|
109 | 134 |
|
|
0 commit comments