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 static 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 #ifdef PICOLIBC_TLS
234 static void
hexdump(const void * ptr,int length,const char * hdr)235 hexdump(const void *ptr, int length, const char *hdr)
236 {
237 	const unsigned char *cp = ptr;
238 
239 	for (int i = 0; i < length; i += 16) {
240 		printf("%s%08zx  ", hdr, i + (size_t)ptr);
241 		for (int j = 0; j < 16; j++) {
242 			int offset = i + j;
243 			if (offset < length)
244 				printf(" %02x", cp[offset]);
245 			else
246 				printf("   ");
247 		}
248 		printf("\n");
249 	}
250 }
251 #endif
252 
253 int
main(void)254 main(void)
255 {
256 	int result = 0;
257 
258 	data_addr = &data_var;
259 	overaligned_data_addr = &overaligned_data_var;
260 	bss_addr = &bss_var;
261         overaligned_bss_addr = &overaligned_bss_var;
262 
263 #ifdef PICOLIBC_TLS
264         printf("TLS region: %p-%p (%zd bytes)\n", __tdata_start,
265 	       __tdata_start + _tls_size(), _tls_size());
266 	size_t tdata_source_size = __tdata_source_end - __tdata_source;
267 	size_t tdata_size = __tdata_end - __tdata_start;
268 
269 	if (__tdata_start - __data_start != __tdata_source - __data_source) {
270 		printf("ROM/RAM .tdata offset from .data mismatch. "
271 		       "VMA offset=%zd, LMA offset =%zd."
272 		       "Linker behaviour changed?\n",
273 		       __tdata_start - __data_start,
274 		       __tdata_source - __data_source);
275 	}
276 
277 	if (tdata_source_size != tdata_size ||
278 	    memcmp(&__tdata_source, &__tdata_start, tdata_size) != 0) {
279 		printf("TLS data in RAM does not match ROM\n");
280 		hexdump(__tdata_source, tdata_source_size, "ROM:");
281 		hexdump(__tdata_start, tdata_size, "RAM:");
282 		result++;
283 	}
284         result += check_tls("pre-defined", false, __tdata_start);
285 #else
286         result += check_tls("pre-defined", false, NULL);
287 #endif
288 
289 
290 #ifdef _HAVE_PICOLIBC_TLS_API
291 
292         size_t tls_align = _tls_align();
293         size_t tls_size = _tls_size();
294 	void *tls = aligned_alloc(tls_align, tls_size);
295 
296         if (tls) {
297             /*
298              * Fill the region with data to make sure even bss
299              * gets correctly initialized
300              */
301             memset(tls, 0x55, tls_size);
302 
303             _init_tls(tls);
304             _set_tls(tls);
305 
306             if (memcmp(tls, &__tdata_source, tdata_size) != 0) {
307 		printf("New TLS data in RAM does not match ROM\n");
308 		hexdump(&__tdata_source, tdata_source_size, "ROM:");
309 		hexdump(tls, tdata_size, "RAM:");
310 		result++;
311             }
312 
313             result += check_tls("allocated", true, tls);
314         } else {
315             printf("TLS allocation failed\n");
316             result = 1;
317         }
318 #endif
319 
320 	printf("tls test result %d\n", result);
321 	return result;
322 }
323