1313#include < __config>
1414#include < __configuration/abi.h>
1515#include < __cstddef/size_t.h>
16+ #include < __memory/addressof.h>
1617#include < __type_traits/integral_constant.h>
1718#include < __type_traits/is_trivially_destructible.h>
1819#include < __type_traits/negation.h>
@@ -26,28 +27,95 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2627// Trait representing whether a type requires an array cookie at the start of its allocation when
2728// allocated as `new T[n]` and deallocated as `delete[] array`.
2829//
29- // Under the Itanium C++ ABI [1], we know that an array cookie is available unless `T` is trivially
30- // destructible and the call to `operator delete[]` is not a sized operator delete. Under ABIs other
31- // than the Itanium ABI , we assume there are no array cookies.
30+ // Under the Itanium C++ ABI [1] and the ARM ABI which derives from it , we know that an array cookie is available
31+ // unless `T` is trivially destructible and the call to `operator delete[]` is not a sized operator delete. Under
32+ // other ABIs , we assume there are no array cookies.
3233//
3334// [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies
34- #ifdef _LIBCPP_ABI_ITANIUM
35+ #if defined( _LIBCPP_ABI_ITANIUM) || defined(_LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES)
3536// TODO: Use a builtin instead
36- // TODO: We should factor in the choice of the usual deallocation function in this determination.
37+ // TODO: We should factor in the choice of the usual deallocation function in this determination:
38+ // a cookie may be available in more cases but we ignore those for now.
3739template <class _Tp >
3840struct __has_array_cookie : _Not<is_trivially_destructible<_Tp> > {};
3941#else
4042template <class _Tp >
4143struct __has_array_cookie : false_type {};
4244#endif
4345
46+ struct __itanium_array_cookie {
47+ size_t __element_count;
48+ };
49+
50+ template <class _Tp >
51+ struct [[__gnu__::__aligned__(_LIBCPP_ALIGNOF(_Tp))]] __arm_array_cookie {
52+ size_t __element_size;
53+ size_t __element_count;
54+ };
55+
56+ // Return the element count in the array cookie located before the given pointer.
57+ //
58+ // In the Itanium ABI [1]
59+ // ----------------------
60+ // The element count is stored immediately before the first element of the array. If the preferred alignment
61+ // of array elements (which is different from the ABI alignment) is more than that of size_t, additional
62+ // padding bytes exist before the array cookie. Assuming array elements of size and alignment 16 bytes, that
63+ // gives us the following layout:
64+ //
65+ // |ooooooooxxxxxxxxaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd|
66+ // ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
67+ // | ^^^^^^^^ |
68+ // | | array elements
69+ // padding |
70+ // element count
71+ //
72+ //
73+ // In the Itanium ABI with ARM differences [2]
74+ // -------------------------------------------
75+ // The array cookie is stored at the very start of the allocation and it has the following form:
76+ //
77+ // struct array_cookie {
78+ // std::size_t element_size; // element_size != 0
79+ // std::size_t element_count;
80+ // };
81+ //
82+ // Assuming elements of size and alignment 32 bytes, this gives us the following layout:
83+ //
84+ // |xxxxxxxxXXXXXXXXooooooooooooooooaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
85+ // ^^^^^^^^ ^^^^^^^^^^^^^^^^
86+ // | ^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
87+ // element size | padding |
88+ // element count array elements
89+ //
90+ // We must be careful to take into account the alignment of the array cookie, which may result in padding
91+ // bytes between the element count and the first element of the array. Note that for ARM, the compiler
92+ // aligns the array cookie using the ABI alignment, not the preferred alignment of array elements.
93+ //
94+ // [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies
95+ // [2]: https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-C++-differences
4496template <class _Tp >
4597// Avoid failures when -fsanitize-address-poison-custom-array-cookie is enabled
46- _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE (" address" ) size_t __get_array_cookie(_Tp const * __ptr) {
98+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE (" address" ) size_t __get_array_cookie([[__maybe_unused__]] _Tp const * __ptr) {
4799 static_assert (
48100 __has_array_cookie<_Tp>::value, " Trying to access the array cookie of a type that is not guaranteed to have one" );
49- size_t const * __cookie = reinterpret_cast <size_t const *>(__ptr) - 1 ; // TODO: Use a builtin instead
50- return *__cookie;
101+
102+ #if defined(_LIBCPP_ABI_ITANIUM)
103+ using _ArrayCookie = __itanium_array_cookie;
104+ #elif defined(_LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES)
105+ using _ArrayCookie = __arm_array_cookie<_Tp>;
106+ #else
107+ static_assert (false , " The array cookie layout is unknown on this ABI" );
108+ struct _ArrayCookie { // dummy definition required to make the function parse
109+ size_t element_count;
110+ };
111+ #endif
112+
113+ char const * __array_cookie_start = reinterpret_cast <char const *>(__ptr) - sizeof (_ArrayCookie);
114+ _ArrayCookie __cookie;
115+ // This is necessary to avoid violating strict aliasing. It's valid because _ArrayCookie is an
116+ // implicit lifetime type.
117+ __builtin_memcpy (std::addressof (__cookie), __array_cookie_start, sizeof (_ArrayCookie));
118+ return __cookie.__element_count ;
51119}
52120
53121_LIBCPP_END_NAMESPACE_STD
0 commit comments