1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright © 2020 Keith Packard
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 *
18 * 3. Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <inttypes.h>
37 #include <stdio.h>
38 #include <picotls.h>
39 #include <stdlib.h>
40 #include <stdbool.h>
41 #include <stdint.h>
42 #include <string.h>
43
44 #define DATA_VAL 0x600df00d
45 #define DATA_VAL2 0x0a0a0a0a
46
47 #define OVERALIGN_DATA 128
48 #define OVERALIGN_BSS 256
49 #define OVERALIGN_NON_TLS_BSS 512
50
51 #define TLS_ALIGN (OVERALIGN_DATA > OVERALIGN_BSS ? OVERALIGN_DATA : OVERALIGN_BSS)
52
53 NEWLIB_THREAD_LOCAL volatile int32_t data_var = DATA_VAL;
54 NEWLIB_THREAD_LOCAL volatile int32_t bss_var;
55 _Alignas(OVERALIGN_DATA) NEWLIB_THREAD_LOCAL volatile int32_t overaligned_data_var = DATA_VAL2;
56 _Alignas(OVERALIGN_BSS) NEWLIB_THREAD_LOCAL volatile int32_t overaligned_bss_var;
57 _Alignas(OVERALIGN_NON_TLS_BSS) volatile int32_t overaligned_non_tls_bss_var;
58
59 volatile int32_t *volatile data_addr;
60 volatile int32_t *volatile overaligned_data_addr;
61 volatile int32_t *volatile bss_addr;
62 volatile int32_t *volatile overaligned_bss_addr;
63
64 #ifdef PICOLIBC_TLS
65 extern char __tdata_start[], __tdata_end[];
66 extern char __tdata_source[], __tdata_source_end[];
67 extern char __data_start[], __data_source[];
68 extern char __non_tls_bss_start[];
69
70 static bool
inside_tls_region(void * ptr,const void * tls)71 inside_tls_region(void *ptr, const void *tls)
72 {
73 return (uintptr_t)ptr >= (uintptr_t)tls &&
74 (uintptr_t)ptr < (uintptr_t)tls + _tls_size();
75 }
76
77 #define check_inside_tls_region(ptr, tls_start) \
78 if (!inside_tls_region(__DEVOLATILE(void *, ptr), tls_start)) { \
79 printf("%s (%p) is not inside TLS region [%p-%p)\n", #ptr, \
80 ptr, tls_start, (char *)tls_start + _tls_size()); \
81 result++; \
82 }
83 #endif
84
85 int
check_tls(char * where,bool check_addr,void * tls_region)86 check_tls(char *where, bool check_addr, void *tls_region)
87 {
88 int result = 0;
89
90 printf("tls check %s %p %p %p %p\n", where, &data_var,
91 &overaligned_data_var, &bss_var, &overaligned_bss_var);
92 #ifdef PICOLIBC_TLS
93 if (_tls_align() & (OVERALIGN_DATA-1)) {
94 printf("TLS alignment too small for data (need %ld, got %ld)\n",
95 (unsigned long) OVERALIGN_DATA,
96 (unsigned long) _tls_align());
97 result++;
98 }
99 if (_tls_align() & (OVERALIGN_BSS-1)) {
100 printf("TLS alignment too small for bss (need %ld, got %ld)\n",
101 (unsigned long) OVERALIGN_BSS,
102 (unsigned long) _tls_align());
103 result++;
104 }
105 #endif
106 if (!__is_aligned(tls_region, TLS_ALIGN)) {
107 printf("TLS data region (%p) is not %ld aligned\n",
108 tls_region, (unsigned long) TLS_ALIGN);
109 result++;
110 }
111 if (!__is_aligned((uintptr_t)&overaligned_data_var, OVERALIGN_DATA)) {
112 printf("overaligned_data_var (%p) is not %ld aligned\n",
113 &overaligned_data_var, (unsigned long) OVERALIGN_DATA);
114 result++;
115 }
116 if (!__is_aligned((uintptr_t)&overaligned_bss_var, OVERALIGN_BSS)) {
117 printf("overaligned_bss_var (%p) is not %ld aligned\n",
118 &overaligned_bss_var, (unsigned long) OVERALIGN_BSS);
119 result++;
120 }
121 if (!__is_aligned((uintptr_t)&overaligned_non_tls_bss_var, OVERALIGN_NON_TLS_BSS)) {
122 printf("overaligned_non_tls_bss_var (%p) is not %ld aligned\n",
123 &overaligned_non_tls_bss_var, (unsigned long) OVERALIGN_NON_TLS_BSS);
124 result++;
125 }
126 if (data_var != DATA_VAL) {
127 printf("%s: initialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n",
128 where, data_var, (int32_t) DATA_VAL);
129 result++;
130 }
131 if (overaligned_data_var != DATA_VAL2) {
132 printf("%s: initialized overaligned thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n",
133 where, overaligned_data_var, (int32_t) DATA_VAL2);
134 result++;
135 }
136
137 if (bss_var != 0) {
138 printf("%s: uninitialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n",
139 where, bss_var, (int32_t) 0);
140 result++;
141 }
142 if (overaligned_bss_var != 0) {
143 printf("%s: overaligned uninitialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n",
144 where, overaligned_bss_var, (int32_t) 0);
145 result++;
146 }
147 if (overaligned_non_tls_bss_var != 0) {
148 printf("%s: overaligned uninitialized var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n",
149 where, overaligned_non_tls_bss_var, (int32_t) 0);
150 result++;
151 }
152
153 data_var = ~data_var;
154
155 if (data_var != ~DATA_VAL) {
156 printf("%s: initialized thread var set to wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n",
157 where, data_var, ~((int32_t) DATA_VAL));
158 result++;
159 }
160
161 overaligned_data_var = ~overaligned_data_var;
162
163 if (overaligned_data_var != ~DATA_VAL2) {
164 printf("%s: overaligned initialized thread var set to wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n",
165 where, overaligned_data_var, ~((int32_t) DATA_VAL2));
166 result++;
167 }
168
169 bss_var = ~bss_var;
170
171 if (bss_var != ~0) {
172 printf("%s: uninitialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n",
173 where, bss_var, ~((int32_t) 0));
174 result++;
175 }
176
177 overaligned_bss_var = ~overaligned_bss_var;
178
179 if (overaligned_bss_var != ~0) {
180 printf("%s: overaligned uninitialized thread var has wrong value (0x%" PRIx32 " instead of 0x%" PRIx32 ")\n",
181 where, overaligned_bss_var, ~((int32_t) 0));
182 result++;
183 }
184
185 if (check_addr) {
186 if (data_addr == &data_var) {
187 printf("_set_tls didn't affect initialized addr %p\n", data_addr);
188 result++;
189 }
190
191 if (overaligned_data_addr == &overaligned_data_var) {
192 printf("_set_tls didn't affect initialized addr %p\n", overaligned_data_addr);
193 result++;
194 }
195
196 if (bss_addr == &bss_var) {
197 printf("_set_tls didn't affect uninitialized addr %p\n", bss_addr);
198 result++;
199 }
200
201 if (overaligned_bss_addr == &overaligned_bss_var) {
202 printf("_set_tls didn't affect uninitialized addr %p\n", overaligned_bss_addr);
203 result++;
204 }
205 }
206 #ifdef PICOLIBC_TLS
207 check_inside_tls_region(&data_var, tls_region);
208 check_inside_tls_region(&overaligned_data_var, tls_region);
209 check_inside_tls_region(&bss_var, tls_region);
210 check_inside_tls_region(&overaligned_bss_var, tls_region);
211
212 char *tdata_end = __tdata_start + _tls_size();
213 /*
214 * We allow for up to OVERALIGN_NON_TLS_BSS -1 bytes of padding after
215 * the end of .tbss and the start of aligned .bss since in theory the
216 * linker could fill this space with smaller .bss variables before the
217 * overaligned value that we define in this file.
218 */
219 char *non_tls_bss_start_latest = __align_up(
220 tdata_end + OVERALIGN_NON_TLS_BSS, OVERALIGN_NON_TLS_BSS);
221 if (__non_tls_bss_start < tdata_end ||
222 __non_tls_bss_start > non_tls_bss_start_latest) {
223 printf("non-TLS bss data doesn't start shortly after TLS data "
224 "(is %p should be between %p and %p)\n",
225 __non_tls_bss_start, tdata_end,
226 non_tls_bss_start_latest);
227 result++;
228 }
229 #endif
230 return result;
231 }
232
233 void
hexdump(const void * ptr,int length,const char * hdr)234 hexdump(const void *ptr, int length, const char *hdr)
235 {
236 const unsigned char *cp = ptr;
237
238 for (int i = 0; i < length; i += 16) {
239 printf("%s%08zx ", hdr, i + (size_t)ptr);
240 for (int j = 0; j < 16; j++) {
241 int offset = i + j;
242 if (offset < length)
243 printf(" %02x", cp[offset]);
244 else
245 printf(" ");
246 }
247 printf("\n");
248 }
249 }
250
251 int
main(void)252 main(void)
253 {
254 int result = 0;
255
256 data_addr = &data_var;
257 overaligned_data_addr = &overaligned_data_var;
258 bss_addr = &bss_var;
259 overaligned_bss_addr = &overaligned_bss_var;
260
261 #ifdef PICOLIBC_TLS
262 printf("TLS region: %p-%p (%zd bytes)\n", __tdata_start,
263 __tdata_start + _tls_size(), _tls_size());
264 size_t tdata_source_size = __tdata_source_end - __tdata_source;
265 size_t tdata_size = __tdata_end - __tdata_start;
266
267 if (__tdata_start - __data_start != __tdata_source - __data_source) {
268 printf("ROM/RAM .tdata offset from .data mismatch. "
269 "VMA offset=%zd, LMA offset =%zd."
270 "Linker behaviour changed?\n",
271 __tdata_start - __data_start,
272 __tdata_source - __data_source);
273 }
274
275 if (tdata_source_size != tdata_size ||
276 memcmp(&__tdata_source, &__tdata_start, tdata_size) != 0) {
277 printf("TLS data in RAM does not match ROM\n");
278 hexdump(__tdata_source, tdata_source_size, "ROM:");
279 hexdump(__tdata_start, tdata_size, "RAM:");
280 result++;
281 }
282 result += check_tls("pre-defined", false, __tdata_start);
283 #else
284 result += check_tls("pre-defined", false, NULL);
285 #endif
286
287
288 #ifdef _HAVE_PICOLIBC_TLS_API
289
290 size_t tls_align = _tls_align();
291 size_t tls_size = _tls_size();
292 void *tls = aligned_alloc(tls_align, tls_size);
293
294 if (tls) {
295 /*
296 * Fill the region with data to make sure even bss
297 * gets correctly initialized
298 */
299 memset(tls, 0x55, tls_size);
300
301 _init_tls(tls);
302 _set_tls(tls);
303
304 if (memcmp(tls, &__tdata_source, tdata_size) != 0) {
305 printf("New TLS data in RAM does not match ROM\n");
306 hexdump(&__tdata_source, tdata_source_size, "ROM:");
307 hexdump(tls, tdata_size, "RAM:");
308 result++;
309 }
310
311 result += check_tls("allocated", true, tls);
312 } else {
313 printf("TLS allocation failed\n");
314 result = 1;
315 }
316 #endif
317
318 printf("tls test result %d\n", result);
319 return result;
320 }
321