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 2015 Garrett D'Amore <garrett@damore.org>
14 * Copyright 2018 Joyent, Inc.
15 * Copyright 2024 Oxide Computer Company
16 */
17
18 /*
19 * This program tests symbol visibility in different compilation environments.
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <err.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <libcustr.h>
31 #include <sys/wait.h>
32 #include <stdbool.h>
33 #include "test_common.h"
34
35 char *dname;
36 char *cfile;
37 char *ofile;
38 char *lfile;
39 char *efile;
40
41 const char *sym = NULL;
42
43 static int good_count = 0;
44 static int fail_count = 0;
45 static int full_count = 0;
46 static int extra_debug = 0;
47 static char *compilation = "compilation.cfg";
48
49 #if defined(_LP64)
50 #define MFLAG "-m64"
51 #elif defined(_ILP32)
52 #define MFLAG "-m32"
53 #endif
54
55 static const char *compilers[] = {
56 "gcc",
57 "clang",
58 NULL
59 };
60
61 /*
62 * We turn off -Wformat-security because the auto-generated tests don't pass
63 * string literals to printf family functions, which will trigger warnings in
64 * some compilers (e.g. clang-16).
65 */
66 static const char *compiler = NULL;
67 static const char *common_flags = "-Wall -Werror -nostdinc -isystem "
68 "/usr/include -Wno-format-security";
69 static const char *c89flags = "-std=c89";
70 static const char *c99flags = "-std=c99";
71 static const char *c11flags = "-std=c11";
72 static const char *c17flags = "-std=c17";
73
74 #define MAXENV 64 /* maximum number of environments (bitmask width) */
75 #define MAXHDR 10 /* maximum # headers to require to access symbol */
76 #define MAXARG 20 /* maximum # of arguments */
77
78 #define WS " \t"
79
80 static int next_env = 0;
81
82 struct compile_env {
83 char *ce_name;
84 char *ce_lang;
85 char *ce_defs;
86 int ce_index;
87 };
88
89 static struct compile_env compile_env[MAXENV];
90
91 struct env_group {
92 char *eg_name;
93 uint64_t eg_mask;
94 struct env_group *eg_next;
95 };
96
97 typedef enum {
98 SYM_TYPE,
99 SYM_VALUE,
100 SYM_DEFINE,
101 SYM_FUNC
102 } sym_type_t;
103
104 struct sym_test {
105 char *st_name;
106 sym_type_t st_type;
107 char *st_hdrs[MAXHDR];
108 char *st_rtype;
109 char *st_atypes[MAXARG];
110 char *st_defval;
111 uint64_t st_test_mask;
112 uint64_t st_need_mask;
113 const char *st_prog;
114 struct sym_test *st_next;
115 };
116
117 struct env_group *env_groups = NULL;
118
119 struct sym_test *sym_tests = NULL;
120 struct sym_test **sym_insert = &sym_tests;
121
122 static char *
mystrdup(const char * s)123 mystrdup(const char *s)
124 {
125 char *r;
126 if ((r = strdup(s)) == NULL) {
127 perror("strdup");
128 exit(1);
129 }
130 return (r);
131 }
132
133 static void *
myzalloc(size_t sz)134 myzalloc(size_t sz)
135 {
136 void *buf;
137 if ((buf = calloc(1, sz)) == NULL) {
138 perror("calloc");
139 exit(1);
140 }
141 return (buf);
142 }
143
144 static void
myasprintf(char ** buf,const char * fmt,...)145 myasprintf(char **buf, const char *fmt, ...)
146 {
147 int rv;
148 va_list va;
149 va_start(va, fmt);
150 rv = vasprintf(buf, fmt, va);
151 va_end(va);
152 if (rv < 0) {
153 perror("vasprintf");
154 exit(1);
155 }
156 }
157
158 static void
append_sym_test(struct sym_test * st)159 append_sym_test(struct sym_test *st)
160 {
161 *sym_insert = st;
162 sym_insert = &st->st_next;
163 }
164
165 static int
find_env_mask(const char * name,uint64_t * mask)166 find_env_mask(const char *name, uint64_t *mask)
167 {
168 for (int i = 0; i < MAXENV; i++) {
169 if (compile_env[i].ce_name != NULL &&
170 strcmp(compile_env[i].ce_name, name) == 0) {
171 *mask |= (1ULL << i);
172 return (0);
173 }
174 }
175
176 for (struct env_group *eg = env_groups; eg != NULL; eg = eg->eg_next) {
177 if (strcmp(name, eg->eg_name) == 0) {
178 *mask |= eg->eg_mask;
179 return (0);
180 }
181 }
182 return (-1);
183 }
184
185
186 static int
expand_env(char * list,uint64_t * mask,char ** erritem)187 expand_env(char *list, uint64_t *mask, char **erritem)
188 {
189 char *item;
190 for (item = strtok(list, WS); item != NULL; item = strtok(NULL, WS)) {
191 if (find_env_mask(item, mask) < 0) {
192 if (erritem != NULL) {
193 *erritem = item;
194 }
195 return (-1);
196 }
197 }
198 return (0);
199 }
200
201 static int
expand_env_list(char * list,uint64_t * test,uint64_t * need,char ** erritem)202 expand_env_list(char *list, uint64_t *test, uint64_t *need, char **erritem)
203 {
204 uint64_t mask = 0;
205 int act;
206 char *item;
207 for (item = strtok(list, WS); item != NULL; item = strtok(NULL, WS)) {
208 switch (item[0]) {
209 case '+':
210 act = 1;
211 item++;
212 break;
213 case '-':
214 act = 0;
215 item++;
216 break;
217 default:
218 act = 1;
219 break;
220 }
221
222 mask = 0;
223 if (find_env_mask(item, &mask) < 0) {
224 if (erritem != NULL) {
225 *erritem = item;
226 }
227 return (-1);
228 }
229 *test |= mask;
230 if (act) {
231 *need |= mask;
232 } else {
233 *need &= ~(mask);
234 }
235 }
236 return (0);
237 }
238
239 static int
do_env(char ** fields,int nfields,char ** err)240 do_env(char **fields, int nfields, char **err)
241 {
242 char *name;
243 char *lang;
244 char *defs;
245
246 if (nfields != 3) {
247 myasprintf(err, "number of fields (%d) != 3", nfields);
248 return (-1);
249 }
250
251 if (next_env >= MAXENV) {
252 myasprintf(err, "too many environments");
253 return (-1);
254 }
255
256 name = fields[0];
257 lang = fields[1];
258 defs = fields[2];
259
260 compile_env[next_env].ce_name = mystrdup(name);
261 compile_env[next_env].ce_lang = mystrdup(lang);
262 compile_env[next_env].ce_defs = mystrdup(defs);
263 compile_env[next_env].ce_index = next_env;
264 next_env++;
265 return (0);
266 }
267
268 static int
do_env_group(char ** fields,int nfields,char ** err)269 do_env_group(char **fields, int nfields, char **err)
270 {
271 char *name;
272 char *list;
273 struct env_group *eg;
274 uint64_t mask;
275 char *item;
276
277 if (nfields != 2) {
278 myasprintf(err, "number of fields (%d) != 2", nfields);
279 return (-1);
280 }
281
282 name = fields[0];
283 list = fields[1];
284 mask = 0;
285
286 if (expand_env(list, &mask, &item) < 0) {
287 myasprintf(err, "reference to undefined env %s", item);
288 return (-1);
289 }
290
291 eg = myzalloc(sizeof (*eg));
292 eg->eg_name = mystrdup(name);
293 eg->eg_mask = mask;
294 eg->eg_next = env_groups;
295 env_groups = eg;
296 return (0);
297 }
298
299 static custr_t *st_custr;
300
301 static void
addprogch(char c)302 addprogch(char c)
303 {
304 if (custr_appendc(st_custr, c) == -1) {
305 perror("custr_appendc");
306 exit(1);
307 }
308 }
309
310 static void
addprogstr(char * s)311 addprogstr(char *s)
312 {
313 if (custr_append(st_custr, s) == -1) {
314 perror("custr_append");
315 exit(1);
316 }
317 }
318
319 static void
addprogfmt(const char * fmt,...)320 addprogfmt(const char *fmt, ...)
321 {
322 va_list va;
323 va_start(va, fmt);
324 if (custr_append_vprintf(st_custr, fmt, va) == -1) {
325 perror("custr_append_vprintf");
326 exit(1);
327 }
328 va_end(va);
329 }
330
331 static void
mkprog(struct sym_test * st)332 mkprog(struct sym_test *st)
333 {
334 char *s = NULL;
335
336 custr_reset(st_custr);
337
338 for (int i = 0; i < MAXHDR && st->st_hdrs[i] != NULL; i++) {
339 addprogfmt("#include <%s>\n", st->st_hdrs[i]);
340 }
341
342 if (st->st_rtype != NULL) {
343 for (s = st->st_rtype; *s; s++) {
344 addprogch(*s);
345 if (*s == '(') {
346 s++;
347 addprogch(*s);
348 s++;
349 break;
350 }
351 }
352 addprogch(' ');
353 }
354
355 /* for function pointers, s is closing suffix, otherwise empty */
356
357 switch (st->st_type) {
358 case SYM_TYPE:
359 addprogstr("test_type;");
360 break;
361
362 case SYM_VALUE:
363 addprogfmt("test_value%s;\n", s); /* s usually empty */
364 addprogstr("void\ntest_func(void)\n{\n");
365 addprogfmt("\ttest_value = %s;\n}", st->st_name);
366 break;
367
368 case SYM_DEFINE:
369 addprogfmt("#if !defined(%s)", st->st_name);
370 if (st->st_defval != NULL)
371 addprogfmt("|| %s != %s", st->st_name, st->st_defval);
372 addprogfmt("\n#error %s is not defined or has the wrong value",
373 st->st_name);
374 addprogfmt("\n#endif\n");
375 break;
376
377 case SYM_FUNC:
378 addprogstr("\ntest_func(");
379 for (int i = 0; i < MAXARG && st->st_atypes[i] != NULL; i++) {
380 int didname = 0;
381 if (i > 0) {
382 addprogstr(", ");
383 }
384 if (strcmp(st->st_atypes[i], "void") == 0) {
385 didname = 1;
386 }
387 if (strcmp(st->st_atypes[i], "") == 0) {
388 didname = 1;
389 addprogstr("void");
390 }
391
392 /* print the argument list */
393 for (char *a = st->st_atypes[i]; *a; a++) {
394 if (*a == '(' && a[1] == '*' && !didname) {
395 addprogfmt("(*a%d", i);
396 didname = 1;
397 a++;
398 } else if (*a == '[' && !didname) {
399 addprogfmt("a%d[", i);
400 didname = 1;
401 } else {
402 addprogch(*a);
403 }
404 }
405 if (!didname) {
406 addprogfmt(" a%d", i);
407 }
408 }
409
410 if (st->st_atypes[0] == NULL) {
411 addprogstr("void");
412 }
413
414 /*
415 * Close argument list, and closing ")" for func ptrs.
416 * Note that for non-function pointers, s will be empty
417 * below, otherwise it points to the trailing argument
418 * list.
419 */
420 addprogfmt(")%s\n{\n\t", s);
421
422 if (strcmp(st->st_rtype, "") != 0 &&
423 strcmp(st->st_rtype, "void") != 0) {
424 addprogstr("return ");
425 }
426
427 /* add the function call */
428 addprogfmt("%s(", st->st_name);
429 for (int i = 0; i < MAXARG && st->st_atypes[i] != NULL; i++) {
430 if (strcmp(st->st_atypes[i], "") != 0 &&
431 strcmp(st->st_atypes[i], "void") != 0) {
432 addprogfmt("%sa%d", i > 0 ? ", " : "", i);
433 }
434 }
435
436 addprogstr(");\n}");
437 break;
438 }
439
440 addprogch('\n');
441
442 st->st_prog = custr_cstr(st_custr);
443 }
444
445 static int
add_envs(struct sym_test * st,char * envs,char ** err)446 add_envs(struct sym_test *st, char *envs, char **err)
447 {
448 char *item;
449 if (expand_env_list(envs, &st->st_test_mask, &st->st_need_mask,
450 &item) < 0) {
451 myasprintf(err, "bad env action %s", item);
452 return (-1);
453 }
454 return (0);
455 }
456
457 static int
add_headers(struct sym_test * st,char * hdrs,char ** err)458 add_headers(struct sym_test *st, char *hdrs, char **err)
459 {
460 int i = 0;
461
462 for (char *h = strsep(&hdrs, ";"); h != NULL; h = strsep(&hdrs, ";")) {
463 if (i >= MAXHDR) {
464 myasprintf(err, "too many headers");
465 return (-1);
466 }
467 test_trim(&h);
468 st->st_hdrs[i++] = mystrdup(h);
469 }
470
471 return (0);
472 }
473
474 static int
add_arg_types(struct sym_test * st,char * atype,char ** err)475 add_arg_types(struct sym_test *st, char *atype, char **err)
476 {
477 int i = 0;
478 char *a;
479 for (a = strsep(&atype, ";"); a != NULL; a = strsep(&atype, ";")) {
480 if (i >= MAXARG) {
481 myasprintf(err, "too many arguments");
482 return (-1);
483 }
484 test_trim(&a);
485 st->st_atypes[i++] = mystrdup(a);
486 }
487
488 return (0);
489 }
490
491 static int
do_type(char ** fields,int nfields,char ** err)492 do_type(char **fields, int nfields, char **err)
493 {
494 char *decl;
495 char *hdrs;
496 char *envs;
497 struct sym_test *st;
498
499 if (nfields != 3) {
500 myasprintf(err, "number of fields (%d) != 3", nfields);
501 return (-1);
502 }
503 decl = fields[0];
504 hdrs = fields[1];
505 envs = fields[2];
506
507 st = myzalloc(sizeof (*st));
508 st->st_type = SYM_TYPE;
509 st->st_name = mystrdup(decl);
510 st->st_rtype = mystrdup(decl);
511
512 if ((add_envs(st, envs, err) < 0) ||
513 (add_headers(st, hdrs, err) < 0)) {
514 return (-1);
515 }
516 append_sym_test(st);
517
518 return (0);
519 }
520
521 static int
do_value(char ** fields,int nfields,char ** err)522 do_value(char **fields, int nfields, char **err)
523 {
524 char *name;
525 char *type;
526 char *hdrs;
527 char *envs;
528 struct sym_test *st;
529
530 if (nfields != 4) {
531 myasprintf(err, "number of fields (%d) != 4", nfields);
532 return (-1);
533 }
534 name = fields[0];
535 type = fields[1];
536 hdrs = fields[2];
537 envs = fields[3];
538
539 st = myzalloc(sizeof (*st));
540 st->st_type = SYM_VALUE;
541 st->st_name = mystrdup(name);
542 st->st_rtype = mystrdup(type);
543
544 if ((add_envs(st, envs, err) < 0) ||
545 (add_headers(st, hdrs, err) < 0)) {
546 return (-1);
547 }
548 append_sym_test(st);
549
550 return (0);
551 }
552
553 static int
do_define(char ** fields,int nfields,char ** err)554 do_define(char **fields, int nfields, char **err)
555 {
556 char *name, *value, *hdrs, *envs;
557 struct sym_test *st;
558
559 if (nfields != 4) {
560 myasprintf(err, "number of fields (%d) != 4", nfields);
561 return (-1);
562 }
563
564 name = fields[0];
565 value = fields[1];
566 hdrs = fields[2];
567 envs = fields[3];
568
569 st = myzalloc(sizeof (*st));
570 st->st_type = SYM_DEFINE;
571 st->st_name = mystrdup(name);
572
573 /*
574 * A value to compare against is optional. trim will leave it as a null
575 * pointer if there's nothing there.
576 */
577 test_trim(&value);
578 if (*value != '\0')
579 st->st_defval = mystrdup(value);
580
581 if ((add_envs(st, envs, err) < 0) ||
582 (add_headers(st, hdrs, err) < 0)) {
583 return (-1);
584 }
585
586 append_sym_test(st);
587
588 return (0);
589 }
590
591 static int
do_func(char ** fields,int nfields,char ** err)592 do_func(char **fields, int nfields, char **err)
593 {
594 char *name;
595 char *rtype;
596 char *atype;
597 char *hdrs;
598 char *envs;
599 struct sym_test *st;
600
601 if (nfields != 5) {
602 myasprintf(err, "number of fields (%d) != 5", nfields);
603 return (-1);
604 }
605 name = fields[0];
606 rtype = fields[1];
607 atype = fields[2];
608 hdrs = fields[3];
609 envs = fields[4];
610
611 st = myzalloc(sizeof (*st));
612 st->st_type = SYM_FUNC;
613 st->st_name = mystrdup(name);
614 st->st_rtype = mystrdup(rtype);
615
616 if ((add_envs(st, envs, err) < 0) ||
617 (add_headers(st, hdrs, err) < 0) ||
618 (add_arg_types(st, atype, err) < 0)) {
619 return (-1);
620 }
621 append_sym_test(st);
622
623 return (0);
624 }
625
626 struct sym_test *
next_sym_test(struct sym_test * st)627 next_sym_test(struct sym_test *st)
628 {
629 return (st == NULL ? sym_tests : st->st_next);
630 }
631
632 const char *
sym_test_prog(struct sym_test * st)633 sym_test_prog(struct sym_test *st)
634 {
635 if (st->st_prog == NULL) {
636 mkprog(st);
637 }
638 return (st->st_prog);
639 }
640
641 const char *
sym_test_name(struct sym_test * st)642 sym_test_name(struct sym_test *st)
643 {
644 return (st->st_name);
645 }
646
647 /*
648 * Iterate through tests. Pass in NULL for cenv to begin the iteration. For
649 * subsequent iterations, use the return value from the previous iteration.
650 * Returns NULL when there are no more environments.
651 */
652 struct compile_env *
sym_test_env(struct sym_test * st,struct compile_env * cenv,int * need)653 sym_test_env(struct sym_test *st, struct compile_env *cenv, int *need)
654 {
655 int i = cenv ? cenv->ce_index + 1: 0;
656 uint64_t b = 1ULL << i;
657
658 while ((i < MAXENV) && (b != 0)) {
659 cenv = &compile_env[i];
660 if (b & st->st_test_mask) {
661 *need = (st->st_need_mask & b) ? 1 : 0;
662 return (cenv);
663 }
664 b <<= 1;
665 i++;
666 }
667 return (NULL);
668 }
669
670 const char *
env_name(struct compile_env * cenv)671 env_name(struct compile_env *cenv)
672 {
673 return (cenv->ce_name);
674 }
675
676 const char *
env_lang(struct compile_env * cenv)677 env_lang(struct compile_env *cenv)
678 {
679 return (cenv->ce_lang);
680 }
681
682 const char *
env_defs(struct compile_env * cenv)683 env_defs(struct compile_env *cenv)
684 {
685 return (cenv->ce_defs);
686 }
687
688 static void
show_file(test_t t,const char * path)689 show_file(test_t t, const char *path)
690 {
691 FILE *f;
692 char *buf = NULL;
693 size_t cap = 0;
694 int line = 1;
695
696 f = fopen(path, "r");
697 if (f == NULL) {
698 test_debugf(t, "fopen(%s): %s", path, strerror(errno));
699 return;
700 }
701
702 test_debugf(t, "----->> begin (%s) <<------", path);
703 while (getline(&buf, &cap, f) >= 0) {
704 (void) strtok(buf, "\r\n");
705 test_debugf(t, "%d: %s", line, buf);
706 line++;
707 }
708 test_debugf(t, "----->> end (%s) <<------", path);
709 (void) fclose(f);
710 }
711
712 static void
cleanup(void)713 cleanup(void)
714 {
715 if (ofile != NULL) {
716 (void) unlink(ofile);
717 free(ofile);
718 ofile = NULL;
719 }
720 if (lfile != NULL) {
721 (void) unlink(lfile);
722 free(lfile);
723 lfile = NULL;
724 }
725 if (cfile != NULL) {
726 (void) unlink(cfile);
727 free(cfile);
728 cfile = NULL;
729 }
730 if (efile != NULL) {
731 (void) unlink(efile);
732 free(efile);
733 efile = NULL;
734 }
735 if (dname) {
736 (void) rmdir(dname);
737 free(dname);
738 dname = NULL;
739 }
740 }
741
742 static int
mkworkdir(void)743 mkworkdir(void)
744 {
745 char b[32];
746 char *d;
747
748 cleanup();
749
750 (void) strlcpy(b, "/tmp/symbols_testXXXXXX", sizeof (b));
751 if ((d = mkdtemp(b)) == NULL) {
752 perror("mkdtemp");
753 return (-1);
754 }
755 dname = mystrdup(d);
756 myasprintf(&cfile, "%s/compile_test.c", d);
757 myasprintf(&ofile, "%s/compile_test.o", d);
758 myasprintf(&lfile, "%s/compile_test.log", d);
759 myasprintf(&efile, "%s/compile_test.exe", d);
760 return (0);
761 }
762
763 typedef enum {
764 SYM_COMP_STUDIO = 51,
765 SYM_COMP_CLANG,
766 SYM_COMP_GCC,
767 SYM_COMP_UNKNOWN = 99
768 } sym_comp_t;
769
770 static bool
test_compiler(test_t t,const char * cc)771 test_compiler(test_t t, const char *cc)
772 {
773 char cmd[256];
774 int rv;
775
776 (void) snprintf(cmd, sizeof (cmd), "%s %s %s -o %s >/dev/null 2>&1",
777 cc, MFLAG, cfile, efile);
778 test_debugf(t, "trying %s", cmd);
779 rv = system(cmd);
780
781 test_debugf(t, "result: %d", rv);
782
783 if ((rv < 0) || !WIFEXITED(rv) || WEXITSTATUS(rv) != 0)
784 return (false);
785
786 rv = system(efile);
787 if (rv >= 0 && WIFEXITED(rv)) {
788 rv = WEXITSTATUS(rv);
789 } else {
790 rv = -1;
791 }
792
793 switch (rv) {
794 case SYM_COMP_STUDIO:
795 test_failed(t, "Sun Studio is not supported");
796 return (false);
797 case SYM_COMP_CLANG:
798 test_debugf(t, "Found clang");
799 test_passed(t);
800 break;
801 case SYM_COMP_GCC:
802 test_debugf(t, "Found gcc");
803 test_passed(t);
804 break;
805 case SYM_COMP_UNKNOWN:
806 test_debugf(t, "Found unknown (unsupported) compiler");
807 return (false);
808 default:
809 return (false);
810 }
811
812 test_debugf(t, "compiler: %s", cc);
813 return (true);
814 }
815
816 static void
find_compiler(void)817 find_compiler(void)
818 {
819 test_t t;
820 int i;
821 FILE *cf;
822
823 t = test_start("finding compiler");
824
825 if ((cf = fopen(cfile, "w+")) == NULL) {
826 test_failed(t, "Unable to open %s for write: %s", cfile,
827 strerror(errno));
828 return;
829 }
830
831 /*
832 * clang defines both __GNUC__ and __clang__, therefore test for
833 * __clang__ ahead of __GNUC__.
834 */
835 (void) fprintf(cf, "#include <stdlib.h>\n");
836 (void) fprintf(cf, "int main(int argc, char **argv) {\n");
837 (void) fprintf(cf, "#if defined(__SUNPRO_C)\n");
838 (void) fprintf(cf, "exit(%d);\n", SYM_COMP_STUDIO);
839 (void) fprintf(cf, "#elif defined(__clang__)\n");
840 (void) fprintf(cf, "exit(%d);\n", SYM_COMP_CLANG);
841 (void) fprintf(cf, "#elif defined(__GNUC__)\n");
842 (void) fprintf(cf, "exit(%d);\n", SYM_COMP_GCC);
843 (void) fprintf(cf, "#else\n");
844 (void) fprintf(cf, "exit(%d)\n", SYM_COMP_UNKNOWN);
845 (void) fprintf(cf, "#endif\n}\n");
846 (void) fclose(cf);
847
848 if (compiler != NULL) {
849 if (test_compiler(t, compiler)) {
850 return;
851 }
852
853 test_failed(t, "compiler %s is not usable", compiler);
854 }
855
856 for (i = 0; compilers[i] != NULL; i++) {
857 if (test_compiler(t, compilers[i])) {
858 compiler = compilers[i];
859 return;
860 }
861 }
862 test_failed(t, "No compiler found.");
863 }
864
865 static int
do_compile(test_t t,struct sym_test * st,struct compile_env * cenv,int need)866 do_compile(test_t t, struct sym_test *st, struct compile_env *cenv, int need)
867 {
868 char *cmd;
869 FILE *logf;
870 FILE *dotc;
871 const char *prog, *cflags, *lang;
872
873 full_count++;
874
875 if ((dotc = fopen(cfile, "w+")) == NULL) {
876 test_failed(t, "fopen(%s): %s", cfile, strerror(errno));
877 return (-1);
878 }
879 prog = sym_test_prog(st);
880 if (fwrite(prog, 1, strlen(prog), dotc) < strlen(prog)) {
881 test_failed(t, "fwrite: %s", strerror(errno));
882 (void) fclose(dotc);
883 return (-1);
884 }
885 if (fclose(dotc) < 0) {
886 test_failed(t, "fclose: %s", strerror(errno));
887 return (-1);
888 }
889
890 (void) unlink(ofile);
891
892 if (strcmp(env_lang(cenv), "c99") == 0) {
893 lang = "c99";
894 cflags = c99flags;
895 } else if (strcmp(env_lang(cenv), "c11") == 0) {
896 lang = "c11";
897 cflags = c11flags;
898 } else if (strcmp(env_lang(cenv), "c17") == 0) {
899 lang = "c17";
900 cflags = c17flags;
901 } else {
902 lang = "c89";
903 cflags = c89flags;
904 }
905
906 if (cflags == NULL) {
907 test_failed(t, "compiler %s does not support %s", compiler,
908 lang);
909 return (-1);
910 }
911
912 myasprintf(&cmd, "%s %s %s %s -c %s -o %s >>%s 2>&1",
913 compiler, cflags, common_flags, env_defs(cenv), cfile, ofile,
914 lfile);
915
916 if (extra_debug) {
917 test_debugf(t, "command: %s", cmd);
918 }
919
920 if ((logf = fopen(lfile, "w+")) == NULL) {
921 test_failed(t, "fopen: %s", strerror(errno));
922 return (-1);
923 }
924 (void) fprintf(logf, "===================\n");
925 (void) fprintf(logf, "PROGRAM:\n%s\n", sym_test_prog(st));
926 (void) fprintf(logf, "COMMAND: %s\n", cmd);
927 (void) fprintf(logf, "EXPECT: %s\n", need ? "OK" : "FAIL");
928 (void) fclose(logf);
929
930 switch (system(cmd)) {
931 case -1:
932 test_failed(t, "error compiling in %s: %s", env_name(cenv),
933 strerror(errno));
934 return (-1);
935 case 0:
936 if (!need) {
937 fail_count++;
938 show_file(t, lfile);
939 test_failed(t, "symbol visible in %s", env_name(cenv));
940 return (-1);
941 }
942 break;
943 default:
944 if (need) {
945 fail_count++;
946 show_file(t, lfile);
947 test_failed(t, "error compiling in %s", env_name(cenv));
948 return (-1);
949 }
950 break;
951 }
952 good_count++;
953 return (0);
954 }
955
956 static void
test_compile(void)957 test_compile(void)
958 {
959 struct sym_test *st;
960 struct compile_env *cenv;
961 test_t t;
962 int need;
963
964 for (st = next_sym_test(NULL); st; st = next_sym_test(st)) {
965 if ((sym != NULL) && strcmp(sym, sym_test_name(st))) {
966 continue;
967 }
968 /* XXX: we really want a sym_test_desc() */
969 for (cenv = sym_test_env(st, NULL, &need);
970 cenv != NULL;
971 cenv = sym_test_env(st, cenv, &need)) {
972 t = test_start("%s : %c%s", sym_test_name(st),
973 need ? '+' : '-', env_name(cenv));
974 if (do_compile(t, st, cenv, need) == 0) {
975 test_passed(t);
976 }
977 }
978 }
979
980 if (full_count > 0) {
981 test_summary();
982 }
983 }
984
985 int
main(int argc,char ** argv)986 main(int argc, char **argv)
987 {
988 int optc;
989 int optC = 0;
990
991 while ((optc = getopt(argc, argv, "DdfCs:c:")) != EOF) {
992 switch (optc) {
993 case 'd':
994 test_set_debug();
995 break;
996 case 'f':
997 test_set_force();
998 break;
999 case 'D':
1000 test_set_debug();
1001 extra_debug++;
1002 break;
1003 case 'c':
1004 compiler = optarg;
1005 break;
1006 case 'C':
1007 optC++;
1008 break;
1009 case 's':
1010 sym = optarg;
1011 break;
1012 default:
1013 (void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]);
1014 exit(1);
1015 }
1016 }
1017
1018 if (test_load_config(NULL, compilation,
1019 "env", do_env, "env_group", do_env_group, NULL) < 0) {
1020 exit(1);
1021 }
1022
1023 while (optind < argc) {
1024 if (test_load_config(NULL, argv[optind++],
1025 "type", do_type,
1026 "value", do_value,
1027 "define", do_define,
1028 "func", do_func,
1029 NULL) < 0) {
1030 exit(1);
1031 }
1032 }
1033
1034 if (atexit(cleanup) != 0) {
1035 perror("atexit");
1036 exit(1);
1037 }
1038
1039 if (custr_alloc(&st_custr) == -1) {
1040 perror("custr");
1041 exit(1);
1042 }
1043
1044 if (mkworkdir() < 0) {
1045 perror("mkdir");
1046 exit(1);
1047 }
1048
1049 find_compiler();
1050 if (!optC)
1051 test_compile();
1052
1053 exit(0);
1054 }
1055