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 "private/bionic_tls.h"
39
40 using namespace std::string_literals;
41
42 struct AlignedSizeFlat {
43 size_t size = 0;
44 size_t align = 1;
45 size_t skew = 0;
46 };
47
unflatten_size(AlignedSizeFlat flat)48 static TlsAlignedSize unflatten_size(AlignedSizeFlat flat) {
49 return TlsAlignedSize{.size = flat.size,
50 .align = TlsAlign{
51 .value = flat.align,
52 .skew = flat.skew,
53 }};
54 }
55
TEST(static_tls_layout,reserve_tp_pair)56 TEST(static_tls_layout, reserve_tp_pair) {
57 auto reserve_tp = [](const AlignedSizeFlat& before, const AlignedSizeFlat& after,
58 StaticTlsLayout layout = {}) {
59 auto allocs = layout.reserve_tp_pair(unflatten_size(before), unflatten_size(after));
60 return std::make_tuple(layout, allocs);
61 };
62
63 StaticTlsLayout layout;
64 StaticTlsLayout::TpAllocations allocs;
65
66 // Simple case.
67 std::tie(layout, allocs) = reserve_tp({.size = 8, .align = 2}, {.size = 16, .align = 2});
68 EXPECT_EQ(0u, allocs.before);
69 EXPECT_EQ(8u, allocs.tp);
70 EXPECT_EQ(8u, allocs.after);
71 EXPECT_EQ(24u, layout.size());
72 EXPECT_EQ(2u, layout.align_);
73
74 // Zero-sized `before`
75 std::tie(layout, allocs) = reserve_tp({.size = 0}, {.size = 64, .align = 8});
76 EXPECT_EQ(0u, allocs.before);
77 EXPECT_EQ(0u, allocs.tp);
78 EXPECT_EQ(0u, allocs.after);
79
80 // Zero-sized `after`
81 std::tie(layout, allocs) = reserve_tp({.size = 64, .align = 8}, {.size = 0});
82 EXPECT_EQ(0u, allocs.before);
83 EXPECT_EQ(64u, allocs.tp);
84 EXPECT_EQ(64u, allocs.after);
85
86 // The `before` allocation is shifted forward to the TP.
87 std::tie(layout, allocs) = reserve_tp({.size = 1}, {.size = 64, .align = 8});
88 EXPECT_EQ(7u, allocs.before);
89 EXPECT_EQ(8u, allocs.tp);
90 EXPECT_EQ(8u, allocs.after);
91
92 // Alignment gap between `before` and TP.
93 std::tie(layout, allocs) = reserve_tp({.size = 9, .align = 4}, {.size = 1});
94 EXPECT_EQ(0u, allocs.before);
95 EXPECT_EQ(12u, allocs.tp);
96 EXPECT_EQ(12u, allocs.after);
97 EXPECT_EQ(13u, layout.size());
98 EXPECT_EQ(4u, layout.align_);
99
100 // Alignment gap between `before` and TP.
101 std::tie(layout, allocs) = reserve_tp({.size = 9, .align = 4}, {.size = 128, .align = 64});
102 EXPECT_EQ(52u, allocs.before);
103 EXPECT_EQ(64u, allocs.tp);
104 EXPECT_EQ(64u, allocs.after);
105 EXPECT_EQ(192u, layout.size());
106 EXPECT_EQ(64u, layout.align_);
107
108 // Skew-aligned `before` with low alignment.
109 std::tie(layout, allocs) =
110 reserve_tp({.size = 1, .align = 4, .skew = 1}, {.size = 64, .align = 8});
111 EXPECT_EQ(5u, allocs.before);
112 EXPECT_EQ(8u, allocs.tp);
113
114 // Skew-aligned `before` with high alignment.
115 std::tie(layout, allocs) = reserve_tp({.size = 48, .align = 64, .skew = 17}, {.size = 1});
116 EXPECT_EQ(17u, allocs.before);
117 EXPECT_EQ(128u, allocs.tp);
118
119 // An unrelated byte precedes the pair in the layout. Make sure `before` is
120 // still aligned.
121 layout = {};
122 layout.reserve_type<char>();
123 std::tie(layout, allocs) = reserve_tp({.size = 12, .align = 16}, {.size = 1}, layout);
124 EXPECT_EQ(16u, allocs.before);
125 EXPECT_EQ(32u, allocs.tp);
126
127 // Skew-aligned `after`.
128 std::tie(layout, allocs) =
129 reserve_tp({.size = 32, .align = 8}, {.size = 16, .align = 4, .skew = 3});
130 EXPECT_EQ(0u, allocs.before);
131 EXPECT_EQ(32u, allocs.tp);
132 EXPECT_EQ(35u, allocs.after);
133 EXPECT_EQ(51u, layout.size());
134 }
135
136 // A "NUM_words" literal is the size in bytes of NUM words of memory.
operator ""_words(unsigned long long i)137 static size_t operator""_words(unsigned long long i) {
138 return i * sizeof(void*);
139 }
140
TEST(static_tls_layout,arm)141 TEST(static_tls_layout, arm) {
142 #if !defined(__arm__) && !defined(__aarch64__)
143 GTEST_SKIP() << "test only applies to arm32/arm64 targets";
144 #endif
145
146 auto reserve_exe = [](const AlignedSizeFlat& config) {
147 StaticTlsLayout layout;
148 TlsSegment seg = {.aligned_size = unflatten_size(config)};
149 layout.reserve_exe_segment_and_tcb(&seg, "prog");
150 return layout;
151 };
152
153 auto underalign_error = [](size_t align, size_t offset) {
154 return R"(error: "prog": executable's TLS segment is underaligned: )"s
155 R"(alignment is )"s +
156 std::to_string(align) + R"( \(skew )" + std::to_string(offset) +
157 R"(\), needs to be at least (32 for ARM|64 for ARM64) Bionic)"s;
158 };
159
160 // Amount of memory needed for negative TLS slots, given a segment p_align of
161 // 8 or 16 words.
162 const size_t base8 = __BIONIC_ALIGN(-MIN_TLS_SLOT, 8) * sizeof(void*);
163 const size_t base16 = __BIONIC_ALIGN(-MIN_TLS_SLOT, 16) * sizeof(void*);
164
165 StaticTlsLayout layout;
166
167 // An executable with a single word.
168 layout = reserve_exe({.size = 1_words, .align = 8_words});
169 EXPECT_EQ(base8 + MIN_TLS_SLOT * sizeof(void*), layout.offset_bionic_tcb());
170 EXPECT_EQ(base8, layout.offset_thread_pointer());
171 EXPECT_EQ(base8 + 8_words, layout.offset_exe());
172 EXPECT_EQ(base8 + 9_words, layout.size());
173 EXPECT_EQ(8_words, layout.align_);
174
175 // Simple underalignment case.
176 EXPECT_DEATH(reserve_exe({.size = 1_words, .align = 1_words}), underalign_error(1_words, 0));
177
178 // Skewed by 1 word is OK.
179 layout = reserve_exe({.size = 1_words, .align = 8_words, .skew = 1_words});
180 EXPECT_EQ(base8, layout.offset_thread_pointer());
181 EXPECT_EQ(base8 + 9_words, layout.offset_exe());
182 EXPECT_EQ(base8 + 10_words, layout.size());
183 EXPECT_EQ(8_words, layout.align_);
184
185 // Skewed by 2 words would overlap Bionic slots, regardless of the p_align
186 // value.
187 EXPECT_DEATH(reserve_exe({.size = 1_words, .align = 8_words, .skew = 2_words}),
188 underalign_error(8_words, 2_words));
189 EXPECT_DEATH(reserve_exe({.size = 1_words, .align = 0x1000, .skew = 2_words}),
190 underalign_error(0x1000, 2_words));
191
192 // Skewed by 8 words is OK again.
193 layout = reserve_exe({.size = 1_words, .align = 16_words, .skew = 8_words});
194 EXPECT_EQ(base16, layout.offset_thread_pointer());
195 EXPECT_EQ(base16 + 8_words, layout.offset_exe());
196 EXPECT_EQ(base16 + 9_words, layout.size());
197 EXPECT_EQ(16_words, layout.align_);
198
199 // Skewed by 9 words is also OK. (The amount of skew doesn't need to be a
200 // multiple of anything.)
201 layout = reserve_exe({.size = 1_words, .align = 16_words, .skew = 9_words});
202 EXPECT_EQ(base16, layout.offset_thread_pointer());
203 EXPECT_EQ(base16 + 9_words, layout.offset_exe());
204 EXPECT_EQ(base16 + 10_words, layout.size());
205 EXPECT_EQ(16_words, layout.align_);
206
207 // Skew with large alignment.
208 layout = reserve_exe({.size = 1_words, .align = 256_words, .skew = 8_words});
209 EXPECT_EQ(256_words, layout.offset_thread_pointer());
210 EXPECT_EQ(264_words, layout.offset_exe());
211 EXPECT_EQ(265_words, layout.size());
212 EXPECT_EQ(256_words, layout.align_);
213 }
214