1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2019, Joyent, Inc.
14 * Copyright 2025 Oxide Computer Company
15 */
16
17 /*
18 * Check that we properly handle structures and unions.
19 */
20
21 #include "check-common.h"
22
23 static check_number_t check_bitfields[] = {
24 #ifdef TARGET_LP64
25 { "unsigned long:1", CTF_K_INTEGER, 0, 0, 1 },
26 { "unsigned long:2", CTF_K_INTEGER, 0, 0, 2 },
27 { "unsigned long:4", CTF_K_INTEGER, 0, 0, 4 },
28 { "unsigned long:5", CTF_K_INTEGER, 0, 0, 5 },
29 { "unsigned long:8", CTF_K_INTEGER, 0, 0, 8 },
30 { "unsigned long:16", CTF_K_INTEGER, 0, 0, 16 },
31 { "unsigned long:19", CTF_K_INTEGER, 0, 0, 19 },
32 { "unsigned long:32", CTF_K_INTEGER, 0, 0, 32 },
33 #else
34 { "unsigned long long:1", CTF_K_INTEGER, 0, 0, 1 },
35 { "unsigned long long:2", CTF_K_INTEGER, 0, 0, 2 },
36 { "unsigned long long:4", CTF_K_INTEGER, 0, 0, 4 },
37 { "unsigned long long:5", CTF_K_INTEGER, 0, 0, 5 },
38 { "unsigned long long:8", CTF_K_INTEGER, 0, 0, 8 },
39 { "unsigned long long:16", CTF_K_INTEGER, 0, 0, 16 },
40 { "unsigned long long:19", CTF_K_INTEGER, 0, 0, 19 },
41 { "unsigned long long:32", CTF_K_INTEGER, 0, 0, 32 },
42 #endif
43 { "unsigned short:1", CTF_K_INTEGER, 0, 0, 1 },
44 { "unsigned int:7", CTF_K_INTEGER, 0, 0, 7 },
45 /*
46 * Skipped on clang as it doesn't process csts correctly. See
47 * check_members_csts.
48 */
49 { "unsigned int:32", CTF_K_INTEGER, 0, 0, 32, SKIP_CLANG },
50 { "int:3", CTF_K_INTEGER, CTF_INT_SIGNED, 0, 3 },
51 { NULL }
52 };
53
54 static check_symbol_t check_syms[] = {
55 { "foo", "struct foo" },
56 { "head", "nlist_t" },
57 { "forward", "const forward_t" },
58 { "oot", "struct round_up" },
59 { "botw", "struct fixed_up" },
60 { "sophie", "struct mysterious_barrel" },
61 { "ayesha", "struct dusk_barrel" },
62 { "stats", "struct stats" },
63 { "ring", "struct fellowship" },
64 { "rings", "struct rings" },
65 { "nvme", "struct csts" },
66 { "games", "union jrpg" },
67 { "nier", "union nier" },
68 { "kh", "union kh" },
69 { "ct", "struct trigger" },
70 { "regress", "const union regress [9]" },
71 { NULL }
72 };
73
74 static check_member_t check_member_foo[] = {
75 { "a", "int", 0 },
76 { "b", "float", 4 * NBBY },
77 { "c", "const char *", 8 * NBBY },
78 { NULL }
79 };
80
81 static check_member_t check_member_node[] = {
82 { "prev", "struct node *", 0 },
83 #ifdef TARGET_LP64
84 { "next", "struct node *", 8 * NBBY },
85 #else
86 { "next", "struct node *", 4 * NBBY },
87 #endif
88 { NULL }
89 };
90
91 static check_member_t check_member_nlist[] = {
92 { "size", "size_t", 0 },
93 #ifdef TARGET_LP64
94 { "off", "size_t", 8 * NBBY },
95 { "head", "struct node", 16 * NBBY },
96 #else
97 { "off", "size_t", 4 * NBBY },
98 { "head", "struct node", 8 * NBBY },
99 #endif
100 { NULL }
101 };
102
103 static check_member_t check_member_forward[] = {
104 { "past", "void *", 0 },
105 #ifdef TARGET_LP64
106 { "present", "void *", 8 * NBBY },
107 { "future", "void *", 16 * NBBY },
108 #else
109 { "present", "void *", 4 * NBBY },
110 { "future", "void *", 8 * NBBY },
111 #endif
112 { NULL }
113 };
114
115 static check_member_t check_member_round_up[] = {
116 { "triforce", "uint8_t", 0 },
117 { "link", "uint32_t", 4 * NBBY },
118 { "zelda", "uint8_t", 8 * NBBY },
119 { "ganon", "uint8_t", 9 * NBBY },
120 { NULL }
121 };
122
123 static check_member_t check_member_fixed_up[] = {
124 { "triforce", "uint8_t", 0 },
125 { "link", "uint32_t", 1 * NBBY },
126 { "zelda", "uint8_t", 5 * NBBY },
127 { "ganon", "uint8_t", 6 * NBBY },
128 { NULL }
129 };
130
131 #ifdef TARGET_LP64
132 static check_member_t check_member_component[] = {
133 { "m", "enum material", 0 },
134 { "grade", "uint64_t", 8 * NBBY },
135 { "count", "uint64_t", 16 * NBBY },
136 { "locations", "const char *[4]", 24 * NBBY },
137 { NULL }
138 };
139
140 static check_member_t check_member_mysterious[] = {
141 { "name", "const char *", 0 },
142 { "capacity", "size_t", 8 * NBBY },
143 { "optional", "struct component [0]", 16 * NBBY },
144 { NULL }
145 };
146
147 static check_member_t check_member_dusk[] = {
148 { "name", "const char *", 0 },
149 { "opacity", "size_t", 8 * NBBY },
150 { "optional", "struct component [0]", 16 * NBBY },
151 { NULL }
152 };
153
154
155 static check_member_t check_member_stats[] = {
156 { "hp", "unsigned long:16", 0 },
157 { "mp", "unsigned long:16", 16 },
158 { "str", "unsigned long:8", 32 },
159 { "dex", "unsigned long:4", 40 },
160 { "con", "unsigned long:1", 44 },
161 { "inte", "unsigned long:2", 45 },
162 { "wis", "unsigned long:1", 47 },
163 { "cha", "unsigned long:4", 48 },
164 { "sanity", "unsigned long:1", 52 },
165 { "attack", "unsigned long:2", 53 },
166 { "mattack", "unsigned long:1", 55 },
167 { "defense", "unsigned long:8", 56 },
168 { "mdefense", "unsigned long:32", 64 },
169 { "evasion", "unsigned long:8", 96 },
170 { "crit", "unsigned long:5", 104 },
171 { "luck", "unsigned long:19", 109 },
172 { NULL }
173 };
174 #else
175 static check_member_t check_member_component[] = {
176 { "m", "enum material", 0 },
177 { "grade", "uint64_t", 4 * NBBY },
178 { "count", "uint64_t", 12 * NBBY },
179 { "locations", "const char *[4]", 20 * NBBY },
180 { NULL }
181 };
182
183 static check_member_t check_member_mysterious[] = {
184 { "name", "const char *", 0 },
185 { "capacity", "size_t", 4 * NBBY },
186 { "optional", "struct component [0]", 8 * NBBY },
187 { NULL }
188 };
189
190 static check_member_t check_member_dusk[] = {
191 { "name", "const char *", 0 },
192 { "opacity", "size_t", 4 * NBBY },
193 { "optional", "struct component [0]", 8 * NBBY },
194 { NULL }
195 };
196
197
198 static check_member_t check_member_stats[] = {
199 { "hp", "unsigned long long:16", 0 },
200 { "mp", "unsigned long long:16", 16 },
201 { "str", "unsigned long long:8", 32 },
202 { "dex", "unsigned long long:4", 40 },
203 { "con", "unsigned long long:1", 44 },
204 { "inte", "unsigned long long:2", 45 },
205 { "wis", "unsigned long long:1", 47 },
206 { "cha", "unsigned long long:4", 48 },
207 { "sanity", "unsigned long long:1", 52 },
208 { "attack", "unsigned long long:2", 53 },
209 { "mattack", "unsigned long long:1", 55 },
210 { "defense", "unsigned long long:8", 56 },
211 { "mdefense", "unsigned long long:32", 64 },
212 { "evasion", "unsigned long long:8", 96 },
213 { "crit", "unsigned long long:5", 104 },
214 { "luck", "unsigned long long:19", 109 },
215 { NULL }
216 };
217 #endif
218
219 static check_member_t check_member_fellowship[] = {
220 { "frodo", "unsigned short:1", 0 },
221 { "sam", "unsigned short:1", 1 },
222 { "merry", "unsigned short:1", 2 },
223 { "pippin", "unsigned short:1", 3 },
224 { "aragorn", "unsigned short:1", 4 },
225 { "boromir", "unsigned short:1", 5 },
226 { "legolas", "unsigned short:1", 6 },
227 { "gimli", "unsigned short:1", 7 },
228 { "gandalf", "unsigned short:1", 8 },
229 { NULL }
230 };
231
232 static check_member_t check_member_rings[] = {
233 { "elves", "unsigned int:3", 0 },
234 { "dwarves", "unsigned int:7", 3 },
235 { "men", "unsigned int:9", 10 },
236 { "one", "uint8_t", 3 * NBBY },
237 { "silmarils", "uint8_t [3]", 4 * NBBY },
238 { NULL }
239 };
240
241 /*
242 * Unfortunately this test case fails with clang in at least versions 8-10. See
243 * https://bugs.llvm.org/show_bug.cgi?id=44601 for more information on the bug.
244 */
245 static check_member_t check_member_csts[] = {
246 { "rdy", "unsigned int:7", 0 },
247 { "csts", "unsigned int:32", 7 },
248 { NULL }
249 };
250
251 static check_member_t check_member_jrpg[] = {
252 { "ff", "int", 0 },
253 { "atelier", "double [4]", 0 },
254 { "tales", "const char *", 0 },
255 { "chrono", "int (*)()", 0 },
256 { "xeno", "struct rings", 0 },
257 { NULL }
258 };
259
260 static check_member_t check_member_android[] = {
261 { "_2b", "unsigned int:16", 0 },
262 { "_9s", "unsigned int:16", 16 },
263 { NULL }
264 };
265
266 static check_member_t check_member_nier[] = {
267 { "automata", "uint32_t", 0 },
268 { "android", "struct android", 0 },
269 { NULL }
270 };
271
272 static check_member_t check_member_kh[] = {
273 { "sora", "int:3", 0 },
274 { "riku", "char:7", 0 },
275 { "kairi", "double", 0 },
276 { "namine", "complex double", 0 },
277 { NULL }
278 };
279
280 static check_member_t check_member_trigger[] = {
281 { "chrono", "uint8_t", 0 },
282 { "cross", "uint8_t", 8 },
283 /*
284 * This test has an anonymous union. Unfortunately, there's not a great
285 * way to distinguish between various anonymous unions in this form.
286 */
287 #ifdef TARGET_LP64
288 { "", "union ", 64 },
289 #else
290 { "", "union ", 32 },
291 #endif
292 { NULL }
293 };
294
295 static check_member_t check_member_regress[] = {
296 { "i", "unsigned int [3]", 0 },
297 { "e", "long double", 0 },
298 { NULL }
299 };
300
301 static check_member_test_t members[] = {
302 #ifdef TARGET_LP64
303 { "struct foo", CTF_K_STRUCT, 16, check_member_foo },
304 { "struct node", CTF_K_STRUCT, 16, check_member_node },
305 { "struct nlist", CTF_K_STRUCT, 32, check_member_nlist },
306 { "struct forward", CTF_K_STRUCT, 24, check_member_forward },
307 #else
308 { "struct foo", CTF_K_STRUCT, 12, check_member_foo },
309 { "struct node", CTF_K_STRUCT, 8, check_member_node },
310 { "struct nlist", CTF_K_STRUCT, 16, check_member_nlist },
311 { "struct forward", CTF_K_STRUCT, 12, check_member_forward },
312 #endif
313 { "struct round_up", CTF_K_STRUCT, 12, check_member_round_up },
314 { "struct fixed_up", CTF_K_STRUCT, 7, check_member_fixed_up },
315 #ifdef TARGET_LP64
316 { "struct component", CTF_K_STRUCT, 56, check_member_component },
317 { "struct mysterious_barrel", CTF_K_STRUCT, 16,
318 check_member_mysterious },
319 { "struct dusk_barrel", CTF_K_STRUCT, 16, check_member_dusk },
320 #else
321 { "struct component", CTF_K_STRUCT, 36, check_member_component },
322 { "struct mysterious_barrel", CTF_K_STRUCT, 8,
323 check_member_mysterious },
324 { "struct dusk_barrel", CTF_K_STRUCT, 8, check_member_dusk },
325 #endif
326 { "struct stats", CTF_K_STRUCT, 16, check_member_stats },
327 { "struct fellowship", CTF_K_STRUCT, 2, check_member_fellowship },
328 { "struct rings", CTF_K_STRUCT, 8, check_member_rings },
329 { "struct csts", CTF_K_STRUCT, 5, check_member_csts, SKIP_CLANG },
330 { "union jrpg", CTF_K_UNION, 32, check_member_jrpg },
331 { "struct android", CTF_K_STRUCT, 4, check_member_android },
332 { "union nier", CTF_K_UNION, 4, check_member_nier },
333 { "union kh", CTF_K_UNION, 16, check_member_kh },
334 #ifdef TARGET_LP64
335 { "struct trigger", CTF_K_STRUCT, 32, check_member_trigger },
336 { "union regress", CTF_K_UNION, 16, check_member_regress },
337 #else
338 { "struct trigger", CTF_K_STRUCT, 28, check_member_trigger },
339 { "union regress", CTF_K_UNION, 12, check_member_regress },
340 #endif
341 { NULL }
342 };
343
344 #ifdef TARGET_LP64
345 static check_member_t check_member_anon_basic[] = {
346 { "a", "int", 0 },
347 { "b", "int", 8 * NBBY },
348 { "c", "double", 8 * NBBY },
349 { "d", "const char *", 8 * NBBY },
350 { "e", "int", 16 * NBBY },
351 { "f", "const char *", 24 * NBBY },
352 { "g", "unsigned int [10]", 32 * NBBY },
353 { NULL }
354 };
355 #else /* !TARGET_LP64 */
356 static check_member_t check_member_anon_basic[] = {
357 { "a", "int", 0 },
358 { "b", "int", 4 * NBBY },
359 { "c", "double", 4 * NBBY },
360 { "d", "const char *", 4 * NBBY },
361 { "e", "int", 12 * NBBY },
362 { "f", "const char *", 16 * NBBY },
363 { "g", "unsigned int [10]", 20 * NBBY },
364 { NULL }
365 };
366 #endif /* TARGET_LP64 */
367
368 static check_member_t check_member_nested[] = {
369 { "a", "int", 0 },
370 { "b", "int", 4 * NBBY },
371 { "c", "int", 4 * NBBY },
372 { "d", "int", 8 * NBBY },
373 { "e", "int", 12 * NBBY },
374 { "g", "int", 16 * NBBY },
375 { "h", "int", 16 * NBBY },
376 { "i", "int", 20 * NBBY },
377 { "j", "int", 24 * NBBY },
378 { "k", "int", 28 * NBBY },
379 { "l", "int", 28 * NBBY },
380 { "m", "int", 32 * NBBY },
381 { "n", "int", 28 * NBBY },
382 { "o", "int", 28 * NBBY },
383 { "p", "int", 32 * NBBY },
384 { NULL }
385 };
386
387 /*
388 * This contains members tests that involve anonyous unions and structures and
389 * therefore only are for the ctftest_check_member_info() version.
390 */
391 static check_member_test_t anon_members[] = {
392 #ifdef TARGET_LP64
393 { "struct anon_basic", CTF_K_STRUCT, 72, check_member_anon_basic },
394 #else
395 { "struct anon_basic", CTF_K_STRUCT, 60, check_member_anon_basic },
396 #endif
397 { "struct nested", CTF_K_STRUCT, 36, check_member_nested},
398 { NULL }
399 };
400
401 static check_descent_t check_descent_head[] = {
402 { "nlist_t", CTF_K_TYPEDEF },
403 { "struct nlist", CTF_K_STRUCT },
404 { NULL }
405 };
406
407 static check_descent_t check_descent_forward[] = {
408 { "const forward_t", CTF_K_CONST },
409 { "forward_t", CTF_K_TYPEDEF },
410 { "struct forward", CTF_K_STRUCT },
411 { NULL }
412 };
413
414 static check_descent_test_t descents[] = {
415 { "head", check_descent_head },
416 { "forward", check_descent_forward },
417 { NULL }
418 };
419
420 static check_descent_t check_descent_regress_gcc4[] = {
421 { "const union regress [9]", CTF_K_CONST },
422 { "union regress [9]", CTF_K_ARRAY, "union regress", 9 },
423 { "union regress", CTF_K_UNION },
424 { NULL }
425 };
426
427 static check_descent_t check_descent_regress_gcc7[] = {
428 { "const union regress [9]", CTF_K_ARRAY, "const union regress", 9 },
429 { "const union regress", CTF_K_CONST },
430 { "union regress", CTF_K_UNION },
431 { NULL }
432 };
433
434 /*
435 * See needed_array_qualifier(): applying this fix means the qualifier order is
436 * different between GCC versions. Accept either form.
437 */
438 static check_descent_test_t alt_descents[] = {
439 { "regress", check_descent_regress_gcc4 },
440 { "regress", check_descent_regress_gcc7 },
441 { NULL }
442 };
443
444 int
main(int argc,char * argv[])445 main(int argc, char *argv[])
446 {
447 int ret = 0;
448
449 if (argc < 2) {
450 errx(EXIT_FAILURE, "missing test files");
451 }
452
453 for (int i = 1; i < argc; i++) {
454 ctf_file_t *fp;
455 int alt_ok = 0;
456
457 if ((fp = ctf_open(argv[i], &ret)) == NULL) {
458 warnx("failed to open %s: %s", argv[i],
459 ctf_errmsg(ret));
460 ret = EXIT_FAILURE;
461 continue;
462 }
463
464 if (!ctftest_check_numbers(fp, check_bitfields))
465 ret = EXIT_FAILURE;
466 if (!ctftest_check_symbols(fp, check_syms))
467 ret = EXIT_FAILURE;
468 for (size_t j = 0; descents[j].cdt_sym != NULL; j++) {
469 if (!ctftest_check_descent(descents[j].cdt_sym, fp,
470 descents[j].cdt_tests, B_FALSE)) {
471 ret = EXIT_FAILURE;
472 }
473 }
474
475 for (size_t j = 0; alt_descents[j].cdt_sym != NULL; j++) {
476 if (ctftest_check_descent(alt_descents[j].cdt_sym, fp,
477 alt_descents[j].cdt_tests, B_TRUE)) {
478 alt_ok = 1;
479 break;
480 }
481 }
482
483 if (!alt_ok) {
484 warnx("all descents failed for %s",
485 alt_descents[0].cdt_sym);
486 ret = EXIT_FAILURE;
487 }
488
489 for (size_t j = 0; members[j].cmt_type != NULL; j++) {
490 if (ctftest_skip(members[j].cmt_skips)) {
491 warnx("skipping members test %s due to "
492 "known compiler issue",
493 members[j].cmt_type);
494 continue;
495 }
496
497 if (!ctftest_check_members(members[j].cmt_type, fp,
498 members[j].cmt_kind, members[j].cmt_size,
499 members[j].cmt_members)) {
500 ret = EXIT_FAILURE;
501 }
502
503 if (!ctftest_check_member_info(members[j].cmt_type, fp,
504 members[j].cmt_kind, members[j].cmt_size,
505 members[j].cmt_members)) {
506 ret = EXIT_FAILURE;
507 }
508 }
509
510 for (size_t j = 0; anon_members[j].cmt_type != NULL; j++) {
511 if (!ctftest_check_member_info(anon_members[j].cmt_type,
512 fp, anon_members[j].cmt_kind,
513 anon_members[j].cmt_size,
514 anon_members[j].cmt_members)) {
515 ret = EXIT_FAILURE;
516 }
517 }
518
519 ctf_close(fp);
520 }
521
522 return (ret);
523 }
524