]>
Commit | Line | Data |
---|---|---|
579396ee | 1 | /* Test that pthread_exit does not clobber callee-saved registers. |
6d7e8eda | 2 | Copyright (C) 2018-2023 Free Software Foundation, Inc. |
579396ee FW |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library; if not, see | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
579396ee FW |
18 | |
19 | #include <stdio.h> | |
20 | #include <support/check.h> | |
21 | #include <support/xthread.h> | |
22 | ||
23 | /* This test attempts to check that callee-saved registers are | |
24 | restored to their original values when destructors are run after | |
25 | pthread_exit is called. GCC PR 83641 causes this test to fail. | |
26 | ||
27 | The constants have been chosen randomly and are magic values which | |
28 | are used to detect whether registers have been clobbered. The idea | |
29 | is that these values are hidden behind a compiler barrier and only | |
30 | present in .rodata initially, so that it is less likely that they | |
31 | are in a register by accident. | |
32 | ||
33 | The checker class can be stored in registers, and the magic values | |
34 | are directly loaded into these registers. The checker destructor | |
35 | is eventually invoked by pthread_exit and calls one of the | |
36 | check_magic functions to verify that the class contents (that is, | |
37 | register value) is correct. | |
38 | ||
39 | These tests are performed both for unsigned int and double values, | |
40 | to cover different calling conventions. */ | |
41 | ||
42 | template <class T> | |
43 | struct values | |
44 | { | |
45 | T v0; | |
46 | T v1; | |
47 | T v2; | |
48 | T v3; | |
49 | T v4; | |
50 | }; | |
51 | ||
52 | static const values<unsigned int> magic_values = | |
53 | { | |
54 | 0x57f7fc72, | |
55 | 0xe582daba, | |
56 | 0x5f6ac994, | |
57 | 0x35efddb7, | |
58 | 0x1fbf5a74, | |
59 | }; | |
60 | ||
61 | static const values<double> magic_values_double = | |
62 | { | |
63 | 0.6764041905675465, | |
64 | 0.9533336788140494, | |
65 | 0.6091161359041452, | |
66 | 0.7668653957125336, | |
67 | 0.010374520235509666, | |
68 | }; | |
69 | ||
70 | /* Special index value which tells check_magic that no check should be | |
71 | performed. */ | |
72 | enum { no_check = -1 }; | |
73 | ||
74 | /* Check that VALUE is the magic value for INDEX, behind a compiler | |
75 | barrier. */ | |
76 | __attribute__ ((noinline, noclone, weak)) | |
77 | void | |
78 | check_magic (int index, unsigned int value) | |
79 | { | |
80 | switch (index) | |
81 | { | |
82 | case 0: | |
83 | TEST_COMPARE (value, magic_values.v0); | |
84 | break; | |
85 | case 1: | |
86 | TEST_COMPARE (value, magic_values.v1); | |
87 | break; | |
88 | case 2: | |
89 | TEST_COMPARE (value, magic_values.v2); | |
90 | break; | |
91 | case 3: | |
92 | TEST_COMPARE (value, magic_values.v3); | |
93 | break; | |
94 | case 4: | |
95 | TEST_COMPARE (value, magic_values.v4); | |
96 | break; | |
97 | case no_check: | |
98 | break; | |
99 | default: | |
100 | FAIL_EXIT1 ("invalid magic value index %d", index); | |
101 | } | |
102 | } | |
103 | ||
104 | /* Check that VALUE is the magic value for INDEX, behind a compiler | |
105 | barrier. Double variant. */ | |
106 | __attribute__ ((noinline, noclone, weak)) | |
107 | void | |
108 | check_magic (int index, double value) | |
109 | { | |
110 | switch (index) | |
111 | { | |
112 | case 0: | |
113 | TEST_VERIFY (value == magic_values_double.v0); | |
114 | break; | |
115 | case 1: | |
116 | TEST_VERIFY (value == magic_values_double.v1); | |
117 | break; | |
118 | case 2: | |
119 | TEST_VERIFY (value == magic_values_double.v2); | |
120 | break; | |
121 | case 3: | |
122 | TEST_VERIFY (value == magic_values_double.v3); | |
123 | break; | |
124 | case 4: | |
125 | TEST_VERIFY (value == magic_values_double.v4); | |
126 | break; | |
127 | case no_check: | |
128 | break; | |
129 | default: | |
130 | FAIL_EXIT1 ("invalid magic value index %d", index); | |
131 | } | |
132 | } | |
133 | ||
134 | /* Store a magic value and check, via the destructor, that it has the | |
135 | expected value. */ | |
136 | template <class T, int I> | |
137 | struct checker | |
138 | { | |
139 | T value; | |
140 | ||
141 | checker (T v) | |
142 | : value (v) | |
143 | { | |
144 | } | |
145 | ||
146 | ~checker () | |
147 | { | |
148 | check_magic (I, value); | |
149 | } | |
150 | }; | |
151 | ||
152 | /* The functions call_pthread_exit_0, call_pthread_exit_1, | |
153 | call_pthread_exit are used to call pthread_exit indirectly, with | |
154 | the intent of clobbering the register values. */ | |
155 | ||
156 | __attribute__ ((noinline, noclone, weak)) | |
157 | void | |
158 | call_pthread_exit_0 (const values<unsigned int> *pvalues) | |
159 | { | |
160 | checker<unsigned int, no_check> c0 (pvalues->v0); | |
161 | checker<unsigned int, no_check> c1 (pvalues->v1); | |
162 | checker<unsigned int, no_check> c2 (pvalues->v2); | |
163 | checker<unsigned int, no_check> c3 (pvalues->v3); | |
164 | checker<unsigned int, no_check> c4 (pvalues->v4); | |
165 | ||
166 | pthread_exit (NULL); | |
167 | } | |
168 | ||
169 | __attribute__ ((noinline, noclone, weak)) | |
170 | void | |
171 | call_pthread_exit_1 (const values<double> *pvalues) | |
172 | { | |
173 | checker<double, no_check> c0 (pvalues->v0); | |
174 | checker<double, no_check> c1 (pvalues->v1); | |
175 | checker<double, no_check> c2 (pvalues->v2); | |
176 | checker<double, no_check> c3 (pvalues->v3); | |
177 | checker<double, no_check> c4 (pvalues->v4); | |
178 | ||
179 | values<unsigned int> other_values = { 0, }; | |
180 | call_pthread_exit_0 (&other_values); | |
181 | } | |
182 | ||
183 | __attribute__ ((noinline, noclone, weak)) | |
184 | void | |
185 | call_pthread_exit () | |
186 | { | |
187 | values<double> other_values = { 0, }; | |
188 | call_pthread_exit_1 (&other_values); | |
189 | } | |
190 | ||
191 | /* Create on-stack objects and check that their values are restored by | |
192 | pthread_exit. If Nested is true, call pthread_exit indirectly via | |
193 | call_pthread_exit. */ | |
194 | template <class T, bool Nested> | |
195 | __attribute__ ((noinline, noclone, weak)) | |
196 | void * | |
197 | threadfunc (void *closure) | |
198 | { | |
199 | const values<T> *pvalues = static_cast<const values<T> *> (closure); | |
200 | ||
201 | checker<T, 0> c0 (pvalues->v0); | |
202 | checker<T, 1> c1 (pvalues->v1); | |
203 | checker<T, 2> c2 (pvalues->v2); | |
204 | checker<T, 3> c3 (pvalues->v3); | |
205 | checker<T, 4> c4 (pvalues->v4); | |
206 | ||
207 | if (Nested) | |
208 | call_pthread_exit (); | |
209 | else | |
210 | pthread_exit (NULL); | |
211 | ||
212 | /* This should not be reached. */ | |
213 | return const_cast<char *> (""); | |
214 | } | |
215 | ||
216 | static int | |
217 | do_test () | |
218 | { | |
219 | puts ("info: unsigned int, direct pthread_exit call"); | |
220 | pthread_t thr | |
221 | = xpthread_create (NULL, &threadfunc<unsigned int, false>, | |
222 | const_cast<values<unsigned int> *> (&magic_values)); | |
223 | TEST_VERIFY (xpthread_join (thr) == NULL); | |
224 | ||
225 | puts ("info: double, direct pthread_exit call"); | |
226 | thr = xpthread_create (NULL, &threadfunc<double, false>, | |
227 | const_cast<values<double> *> (&magic_values_double)); | |
228 | TEST_VERIFY (xpthread_join (thr) == NULL); | |
229 | ||
230 | puts ("info: unsigned int, indirect pthread_exit call"); | |
231 | thr = xpthread_create (NULL, &threadfunc<unsigned int, true>, | |
232 | const_cast<values<unsigned int> *> (&magic_values)); | |
233 | TEST_VERIFY (xpthread_join (thr) == NULL); | |
234 | ||
235 | puts ("info: double, indirect pthread_exit call"); | |
236 | thr = xpthread_create (NULL, &threadfunc<double, true>, | |
237 | const_cast<values<double> *> (&magic_values_double)); | |
238 | TEST_VERIFY (xpthread_join (thr) == NULL); | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
243 | #include <support/test-driver.c> |