1700637cbSDimitry Andric // -*- C++ -*-
2700637cbSDimitry Andric //===----------------------------------------------------------------------===//
3700637cbSDimitry Andric //
4700637cbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5700637cbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
6700637cbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7700637cbSDimitry Andric //
8700637cbSDimitry Andric //===----------------------------------------------------------------------===//
9700637cbSDimitry Andric
10700637cbSDimitry Andric #ifndef _LIBCPP___MEMORY_ARRAY_COOKIE_H
11700637cbSDimitry Andric #define _LIBCPP___MEMORY_ARRAY_COOKIE_H
12700637cbSDimitry Andric
13700637cbSDimitry Andric #include <__config>
14700637cbSDimitry Andric #include <__configuration/abi.h>
15700637cbSDimitry Andric #include <__cstddef/size_t.h>
16*e64bea71SDimitry Andric #include <__memory/addressof.h>
17700637cbSDimitry Andric #include <__type_traits/integral_constant.h>
18700637cbSDimitry Andric #include <__type_traits/is_trivially_destructible.h>
19700637cbSDimitry Andric #include <__type_traits/negation.h>
20700637cbSDimitry Andric
21700637cbSDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
22700637cbSDimitry Andric # pragma GCC system_header
23700637cbSDimitry Andric #endif
24700637cbSDimitry Andric
25700637cbSDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
26700637cbSDimitry Andric
27700637cbSDimitry Andric // Trait representing whether a type requires an array cookie at the start of its allocation when
28700637cbSDimitry Andric // allocated as `new T[n]` and deallocated as `delete[] array`.
29700637cbSDimitry Andric //
30*e64bea71SDimitry Andric // Under the Itanium C++ ABI [1] and the ARM ABI which derives from it, we know that an array cookie is available
31*e64bea71SDimitry Andric // unless `T` is trivially destructible and the call to `operator delete[]` is not a sized operator delete. Under
32*e64bea71SDimitry Andric // other ABIs, we assume there are no array cookies.
33700637cbSDimitry Andric //
34700637cbSDimitry Andric // [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies
35*e64bea71SDimitry Andric #if defined(_LIBCPP_ABI_ITANIUM) || defined(_LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES)
36700637cbSDimitry Andric // TODO: Use a builtin instead
37*e64bea71SDimitry Andric // TODO: We should factor in the choice of the usual deallocation function in this determination:
38*e64bea71SDimitry Andric // a cookie may be available in more cases but we ignore those for now.
39700637cbSDimitry Andric template <class _Tp>
40700637cbSDimitry Andric struct __has_array_cookie : _Not<is_trivially_destructible<_Tp> > {};
41700637cbSDimitry Andric #else
42700637cbSDimitry Andric template <class _Tp>
43700637cbSDimitry Andric struct __has_array_cookie : false_type {};
44700637cbSDimitry Andric #endif
45700637cbSDimitry Andric
46*e64bea71SDimitry Andric struct __itanium_array_cookie {
47*e64bea71SDimitry Andric size_t __element_count;
48*e64bea71SDimitry Andric };
49*e64bea71SDimitry Andric
50*e64bea71SDimitry Andric template <class _Tp>
51*e64bea71SDimitry Andric struct [[__gnu__::__aligned__(_LIBCPP_ALIGNOF(_Tp))]] __arm_array_cookie {
52*e64bea71SDimitry Andric size_t __element_size;
53*e64bea71SDimitry Andric size_t __element_count;
54*e64bea71SDimitry Andric };
55*e64bea71SDimitry Andric
56*e64bea71SDimitry Andric // Return the element count in the array cookie located before the given pointer.
57*e64bea71SDimitry Andric //
58*e64bea71SDimitry Andric // In the Itanium ABI [1]
59*e64bea71SDimitry Andric // ----------------------
60*e64bea71SDimitry Andric // The element count is stored immediately before the first element of the array. If the preferred alignment
61*e64bea71SDimitry Andric // of array elements (which is different from the ABI alignment) is more than that of size_t, additional
62*e64bea71SDimitry Andric // padding bytes exist before the array cookie. Assuming array elements of size and alignment 16 bytes, that
63*e64bea71SDimitry Andric // gives us the following layout:
64*e64bea71SDimitry Andric //
65*e64bea71SDimitry Andric // |ooooooooxxxxxxxxaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd|
66*e64bea71SDimitry Andric // ^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
67*e64bea71SDimitry Andric // | ^^^^^^^^ |
68*e64bea71SDimitry Andric // | | array elements
69*e64bea71SDimitry Andric // padding |
70*e64bea71SDimitry Andric // element count
71*e64bea71SDimitry Andric //
72*e64bea71SDimitry Andric //
73*e64bea71SDimitry Andric // In the Itanium ABI with ARM differences [2]
74*e64bea71SDimitry Andric // -------------------------------------------
75*e64bea71SDimitry Andric // The array cookie is stored at the very start of the allocation and it has the following form:
76*e64bea71SDimitry Andric //
77*e64bea71SDimitry Andric // struct array_cookie {
78*e64bea71SDimitry Andric // std::size_t element_size; // element_size != 0
79*e64bea71SDimitry Andric // std::size_t element_count;
80*e64bea71SDimitry Andric // };
81*e64bea71SDimitry Andric //
82*e64bea71SDimitry Andric // Assuming elements of size and alignment 32 bytes, this gives us the following layout:
83*e64bea71SDimitry Andric //
84*e64bea71SDimitry Andric // |xxxxxxxxXXXXXXXXooooooooooooooooaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
85*e64bea71SDimitry Andric // ^^^^^^^^ ^^^^^^^^^^^^^^^^
86*e64bea71SDimitry Andric // | ^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
87*e64bea71SDimitry Andric // element size | padding |
88*e64bea71SDimitry Andric // element count array elements
89*e64bea71SDimitry Andric //
90*e64bea71SDimitry Andric // We must be careful to take into account the alignment of the array cookie, which may result in padding
91*e64bea71SDimitry Andric // bytes between the element count and the first element of the array. Note that for ARM, the compiler
92*e64bea71SDimitry Andric // aligns the array cookie using the ABI alignment, not the preferred alignment of array elements.
93*e64bea71SDimitry Andric //
94*e64bea71SDimitry Andric // [1]: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#array-cookies
95*e64bea71SDimitry Andric // [2]: https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Handle-C++-differences
96700637cbSDimitry Andric template <class _Tp>
97700637cbSDimitry Andric // Avoid failures when -fsanitize-address-poison-custom-array-cookie is enabled
__get_array_cookie(_Tp const * __ptr)98*e64bea71SDimitry Andric _LIBCPP_HIDE_FROM_ABI _LIBCPP_NO_SANITIZE("address") size_t __get_array_cookie([[__maybe_unused__]] _Tp const* __ptr) {
99700637cbSDimitry Andric static_assert(
100700637cbSDimitry Andric __has_array_cookie<_Tp>::value, "Trying to access the array cookie of a type that is not guaranteed to have one");
101*e64bea71SDimitry Andric
102*e64bea71SDimitry Andric #if defined(_LIBCPP_ABI_ITANIUM)
103*e64bea71SDimitry Andric using _ArrayCookie = __itanium_array_cookie;
104*e64bea71SDimitry Andric #elif defined(_LIBCPP_ABI_ITANIUM_WITH_ARM_DIFFERENCES)
105*e64bea71SDimitry Andric using _ArrayCookie = __arm_array_cookie<_Tp>;
106*e64bea71SDimitry Andric #else
107*e64bea71SDimitry Andric static_assert(false, "The array cookie layout is unknown on this ABI");
108*e64bea71SDimitry Andric struct _ArrayCookie { // dummy definition required to make the function parse
109*e64bea71SDimitry Andric size_t element_count;
110*e64bea71SDimitry Andric };
111*e64bea71SDimitry Andric #endif
112*e64bea71SDimitry Andric
113*e64bea71SDimitry Andric char const* __array_cookie_start = reinterpret_cast<char const*>(__ptr) - sizeof(_ArrayCookie);
114*e64bea71SDimitry Andric _ArrayCookie __cookie;
115*e64bea71SDimitry Andric // This is necessary to avoid violating strict aliasing. It's valid because _ArrayCookie is an
116*e64bea71SDimitry Andric // implicit lifetime type.
117*e64bea71SDimitry Andric __builtin_memcpy(std::addressof(__cookie), __array_cookie_start, sizeof(_ArrayCookie));
118*e64bea71SDimitry Andric return __cookie.__element_count;
119700637cbSDimitry Andric }
120700637cbSDimitry Andric
121700637cbSDimitry Andric _LIBCPP_END_NAMESPACE_STD
122700637cbSDimitry Andric
123700637cbSDimitry Andric #endif // _LIBCPP___MEMORY_ARRAY_COOKIE_H
124