xref: /aosp_15_r20/bionic/tests/static_tls_layout_test.cpp (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *  * Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #define STATIC_TLS_LAYOUT_TEST
30 
31 #include "private/bionic_elf_tls.h"
32 
33 #include <string>
34 #include <tuple>
35 
36 #include <gtest/gtest.h>
37 
38 #include <android-base/silent_death_test.h>
39 
40 #include "private/bionic_tls.h"
41 
42 using namespace std::string_literals;
43 
44 struct AlignedSizeFlat {
45   size_t size = 0;
46   size_t align = 1;
47   size_t skew = 0;
48 };
49 
unflatten_size(AlignedSizeFlat flat)50 static TlsAlignedSize unflatten_size(AlignedSizeFlat flat) {
51   return TlsAlignedSize{.size = flat.size,
52                         .align = TlsAlign{
53                             .value = flat.align,
54                             .skew = flat.skew,
55                         }};
56 }
57 
TEST(static_tls_layout,reserve_tp_pair)58 TEST(static_tls_layout, reserve_tp_pair) {
59   auto reserve_tp = [](const AlignedSizeFlat& before, const AlignedSizeFlat& after,
60                        StaticTlsLayout layout = {}) {
61     auto allocs = layout.reserve_tp_pair(unflatten_size(before), unflatten_size(after));
62     return std::make_tuple(layout, allocs);
63   };
64 
65   StaticTlsLayout layout;
66   StaticTlsLayout::TpAllocations allocs;
67 
68   // Simple case.
69   std::tie(layout, allocs) = reserve_tp({.size = 8, .align = 2}, {.size = 16, .align = 2});
70   EXPECT_EQ(0u, allocs.before);
71   EXPECT_EQ(8u, allocs.tp);
72   EXPECT_EQ(8u, allocs.after);
73   EXPECT_EQ(24u, layout.size());
74   EXPECT_EQ(2u, layout.align_);
75 
76   // Zero-sized `before`
77   std::tie(layout, allocs) = reserve_tp({.size = 0}, {.size = 64, .align = 8});
78   EXPECT_EQ(0u, allocs.before);
79   EXPECT_EQ(0u, allocs.tp);
80   EXPECT_EQ(0u, allocs.after);
81 
82   // Zero-sized `after`
83   std::tie(layout, allocs) = reserve_tp({.size = 64, .align = 8}, {.size = 0});
84   EXPECT_EQ(0u, allocs.before);
85   EXPECT_EQ(64u, allocs.tp);
86   EXPECT_EQ(64u, allocs.after);
87 
88   // The `before` allocation is shifted forward to the TP.
89   std::tie(layout, allocs) = reserve_tp({.size = 1}, {.size = 64, .align = 8});
90   EXPECT_EQ(7u, allocs.before);
91   EXPECT_EQ(8u, allocs.tp);
92   EXPECT_EQ(8u, allocs.after);
93 
94   // Alignment gap between `before` and TP.
95   std::tie(layout, allocs) = reserve_tp({.size = 9, .align = 4}, {.size = 1});
96   EXPECT_EQ(0u, allocs.before);
97   EXPECT_EQ(12u, allocs.tp);
98   EXPECT_EQ(12u, allocs.after);
99   EXPECT_EQ(13u, layout.size());
100   EXPECT_EQ(4u, layout.align_);
101 
102   // Alignment gap between `before` and TP.
103   std::tie(layout, allocs) = reserve_tp({.size = 9, .align = 4}, {.size = 128, .align = 64});
104   EXPECT_EQ(52u, allocs.before);
105   EXPECT_EQ(64u, allocs.tp);
106   EXPECT_EQ(64u, allocs.after);
107   EXPECT_EQ(192u, layout.size());
108   EXPECT_EQ(64u, layout.align_);
109 
110   // Skew-aligned `before` with low alignment.
111   std::tie(layout, allocs) =
112       reserve_tp({.size = 1, .align = 4, .skew = 1}, {.size = 64, .align = 8});
113   EXPECT_EQ(5u, allocs.before);
114   EXPECT_EQ(8u, allocs.tp);
115 
116   // Skew-aligned `before` with high alignment.
117   std::tie(layout, allocs) = reserve_tp({.size = 48, .align = 64, .skew = 17}, {.size = 1});
118   EXPECT_EQ(17u, allocs.before);
119   EXPECT_EQ(128u, allocs.tp);
120 
121   // An unrelated byte precedes the pair in the layout. Make sure `before` is
122   // still aligned.
123   layout = {};
124   layout.reserve_type<char>();
125   std::tie(layout, allocs) = reserve_tp({.size = 12, .align = 16}, {.size = 1}, layout);
126   EXPECT_EQ(16u, allocs.before);
127   EXPECT_EQ(32u, allocs.tp);
128 
129   // Skew-aligned `after`.
130   std::tie(layout, allocs) =
131       reserve_tp({.size = 32, .align = 8}, {.size = 16, .align = 4, .skew = 3});
132   EXPECT_EQ(0u, allocs.before);
133   EXPECT_EQ(32u, allocs.tp);
134   EXPECT_EQ(35u, allocs.after);
135   EXPECT_EQ(51u, layout.size());
136 }
137 
138 // A "NUM_words" literal is the size in bytes of NUM words of memory.
operator ""_words(unsigned long long i)139 static size_t operator""_words(unsigned long long i) {
140   return i * sizeof(void*);
141 }
142 
143 using static_tls_layout_DeathTest = SilentDeathTest;
144 
TEST_F(static_tls_layout_DeathTest,arm)145 TEST_F(static_tls_layout_DeathTest, arm) {
146 #if !defined(__arm__) && !defined(__aarch64__)
147   GTEST_SKIP() << "test only applies to arm32/arm64 targets";
148 #endif
149 
150   auto reserve_exe = [](const AlignedSizeFlat& config) {
151     StaticTlsLayout layout;
152     TlsSegment seg = {.aligned_size = unflatten_size(config)};
153     layout.reserve_exe_segment_and_tcb(&seg, "prog");
154     return layout;
155   };
156 
157   auto underalign_error = [](size_t align, size_t offset) {
158     return R"(error: "prog": executable's TLS segment is underaligned: )"s
159            R"(alignment is )"s +
160            std::to_string(align) + R"( \(skew )" + std::to_string(offset) +
161            R"(\), needs to be at least (32 for ARM|64 for ARM64) Bionic)"s;
162   };
163 
164   // Amount of memory needed for negative TLS slots, given a segment p_align of
165   // 8 or 16 words.
166   const size_t base8 = __BIONIC_ALIGN(-MIN_TLS_SLOT, 8) * sizeof(void*);
167   const size_t base16 = __BIONIC_ALIGN(-MIN_TLS_SLOT, 16) * sizeof(void*);
168 
169   StaticTlsLayout layout;
170 
171   // An executable with a single word.
172   layout = reserve_exe({.size = 1_words, .align = 8_words});
173   EXPECT_EQ(base8 + MIN_TLS_SLOT * sizeof(void*), layout.offset_bionic_tcb());
174   EXPECT_EQ(base8, layout.offset_thread_pointer());
175   EXPECT_EQ(base8 + 8_words, layout.offset_exe());
176   EXPECT_EQ(base8 + 9_words, layout.size());
177   EXPECT_EQ(8_words, layout.align_);
178 
179   // Simple underalignment case.
180   EXPECT_DEATH(reserve_exe({.size = 1_words, .align = 1_words}), underalign_error(1_words, 0));
181 
182   // Skewed by 1 word is OK.
183   layout = reserve_exe({.size = 1_words, .align = 8_words, .skew = 1_words});
184   EXPECT_EQ(base8, layout.offset_thread_pointer());
185   EXPECT_EQ(base8 + 9_words, layout.offset_exe());
186   EXPECT_EQ(base8 + 10_words, layout.size());
187   EXPECT_EQ(8_words, layout.align_);
188 
189   // Skewed by 2 words would overlap Bionic slots, regardless of the p_align
190   // value.
191   EXPECT_DEATH(reserve_exe({.size = 1_words, .align = 8_words, .skew = 2_words}),
192                underalign_error(8_words, 2_words));
193   EXPECT_DEATH(reserve_exe({.size = 1_words, .align = 0x1000, .skew = 2_words}),
194                underalign_error(0x1000, 2_words));
195 
196   // Skewed by 8 words is OK again.
197   layout = reserve_exe({.size = 1_words, .align = 16_words, .skew = 8_words});
198   EXPECT_EQ(base16, layout.offset_thread_pointer());
199   EXPECT_EQ(base16 + 8_words, layout.offset_exe());
200   EXPECT_EQ(base16 + 9_words, layout.size());
201   EXPECT_EQ(16_words, layout.align_);
202 
203   // Skewed by 9 words is also OK. (The amount of skew doesn't need to be a
204   // multiple of anything.)
205   layout = reserve_exe({.size = 1_words, .align = 16_words, .skew = 9_words});
206   EXPECT_EQ(base16, layout.offset_thread_pointer());
207   EXPECT_EQ(base16 + 9_words, layout.offset_exe());
208   EXPECT_EQ(base16 + 10_words, layout.size());
209   EXPECT_EQ(16_words, layout.align_);
210 
211   // Skew with large alignment.
212   layout = reserve_exe({.size = 1_words, .align = 256_words, .skew = 8_words});
213   EXPECT_EQ(256_words, layout.offset_thread_pointer());
214   EXPECT_EQ(264_words, layout.offset_exe());
215   EXPECT_EQ(265_words, layout.size());
216   EXPECT_EQ(256_words, layout.align_);
217 }
218