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 <stdio.h>
37 #include <picotls.h>
38 #include <stdlib.h>
39 #include <stdbool.h>
40 #include <stdint.h>
41 #include <string.h>
42 
43 #define DATA_VAL 0x600df00d
44 #define DATA_VAL2 0x0a0a0a0a
45 
46 #define OVERALIGN_DATA  128
47 #define OVERALIGN_BSS   256
48 
49 #define TLS_ALIGN      (OVERALIGN_DATA > OVERALIGN_BSS ? OVERALIGN_DATA : OVERALIGN_BSS)
50 
51 NEWLIB_THREAD_LOCAL volatile int data_var = DATA_VAL;
52 NEWLIB_THREAD_LOCAL volatile int bss_var;
53 _Alignas(OVERALIGN_DATA) NEWLIB_THREAD_LOCAL volatile int overaligned_data_var = DATA_VAL2;
54 _Alignas(OVERALIGN_BSS) NEWLIB_THREAD_LOCAL volatile int overaligned_bss_var;
55 
56 volatile int *volatile data_addr;
57 volatile int *volatile overaligned_data_addr;
58 volatile int *volatile bss_addr;
59 volatile int *volatile overaligned_bss_addr;
60 
61 #ifdef PICOLIBC_TLS
62 extern char __tdata_start[], __tdata_end[];
63 extern char __tdata_source[], __tdata_source_end[];
64 extern char __data_start[], __data_source[];
65 extern char __non_tls_bss_start[];
66 
67 static bool
inside_tls_region(void * ptr,const void * tls)68 inside_tls_region(void *ptr, const void *tls)
69 {
70 	return (uintptr_t)ptr >= (uintptr_t)tls &&
71 	       (uintptr_t)ptr < (uintptr_t)tls + _tls_size();
72 }
73 
74 #define check_inside_tls_region(ptr, tls_start)                                \
75 	if (!inside_tls_region(__DEVOLATILE(void *, ptr), tls_start)) {        \
76 		printf("%s (%p) is not inside TLS region [%p-%p)\n", #ptr,     \
77 		       ptr, tls_start, (char *)tls_start + _tls_size());       \
78 		result++;                                                      \
79 	}
80 #endif
81 
82 int
check_tls(char * where,bool check_addr,void * tls_region)83 check_tls(char *where, bool check_addr, void *tls_region)
84 {
85 	int result = 0;
86 
87 	printf("tls check %s %p %p %p %p\n", where, &data_var,
88 	       &overaligned_data_var, &bss_var, &overaligned_bss_var);
89 #ifdef PICOLIBC_TLS
90         if (_tls_align() & (OVERALIGN_DATA-1)) {
91                 printf("TLS alignment too small for data (need %ld, got %ld)\n",
92                        (unsigned long) OVERALIGN_DATA,
93                        (unsigned long) _tls_align());
94                 result++;
95         }
96         if (_tls_align() & (OVERALIGN_BSS-1)) {
97                 printf("TLS alignment too small for bss (need %ld, got %ld)\n",
98                        (unsigned long) OVERALIGN_BSS,
99                        (unsigned long) _tls_align());
100                 result++;
101         }
102 #endif
103 	if (!__is_aligned(tls_region, TLS_ALIGN)) {
104 		printf("TLS data region (%p) is not %ld aligned\n",
105                        tls_region, (unsigned long) TLS_ALIGN);
106 		result++;
107 	}
108 	if (!__is_aligned((uintptr_t)&overaligned_data_var, OVERALIGN_DATA)) {
109 		printf("overaligned_data_var (%p) is not %ld aligned\n",
110 		       &overaligned_data_var, (unsigned long) OVERALIGN_DATA);
111 		result++;
112 	}
113         if (!__is_aligned((uintptr_t)&overaligned_bss_var, OVERALIGN_BSS)) {
114                 printf("overaligned_bss_var (%p) is not %ld aligned\n",
115                        &overaligned_bss_var, (unsigned long) OVERALIGN_BSS);
116                 result++;
117         }
118 	if (data_var != DATA_VAL) {
119 		printf("%s: initialized thread var has wrong value (0x%x instead of 0x%x)\n",
120 		       where, data_var, DATA_VAL);
121 		result++;
122 	}
123 	if (overaligned_data_var != DATA_VAL2) {
124 		printf("%s: initialized overaligned thread var has wrong value (0x%x instead of 0x%x)\n",
125 		       where, overaligned_data_var, DATA_VAL2);
126 		result++;
127 	}
128 
129 	if (bss_var != 0) {
130 		printf("%s: uninitialized thread var has wrong value (0x%x instead of 0x%x)\n",
131 		       where, bss_var, 0);
132 		result++;
133 	}
134         if (overaligned_bss_var != 0) {
135 		printf("%s: overaligned uninitialized thread var has wrong value (0x%x instead of 0x%x)\n",
136 		       where, overaligned_bss_var, 0);
137 		result++;
138         }
139 
140 	data_var = ~data_var;
141 
142 	if (data_var != ~DATA_VAL) {
143 		printf("%s: initialized thread var set to wrong value (0x%x instead of 0x%x)\n",
144 		       where, data_var, ~DATA_VAL);
145 		result++;
146 	}
147 
148 	overaligned_data_var = ~overaligned_data_var;
149 
150 	if (overaligned_data_var != ~DATA_VAL2) {
151 		printf("%s: overaligned initialized thread var set to wrong value (0x%x instead of 0x%x)\n",
152 		       where, overaligned_data_var, ~DATA_VAL2);
153 		result++;
154 	}
155 
156 	bss_var = ~bss_var;
157 
158 	if (bss_var != ~0) {
159 		printf("%s: uninitialized thread var has wrong value (0x%x instead of 0x%x)\n",
160 		       where, bss_var, ~0);
161 		result++;
162 	}
163 
164 	overaligned_bss_var = ~overaligned_bss_var;
165 
166 	if (overaligned_bss_var != ~0) {
167 		printf("%s: overaligned uninitialized thread var has wrong value (0x%x instead of 0x%x)\n",
168 		       where, overaligned_bss_var, ~0);
169 		result++;
170 	}
171 
172 	if (check_addr) {
173 		if (data_addr == &data_var) {
174 			printf("_set_tls didn't affect initialized addr %p\n", data_addr);
175 			result++;
176 		}
177 
178                 if (overaligned_data_addr == &overaligned_data_var) {
179 			printf("_set_tls didn't affect initialized addr %p\n", overaligned_data_addr);
180 			result++;
181 		}
182 
183 		if (bss_addr == &bss_var) {
184 			printf("_set_tls didn't affect uninitialized addr %p\n", bss_addr);
185 			result++;
186 		}
187 
188 		if (overaligned_bss_addr == &overaligned_bss_var) {
189 			printf("_set_tls didn't affect uninitialized addr %p\n", overaligned_bss_addr);
190 			result++;
191 		}
192 	}
193 #ifdef PICOLIBC_TLS
194 	check_inside_tls_region(&data_var, tls_region);
195 	check_inside_tls_region(&overaligned_data_var, tls_region);
196 	check_inside_tls_region(&bss_var, tls_region);
197 	check_inside_tls_region(&overaligned_bss_var, tls_region);
198 
199         if (__non_tls_bss_start < __tdata_start + _tls_size() || __tdata_start + _tls_size() + 64 < __non_tls_bss_start) {
200                 printf("non-TLS bss data doesn't start after TLS data (is %p should be %p)\n",
201                        __non_tls_bss_start, __tdata_start + _tls_size());
202                 result++;
203         }
204 #endif
205 	return result;
206 }
207 
208 void
hexdump(const void * ptr,int length,const char * hdr)209 hexdump(const void *ptr, int length, const char *hdr)
210 {
211 	const unsigned char *cp = ptr;
212 
213 	for (int i = 0; i < length; i += 16) {
214 		printf("%s%08zx  ", hdr, i + (size_t)ptr);
215 		for (int j = 0; j < 16; j++) {
216 			int offset = i + j;
217 			if (offset < length)
218 				printf(" %02x", cp[offset]);
219 			else
220 				printf("   ");
221 		}
222 		printf("\n");
223 	}
224 }
225 
226 int
main(void)227 main(void)
228 {
229 	int result = 0;
230 
231 	data_addr = &data_var;
232 	overaligned_data_addr = &overaligned_data_var;
233 	bss_addr = &bss_var;
234         overaligned_bss_addr = &overaligned_bss_var;
235 
236 #ifdef PICOLIBC_TLS
237         printf("TLS region: %p-%p (%zd bytes)\n", __tdata_start,
238 	       __tdata_start + _tls_size(), _tls_size());
239 	size_t tdata_source_size = __tdata_source_end - __tdata_source;
240 	size_t tdata_size = __tdata_end - __tdata_start;
241 
242 	if (__tdata_start - __data_start != __tdata_source - __data_source) {
243 		printf("ROM/RAM .tdata offset from .data mismatch. "
244 		       "VMA offset=%zd, LMA offset =%zd."
245 		       "Linker behaviour changed?\n",
246 		       __tdata_start - __data_start,
247 		       __tdata_source - __data_source);
248 	}
249 
250 	if (tdata_source_size != tdata_size ||
251 	    memcmp(&__tdata_source, &__tdata_start, tdata_size) != 0) {
252 		printf("TLS data in RAM does not match ROM\n");
253 		hexdump(__tdata_source, tdata_source_size, "ROM:");
254 		hexdump(__tdata_start, tdata_size, "RAM:");
255 		result++;
256 	}
257         result += check_tls("pre-defined", false, __tdata_start);
258 #else
259         result += check_tls("pre-defined", false, NULL);
260 #endif
261 
262 
263 #ifdef _HAVE_PICOLIBC_TLS_API
264 
265         size_t tls_align = _tls_align();
266         size_t tls_size = _tls_size();
267 	void *tls = aligned_alloc(tls_align, tls_size);
268 
269         /*
270          * Fill the region with data to make sure even bss
271          * gets correctly initialized
272          */
273         memset(tls, 0x55, tls_size);
274 
275 	_init_tls(tls);
276 	_set_tls(tls);
277 
278 	if (memcmp(tls, &__tdata_source, tdata_size) != 0) {
279 		printf("New TLS data in RAM does not match ROM\n");
280 		hexdump(&__tdata_source, tdata_source_size, "ROM:");
281 		hexdump(tls, tdata_size, "RAM:");
282 		result++;
283 	}
284 
285 	result += check_tls("allocated", true, tls);
286 #endif
287 
288 	printf("tls test result %d\n", result);
289 	return result;
290 }
291