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