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 2014 Garrett D'Amore <garrett@damore.org>
14 */
15
16 /*
17 * This program tests symbol visibility using the /usr/bin/c89 and
18 * /usr/bin/c99 programs.
19 *
20 * See symbols_defs.c for the actual list of symbols tested.
21 */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <err.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <note.h>
32 #include <sys/wait.h>
33 #include "test_common.h"
34
35 char *dname;
36 char *cfile;
37 char *ofile;
38 char *lfile;
39
40 const char *sym = NULL;
41
42 static int good_count = 0;
43 static int fail_count = 0;
44 static int full_count = 0;
45 static int extra_debug = 0;
46 static char *compilation = "compilation.cfg";
47
48 #if defined(_LP64)
49 #define MFLAG "-m64"
50 #elif defined(_ILP32)
51 #define MFLAG "-m32"
52 #endif
53
54 const char *compilers[] = {
55 "cc",
56 "gcc",
57 "/opt/SUNWspro/bin/cc",
58 "/opt/gcc/4.4.4/bin/gcc",
59 "/opt/sunstudio12.1/bin/cc",
60 "/opt/sfw/bin/gcc",
61 "/usr/local/bin/gcc",
62 NULL
63 };
64
65 const char *puname[] = {
66 "",
67 "/usr/bin/puname -S "
68 };
69
70 char *compiler = NULL;
71 const char *c89flags = NULL;
72 const char *c99flags = NULL;
73
74 /* ====== BEGIN ======== */
75
76 #include <errno.h>
77 #include <string.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <ctype.h>
81 #include <stdint.h>
82
83 #define MAXENV 64 /* bits */
84 #define MAXHDR 10 /* maximum # headers to require to access symbol */
85 #define MAXARG 20 /* maximum # of arguments */
86
87 #define WS " \t"
88
89 static int next_env = 0;
90
91 struct compile_env {
92 char *name;
93 char *lang;
94 char *defs;
95 int index;
96 };
97
98 static struct compile_env compile_env[MAXENV];
99
100 struct env_group {
101 char *name;
102 uint64_t mask;
103 struct env_group *next;
105
106 typedef enum { SYM_TYPE, SYM_VALUE, SYM_FUNC } sym_type_t;
107
108 struct sym_test {
109 char *name;
110 sym_type_t type;
111 char *hdrs[MAXHDR];
112 char *rtype;
113 char *atypes[MAXARG];
114 uint64_t test_mask;
115 uint64_t need_mask;
116 char *prog;
117 struct sym_test *next;
118 };
119
120 struct env_group *env_groups = NULL;
121
122 struct sym_test *sym_tests = NULL;
123 struct sym_test **sym_insert = &sym_tests;
124
125 static void
126 append_sym_test(struct sym_test *st)
127 {
128 *sym_insert = st;
129 sym_insert = &st->next;
130 }
131
132 static int
133 find_env_mask(const char *name, uint64_t *mask)
134 {
135 for (int i = 0; i < 64; i++) {
136 if (compile_env[i].name != NULL &&
137 strcmp(compile_env[i].name, name) == 0) {
138 *mask |= (1ULL << i);
139 return (0);
140 }
141 }
142
143 for (struct env_group *eg = env_groups; eg != NULL; eg = eg->next) {
144 if (strcmp(name, eg->name) == 0) {
145 *mask |= eg->mask;
194 return (-1);
195 }
196 *test |= mask;
197 if (act) {
198 *need |= mask;
199 } else {
200 *need &= ~(mask);
201 }
202 }
203 return (0);
204 }
205
206 static int
207 do_env(char **fields, int nfields, char **err)
208 {
209 char *name;
210 char *lang;
211 char *defs;
212
213 if (nfields != 3) {
214 (void) asprintf(err, "number of fields (%d) != 3", nfields);
215 return (-1);
216 }
217
218 if (next_env >= MAXENV) {
219 (void) asprintf(err, "too many environments");
220 return (-1);
221 }
222
223 name = fields[0];
224 lang = fields[1];
225 defs = fields[2];
226
227 compile_env[next_env].name = strdup(name);
228 compile_env[next_env].lang = strdup(lang);
229 compile_env[next_env].defs = strdup(defs);
230 compile_env[next_env].index = next_env;
231 next_env++;
232 return (0);
233 }
234
235 static int
236 do_env_group(char **fields, int nfields, char **err)
237 {
238 char *name;
239 char *list;
240 struct env_group *eg;
241 uint64_t mask;
242 char *item;
243
244 if (nfields != 2) {
245 (void) asprintf(err, "number of fields (%d) != 2", nfields);
246 return (-1);
247 }
248
249 name = fields[0];
250 list = fields[1];
251 mask = 0;
252
253 if (expand_env(list, &mask, &item) < 0) {
254 (void) asprintf(err, "reference to undefined env %s", item);
255 return (-1);
256 }
257
258 eg = calloc(1, sizeof (*eg));
259 eg->name = strdup(name);
260 eg->mask = mask;
261 eg->next = env_groups;
262 env_groups = eg;
263 return (0);
264 }
265
266 static void
267 mkprog(struct sym_test *st)
268 {
269 static char buf[2048];
270 char *s;
271 char *prog = buf;
272
273 *prog = 0;
274
275 #define ADDSTR(p, str) (void) strcpy(p, str); p += strlen(p)
276 #define ADDFMT(p, ...) \
277 (void) snprintf(p, sizeof (buf) - (p-buf), __VA_ARGS__); \
278 p += strlen(p)
279 #define ADDCHR(p, c) *p++ = c; *p = 0
280
281 for (int i = 0; i < MAXHDR && st->hdrs[i] != NULL; i++) {
282 ADDFMT(prog, "#include <%s>\n", st->hdrs[i]);
283 }
284
285 for (s = st->rtype; *s; s++) {
286 ADDCHR(prog, *s);
287 if (*s == '(') {
288 s++;
289 ADDCHR(prog, *s);
290 s++;
291 break;
292 }
293 }
294 ADDCHR(prog, ' ');
295
296 /* for function pointers, s is closing suffix, otherwise empty */
297
298 switch (st->type) {
299 case SYM_TYPE:
300 ADDFMT(prog, "test_type;", st->rtype);
301 break;
302
303 case SYM_VALUE:
304 ADDFMT(prog, "test_value%s;\n", s); /* s usually empty */
305 ADDSTR(prog, "void\ntest_func(void)\n{\n");
306 ADDFMT(prog, "\ttest_value = %s;\n}",
307 st->name);
308 break;
309
310 case SYM_FUNC:
311 ADDSTR(prog, "\ntest_func(");
312 for (int i = 0; st->atypes[i] != NULL && i < MAXARG; i++) {
313 int didname = 0;
314 if (i > 0) {
315 ADDSTR(prog, ", ");
316 }
317 if (strcmp(st->atypes[i], "void") == 0) {
318 didname = 1;
319 }
320 if (strcmp(st->atypes[i], "") == 0) {
321 didname = 1;
322 ADDSTR(prog, "void");
323 }
324
325 /* print the argument list */
326 for (char *a = st->atypes[i]; *a; a++) {
327 if (*a == '(' && a[1] == '*' && !didname) {
328 ADDFMT(prog, "(*a%d", i);
329 didname = 1;
330 a++;
331 } else if (*a == '[' && !didname) {
332 ADDFMT(prog, "a%d[", i);
333 didname = 1;
334 } else {
335 ADDCHR(prog, *a);
336 }
337 }
338 if (!didname) {
339 ADDFMT(prog, " a%d", i);
340 }
341 }
342
343 if (st->atypes[0] == NULL) {
344 ADDSTR(prog, "void");
345 }
346
347 /* close argument list, and closing ")" for func ptrs */
348 ADDFMT(prog, ")%s\n{\n\t", s); /* NB: s is normally empty */
349
350 if (strcmp(st->rtype, "") != 0 &&
351 strcmp(st->rtype, "void") != 0) {
352 ADDSTR(prog, "return ");
353 }
354
355 /* add the function call */
356 ADDFMT(prog, "%s(", st->name);
357 for (int i = 0; st->atypes[i] != NULL && i < MAXARG; i++) {
358 if (strcmp(st->atypes[i], "") != 0 &&
359 strcmp(st->atypes[i], "void") != 0) {
360 ADDFMT(prog, "%sa%d", i > 0 ? ", " : "", i);
361 }
362 }
363
364 ADDSTR(prog, ");\n}");
365 break;
366 }
367
368 ADDCHR(prog, '\n');
369
370 st->prog = strdup(buf);
371 }
372
373 static int
374 add_envs(struct sym_test *st, char *envs, char **err)
375 {
376 char *item;
377 if (expand_env_list(envs, &st->test_mask, &st->need_mask, &item) < 0) {
378 (void) asprintf(err, "bad env action %s", item);
379 return (-1);
380 }
381 return (0);
382 }
383
384 static int
385 add_headers(struct sym_test *st, char *hdrs, char **err)
386 {
387 int i = 0;
388
389 for (char *h = strsep(&hdrs, ";"); h != NULL; h = strsep(&hdrs, ";")) {
390 if (i >= MAXHDR) {
391 (void) asprintf(err, "too many headers");
392 return (-1);
393 }
394 test_trim(&h);
395 st->hdrs[i++] = strdup(h);
396 }
397
398 return (0);
399 }
400
401 static int
402 add_arg_types(struct sym_test *st, char *atype, char **err)
403 {
404 int i = 0;
405 char *a;
406 for (a = strsep(&atype, ";"); a != NULL; a = strsep(&atype, ";")) {
407 if (i >= MAXARG) {
408 (void) asprintf(err, "too many arguments");
409 return (-1);
410 }
411 test_trim(&a);
412 st->atypes[i++] = strdup(a);
413 }
414
415 return (0);
416 }
417
418 static int
419 do_type(char **fields, int nfields, char **err)
420 {
421 char *decl;
422 char *hdrs;
423 char *envs;
424 struct sym_test *st;
425
426 if (nfields != 3) {
427 (void) asprintf(err, "number of fields (%d) != 3", nfields);
428 return (-1);
429 }
430 decl = fields[0];
431 hdrs = fields[1];
432 envs = fields[2];
433
434 st = calloc(1, sizeof (*st));
435 st->type = SYM_TYPE;
436 st->name = strdup(decl);
437 st->rtype = strdup(decl);
438
439 if ((add_envs(st, envs, err) < 0) ||
440 (add_headers(st, hdrs, err) < 0)) {
441 return (-1);
442 }
443 append_sym_test(st);
444
445 return (0);
446 }
447
448 static int
449 do_value(char **fields, int nfields, char **err)
450 {
451 char *name;
452 char *type;
453 char *hdrs;
454 char *envs;
455 struct sym_test *st;
456
457 if (nfields != 4) {
458 (void) asprintf(err, "number of fields (%d) != 4", nfields);
459 return (-1);
460 }
461 name = fields[0];
462 type = fields[1];
463 hdrs = fields[2];
464 envs = fields[3];
465
466 st = calloc(1, sizeof (*st));
467 st->type = SYM_VALUE;
468 st->name = strdup(name);
469 st->rtype = strdup(type);
470
471 if ((add_envs(st, envs, err) < 0) ||
472 (add_headers(st, hdrs, err) < 0)) {
473 return (-1);
474 }
475 append_sym_test(st);
476
477 return (0);
478 }
479
480 static int
481 do_func(char **fields, int nfields, char **err)
482 {
483 char *name;
484 char *rtype;
485 char *atype;
486 char *hdrs;
487 char *envs;
488 struct sym_test *st;
489
490 if (nfields != 5) {
491 (void) asprintf(err, "number of fields (%d) != 5", nfields);
492 return (-1);
493 }
494 name = fields[0];
495 rtype = fields[1];
496 atype = fields[2];
497 hdrs = fields[3];
498 envs = fields[4];
499
500 st = calloc(1, sizeof (*st));
501 st->type = SYM_FUNC;
502 st->name = strdup(name);
503 st->rtype = strdup(rtype);
504
505 if ((add_envs(st, envs, err) < 0) ||
506 (add_headers(st, hdrs, err) < 0) ||
507 (add_arg_types(st, atype, err) < 0)) {
508 return (-1);
509 }
510 append_sym_test(st);
511
512 return (0);
513 }
514
515 struct sym_test *
516 next_sym_test(struct sym_test *st)
517 {
518 return (st == NULL ? sym_tests : st->next);
519 }
520
521 const char *
522 sym_test_prog(struct sym_test *st)
523 {
524 if (st->prog == NULL) {
525 mkprog(st);
526 }
527 return (st->prog);
528 }
529
530 const char *
531 sym_test_name(struct sym_test *st)
532 {
533 return (st->name);
534 }
535
536 /*
537 * Iterate through tests. Pass NULL for cenv first time, and previous result
538 * the next. Returns NULL when no more environments.
539 */
540 struct compile_env *
541 sym_test_env(struct sym_test *st, struct compile_env *cenv, int *need)
542 {
543 int i = cenv ? cenv->index + 1: 0;
544 uint64_t b = 1ULL << i;
545
546 while ((i < MAXENV) && (b != 0)) {
547 cenv = &compile_env[i];
548 if (b & st->test_mask) {
549 *need = (st->need_mask & b) ? 1 : 0;
550 return (cenv);
551 }
552 b <<= 1;
553 i++;
554 }
555 return (NULL);
556 }
557
558 const char *
565 env_lang(struct compile_env *cenv)
566 {
567 return (cenv->lang);
568 }
569
570 const char *
571 env_defs(struct compile_env *cenv)
572 {
573 return (cenv->defs);
574 }
575
576 static void
577 show_file(test_t t, const char *path)
578 {
579 FILE *f;
580 char *buf = NULL;
581 size_t cap = 0;
582 int line = 1;
583
584 f = fopen(path, "r");
585
586 test_debugf(t, "----->> begin (%s) <<------", path);
587 while (getline(&buf, &cap, f) >= 0) {
588 (void) strtok(buf, "\r\n");
589 test_debugf(t, "%d: %s", line, buf);
590 line++;
591 }
592 test_debugf(t, "----->> end (%s) <<------", path);
593 (void) fclose(f);
594 }
595
596 static void
597 cleanup(void)
598 {
599 if (ofile != NULL) {
600 (void) unlink(ofile);
601 free(ofile);
602 ofile = NULL;
603 }
604 if (lfile != NULL) {
605 (void) unlink(lfile);
606 free(lfile);
607 lfile = NULL;
608 }
609 if (cfile != NULL) {
610 (void) unlink(cfile);
611 free(cfile);
612 cfile = NULL;
613 }
614 if (dname) {
615 (void) rmdir(dname);
616 free(dname);
617 dname = NULL;
618 }
619 }
620
621 static int
622 mkworkdir(void)
623 {
624 char b[32];
625 char *d;
626
627 cleanup();
628
629 (void) strlcpy(b, "/tmp/symbols_testXXXXXX", sizeof (b));
630 if ((d = mkdtemp(b)) == NULL) {
631 perror("mkdtemp");
632 return (-1);
633 }
634 dname = strdup(d);
635 (void) asprintf(&cfile, "%s/compile_test.c", d);
636 (void) asprintf(&ofile, "%s/compile_test.o", d);
637 (void) asprintf(&lfile, "%s/compile_test.log", d);
638 return (0);
639 }
640
641 void
642 find_compiler(void)
643 {
644 test_t t;
645 int i;
646 FILE *cf;
647
648 t = test_start("finding compiler");
649
650 if ((cf = fopen(cfile, "w+")) == NULL) {
651 return;
652 }
653 (void) fprintf(cf, "#include <stdio.h>\n");
654 (void) fprintf(cf, "int main(int argc, char **argv) {\n");
655 (void) fprintf(cf, "#if defined(__SUNPRO_C)\n");
656 (void) fprintf(cf, "exit(51);\n");
657 (void) fprintf(cf, "#elif defined(__GNUC__)\n");
658 (void) fprintf(cf, "exit(52);\n");
659 (void) fprintf(cf, "#else\n");
660 (void) fprintf(cf, "exit(99)\n");
661 (void) fprintf(cf, "#endif\n}\n");
662 (void) fclose(cf);
663
664 for (i = 0; compilers[i/2] != NULL; i++) {
665 char cmd[256];
666 int rv;
667
668 (void) snprintf(cmd, sizeof (cmd),
669 "%s%s %s %s -o %s >/dev/null 2>&1",
670 puname[i%2], compilers[i/2], MFLAG, cfile, ofile);
671 test_debugf(t, "trying %s", cmd);
672 rv = system(cmd);
673
674 test_debugf(t, "result: %d", rv);
675
676 if ((rv < 0) || !WIFEXITED(rv) || WEXITSTATUS(rv) != 0)
677 continue;
678
679 rv = system(ofile);
680 if (rv >= 0 && WIFEXITED(rv)) {
681 rv = WEXITSTATUS(rv);
682 } else {
683 rv = -1;
684 }
685
686 switch (rv) {
687 case 51: /* STUDIO */
688 test_debugf(t, "Found Studio C");
689 c89flags = "-Xc -errwarn=%all -v -xc99=%none " MFLAG;
690 c99flags = "-Xc -errwarn=%all -v -xc99=%all " MFLAG;
691 if (extra_debug) {
692 test_debugf(t, "c89flags: %s", c89flags);
693 test_debugf(t, "c99flags: %s", c99flags);
694 }
695 test_passed(t);
696 break;
697 case 52: /* GCC */
698 test_debugf(t, "Found GNU C");
699 c89flags = "-Wall -Werror -std=c89 " MFLAG;
700 c99flags = "-Wall -Werror -std=c99 " MFLAG;
701 if (extra_debug) {
702 test_debugf(t, "c89flags: %s", c89flags);
703 test_debugf(t, "c99flags: %s", c99flags);
704 }
705 test_passed(t);
706 break;
707 default:
708 continue;
709 }
710 (void) asprintf(&compiler,
711 "%s%s", puname[i%2], compilers[i/2]);
712 test_debugf(t, "compiler: %s", compiler);
713 return;
714 }
715 test_failed(t, "No compiler found.");
716 }
717
718 int
719 do_compile(test_t t, struct sym_test *st, struct compile_env *cenv, int need)
720 {
721 char *cmd;
722 FILE *logf;
723 FILE *dotc;
724 const char *prog;
725
726 full_count++;
727
728 if ((dotc = fopen(cfile, "w+")) == NULL) {
729 test_failed(t, "fopen(%s): %s", cfile, strerror(errno));
730 return (-1);
731 }
732 prog = sym_test_prog(st);
733 if (fwrite(prog, 1, strlen(prog), dotc) < strlen(prog)) {
734 test_failed(t, "fwrite: %s", strerror(errno));
735 (void) fclose(dotc);
736 return (-1);
737 }
738 if (fclose(dotc) < 0) {
739 test_failed(t, "fclose: %s", strerror(errno));
740 return (-1);
741 }
742
743 (void) unlink(ofile);
744
745 if (asprintf(&cmd, "%s %s %s -c %s -o %s >>%s 2>&1",
746 compiler, strcmp(env_lang(cenv), "c99") == 0 ? c99flags : c89flags,
747 env_defs(cenv), cfile, ofile, lfile) < 0) {
748 test_failed(t, "asprintf: %s", strerror(errno));
749 return (-1);
750 }
751
752 if (extra_debug) {
753 test_debugf(t, "command: %s", cmd);
754 }
755
756
757 if ((logf = fopen(lfile, "w+")) == NULL) {
758 test_failed(t, "fopen: %s", strerror(errno));
759 return (-1);
760 }
761 (void) fprintf(logf, "===================\n");
762 (void) fprintf(logf, "PROGRAM:\n%s\n", sym_test_prog(st));
763 (void) fprintf(logf, "COMMAND: %s\n", cmd);
764 (void) fprintf(logf, "EXPECT: %s\n", need ? "OK" : "FAIL");
765 (void) fclose(logf);
766
767 if (system(cmd) != 0) {
768 if (need) {
769 fail_count++;
770 show_file(t, lfile);
771 test_failed(t, "error compiling in %s", env_name(cenv));
772 return (-1);
773 }
774 } else {
775 if (!need) {
776 fail_count++;
777 show_file(t, lfile);
778 test_failed(t, "symbol visible in %s", env_name(cenv));
779 return (-1);
780 }
781 }
782 good_count++;
783 return (0);
784 }
785
786 void
787 test_compile(void)
788 {
789 struct sym_test *st;
790 struct compile_env *cenv;
791 test_t t;
792 int need;
793
794 for (st = next_sym_test(NULL); st; st = next_sym_test(st)) {
795 if ((sym != NULL) && strcmp(sym, sym_test_name(st))) {
796 continue;
797 }
798 /* XXX: we really want a sym_test_desc() */
799 for (cenv = sym_test_env(st, NULL, &need);
800 cenv != NULL;
843 (void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]);
844 exit(1);
845 }
846 }
847
848 if (test_load_config(NULL, compilation,
849 "env", do_env, "env_group", do_env_group, NULL) < 0) {
850 exit(1);
851 }
852
853 while (optind < argc) {
854 if (test_load_config(NULL, argv[optind++],
855 "type", do_type,
856 "value", do_value,
857 "func", do_func,
858 NULL) < 0) {
859 exit(1);
860 }
861 }
862
863 (void) atexit(cleanup);
864
865 if (mkworkdir() < 0) {
866 perror("mkdir");
867 exit(1);
868 }
869
870 find_compiler();
871 if (!optC)
872 test_compile();
873
874 exit(0);
875 }
|
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 */
15
16 /*
17 * This program tests symbol visibility using the /usr/bin/c89 and
18 * /usr/bin/c99 programs.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <err.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <note.h>
30 #include <sys/wait.h>
31 #include "test_common.h"
32
33 char *dname;
34 char *cfile;
35 char *ofile;
36 char *lfile;
37 char *efile;
38
39 const char *sym = NULL;
40
41 static int good_count = 0;
42 static int fail_count = 0;
43 static int full_count = 0;
44 static int extra_debug = 0;
45 static char *compilation = "compilation.cfg";
46
47 #if defined(_LP64)
48 #define MFLAG "-m64"
49 #elif defined(_ILP32)
50 #define MFLAG "-m32"
51 #endif
52
53 const char *compilers[] = {
54 "cc",
55 "gcc",
56 "/opt/SUNWspro/bin/cc",
57 "/opt/gcc/4.4.4/bin/gcc",
58 "/opt/sunstudio12.1/bin/cc",
59 "/opt/sfw/bin/gcc",
60 "/usr/local/bin/gcc",
61 NULL
62 };
63
64 char *compiler = NULL;
65 const char *c89flags = NULL;
66 const char *c99flags = NULL;
67
68 #define MAXENV 64 /* maximum number of environments (bitmask width) */
69 #define MAXHDR 10 /* maximum # headers to require to access symbol */
70 #define MAXARG 20 /* maximum # of arguments */
71
72 #define WS " \t"
73
74 static int next_env = 0;
75
76 struct compile_env {
77 char *name;
78 char *lang;
79 char *defs;
80 int index;
81 };
82
83 static struct compile_env compile_env[MAXENV];
84
85 struct env_group {
86 char *name;
87 uint64_t mask;
88 struct env_group *next;
90
91 typedef enum { SYM_TYPE, SYM_VALUE, SYM_FUNC } sym_type_t;
92
93 struct sym_test {
94 char *name;
95 sym_type_t type;
96 char *hdrs[MAXHDR];
97 char *rtype;
98 char *atypes[MAXARG];
99 uint64_t test_mask;
100 uint64_t need_mask;
101 char *prog;
102 struct sym_test *next;
103 };
104
105 struct env_group *env_groups = NULL;
106
107 struct sym_test *sym_tests = NULL;
108 struct sym_test **sym_insert = &sym_tests;
109
110 static char *
111 mystrdup(const char *s)
112 {
113 char *r;
114 if ((r = strdup(s)) == NULL) {
115 perror("strdup");
116 exit(1);
117 }
118 return (r);
119 }
120
121 static void *
122 myzalloc(size_t sz)
123 {
124 void *buf;
125 if ((buf = calloc(1, sz)) == NULL) {
126 perror("calloc");
127 exit(1);
128 }
129 return (buf);
130 }
131
132 static void
133 myasprintf(char **buf, const char *fmt, ...)
134 {
135 int rv;
136 va_list va;
137 va_start(va, fmt);
138 rv = vasprintf(buf, fmt, va);
139 va_end(va);
140 if (rv < 0) {
141 perror("vasprintf");
142 exit(1);
143 }
144 }
145
146 static void
147 append_sym_test(struct sym_test *st)
148 {
149 *sym_insert = st;
150 sym_insert = &st->next;
151 }
152
153 static int
154 find_env_mask(const char *name, uint64_t *mask)
155 {
156 for (int i = 0; i < 64; i++) {
157 if (compile_env[i].name != NULL &&
158 strcmp(compile_env[i].name, name) == 0) {
159 *mask |= (1ULL << i);
160 return (0);
161 }
162 }
163
164 for (struct env_group *eg = env_groups; eg != NULL; eg = eg->next) {
165 if (strcmp(name, eg->name) == 0) {
166 *mask |= eg->mask;
215 return (-1);
216 }
217 *test |= mask;
218 if (act) {
219 *need |= mask;
220 } else {
221 *need &= ~(mask);
222 }
223 }
224 return (0);
225 }
226
227 static int
228 do_env(char **fields, int nfields, char **err)
229 {
230 char *name;
231 char *lang;
232 char *defs;
233
234 if (nfields != 3) {
235 myasprintf(err, "number of fields (%d) != 3", nfields);
236 return (-1);
237 }
238
239 if (next_env >= MAXENV) {
240 myasprintf(err, "too many environments");
241 return (-1);
242 }
243
244 name = fields[0];
245 lang = fields[1];
246 defs = fields[2];
247
248 compile_env[next_env].name = mystrdup(name);
249 compile_env[next_env].lang = mystrdup(lang);
250 compile_env[next_env].defs = mystrdup(defs);
251 compile_env[next_env].index = next_env;
252 next_env++;
253 return (0);
254 }
255
256 static int
257 do_env_group(char **fields, int nfields, char **err)
258 {
259 char *name;
260 char *list;
261 struct env_group *eg;
262 uint64_t mask;
263 char *item;
264
265 if (nfields != 2) {
266 myasprintf(err, "number of fields (%d) != 2", nfields);
267 return (-1);
268 }
269
270 name = fields[0];
271 list = fields[1];
272 mask = 0;
273
274 if (expand_env(list, &mask, &item) < 0) {
275 myasprintf(err, "reference to undefined env %s", item);
276 return (-1);
277 }
278
279 eg = myzalloc(sizeof (*eg));
280 eg->name = mystrdup(name);
281 eg->mask = mask;
282 eg->next = env_groups;
283 env_groups = eg;
284 return (0);
285 }
286
287 static char *progbuf = NULL;
288 size_t proglen = 0;
289 size_t progsiz = 0;
290
291 static void
292 addprogch(char c)
293 {
294 while (progsiz <= (proglen + 1)) {
295 progbuf = realloc(progbuf, progsiz + 4096);
296 if (progbuf == NULL) {
297 perror("realloc");
298 exit(1);
299 }
300 progsiz += 1024;
301 }
302 progbuf[proglen++] = c;
303 progbuf[proglen] = 0;
304 }
305
306 static void
307 addprogstr(char *s)
308 {
309 while (*s != NULL) {
310 addprogch(*s);
311 s++;
312 }
313 }
314
315 static void
316 addprogfmt(const char *fmt, ...)
317 {
318 va_list va;
319 char *buf = NULL;
320 va_start(va, fmt);
321 if (vasprintf(&buf, fmt, va) < 0) {
322 perror("vasprintf");
323 exit(1);
324 }
325 va_end(va);
326 addprogstr(buf);
327 free(buf);
328 }
329
330 static void
331 mkprog(struct sym_test *st)
332 {
333 char *s;
334
335 proglen = 0;
336
337 for (int i = 0; i < MAXHDR && st->hdrs[i] != NULL; i++) {
338 addprogfmt("#include <%s>\n", st->hdrs[i]);
339 }
340
341 for (s = st->rtype; *s; s++) {
342 addprogch(*s);
343 if (*s == '(') {
344 s++;
345 addprogch(*s);
346 s++;
347 break;
348 }
349 }
350 addprogch(' ');
351
352 /* for function pointers, s is closing suffix, otherwise empty */
353
354 switch (st->type) {
355 case SYM_TYPE:
356 addprogstr("test_type;");
357 break;
358
359 case SYM_VALUE:
360 addprogfmt("test_value%s;\n", s); /* s usually empty */
361 addprogstr("void\ntest_func(void)\n{\n");
362 addprogfmt("\ttest_value = %s;\n}", st->name);
363 break;
364
365 case SYM_FUNC:
366 addprogstr("\ntest_func(");
367 for (int i = 0; st->atypes[i] != NULL && i < MAXARG; i++) {
368 int didname = 0;
369 if (i > 0) {
370 addprogstr(", ");
371 }
372 if (strcmp(st->atypes[i], "void") == 0) {
373 didname = 1;
374 }
375 if (strcmp(st->atypes[i], "") == 0) {
376 didname = 1;
377 addprogstr("void");
378 }
379
380 /* print the argument list */
381 for (char *a = st->atypes[i]; *a; a++) {
382 if (*a == '(' && a[1] == '*' && !didname) {
383 addprogfmt("(*a%d", i);
384 didname = 1;
385 a++;
386 } else if (*a == '[' && !didname) {
387 addprogfmt("a%d[", i);
388 didname = 1;
389 } else {
390 addprogch(*a);
391 }
392 }
393 if (!didname) {
394 addprogfmt(" a%d", i);
395 }
396 }
397
398 if (st->atypes[0] == NULL) {
399 addprogstr("void");
400 }
401
402 /* close argument list, and closing ")" for func ptrs */
403 addprogfmt(")%s\n{\n\t", s); /* NB: s is normally empty */
404
405 if (strcmp(st->rtype, "") != 0 &&
406 strcmp(st->rtype, "void") != 0) {
407 addprogstr("return ");
408 }
409
410 /* add the function call */
411 addprogfmt("%s(", st->name);
412 for (int i = 0; st->atypes[i] != NULL && i < MAXARG; i++) {
413 if (strcmp(st->atypes[i], "") != 0 &&
414 strcmp(st->atypes[i], "void") != 0) {
415 addprogfmt("%sa%d", i > 0 ? ", " : "", i);
416 }
417 }
418
419 addprogstr(");\n}");
420 break;
421 }
422
423 addprogch('\n');
424
425 st->prog = progbuf;
426 }
427
428 static int
429 add_envs(struct sym_test *st, char *envs, char **err)
430 {
431 char *item;
432 if (expand_env_list(envs, &st->test_mask, &st->need_mask, &item) < 0) {
433 myasprintf(err, "bad env action %s", item);
434 return (-1);
435 }
436 return (0);
437 }
438
439 static int
440 add_headers(struct sym_test *st, char *hdrs, char **err)
441 {
442 int i = 0;
443
444 for (char *h = strsep(&hdrs, ";"); h != NULL; h = strsep(&hdrs, ";")) {
445 if (i >= MAXHDR) {
446 myasprintf(err, "too many headers");
447 return (-1);
448 }
449 test_trim(&h);
450 st->hdrs[i++] = mystrdup(h);
451 }
452
453 return (0);
454 }
455
456 static int
457 add_arg_types(struct sym_test *st, char *atype, char **err)
458 {
459 int i = 0;
460 char *a;
461 for (a = strsep(&atype, ";"); a != NULL; a = strsep(&atype, ";")) {
462 if (i >= MAXARG) {
463 myasprintf(err, "too many arguments");
464 return (-1);
465 }
466 test_trim(&a);
467 st->atypes[i++] = mystrdup(a);
468 }
469
470 return (0);
471 }
472
473 static int
474 do_type(char **fields, int nfields, char **err)
475 {
476 char *decl;
477 char *hdrs;
478 char *envs;
479 struct sym_test *st;
480
481 if (nfields != 3) {
482 myasprintf(err, "number of fields (%d) != 3", nfields);
483 return (-1);
484 }
485 decl = fields[0];
486 hdrs = fields[1];
487 envs = fields[2];
488
489 st = myzalloc(sizeof (*st));
490 st->type = SYM_TYPE;
491 st->name = mystrdup(decl);
492 st->rtype = mystrdup(decl);
493
494 if ((add_envs(st, envs, err) < 0) ||
495 (add_headers(st, hdrs, err) < 0)) {
496 return (-1);
497 }
498 append_sym_test(st);
499
500 return (0);
501 }
502
503 static int
504 do_value(char **fields, int nfields, char **err)
505 {
506 char *name;
507 char *type;
508 char *hdrs;
509 char *envs;
510 struct sym_test *st;
511
512 if (nfields != 4) {
513 myasprintf(err, "number of fields (%d) != 4", nfields);
514 return (-1);
515 }
516 name = fields[0];
517 type = fields[1];
518 hdrs = fields[2];
519 envs = fields[3];
520
521 st = myzalloc(sizeof (*st));
522 st->type = SYM_VALUE;
523 st->name = mystrdup(name);
524 st->rtype = mystrdup(type);
525
526 if ((add_envs(st, envs, err) < 0) ||
527 (add_headers(st, hdrs, err) < 0)) {
528 return (-1);
529 }
530 append_sym_test(st);
531
532 return (0);
533 }
534
535 static int
536 do_func(char **fields, int nfields, char **err)
537 {
538 char *name;
539 char *rtype;
540 char *atype;
541 char *hdrs;
542 char *envs;
543 struct sym_test *st;
544
545 if (nfields != 5) {
546 myasprintf(err, "number of fields (%d) != 5", nfields);
547 return (-1);
548 }
549 name = fields[0];
550 rtype = fields[1];
551 atype = fields[2];
552 hdrs = fields[3];
553 envs = fields[4];
554
555 st = myzalloc(sizeof (*st));
556 st->type = SYM_FUNC;
557 st->name = mystrdup(name);
558 st->rtype = mystrdup(rtype);
559
560 if ((add_envs(st, envs, err) < 0) ||
561 (add_headers(st, hdrs, err) < 0) ||
562 (add_arg_types(st, atype, err) < 0)) {
563 return (-1);
564 }
565 append_sym_test(st);
566
567 return (0);
568 }
569
570 struct sym_test *
571 next_sym_test(struct sym_test *st)
572 {
573 return (st == NULL ? sym_tests : st->next);
574 }
575
576 const char *
577 sym_test_prog(struct sym_test *st)
578 {
579 if (st->prog == NULL) {
580 mkprog(st);
581 }
582 return (st->prog);
583 }
584
585 const char *
586 sym_test_name(struct sym_test *st)
587 {
588 return (st->name);
589 }
590
591 /*
592 * Iterate through tests. Pass in NULL for cenv to begin the iteration. For
593 * subsequent iterations, use the return value from the previous iteration.
594 * Returns NULL when there are no more environments.
595 */
596 struct compile_env *
597 sym_test_env(struct sym_test *st, struct compile_env *cenv, int *need)
598 {
599 int i = cenv ? cenv->index + 1: 0;
600 uint64_t b = 1ULL << i;
601
602 while ((i < MAXENV) && (b != 0)) {
603 cenv = &compile_env[i];
604 if (b & st->test_mask) {
605 *need = (st->need_mask & b) ? 1 : 0;
606 return (cenv);
607 }
608 b <<= 1;
609 i++;
610 }
611 return (NULL);
612 }
613
614 const char *
621 env_lang(struct compile_env *cenv)
622 {
623 return (cenv->lang);
624 }
625
626 const char *
627 env_defs(struct compile_env *cenv)
628 {
629 return (cenv->defs);
630 }
631
632 static void
633 show_file(test_t t, const char *path)
634 {
635 FILE *f;
636 char *buf = NULL;
637 size_t cap = 0;
638 int line = 1;
639
640 f = fopen(path, "r");
641 if (f == NULL) {
642 test_debugf(t, "fopen(%s): %s", path, strerror(errno));
643 return;
644 }
645
646 test_debugf(t, "----->> begin (%s) <<------", path);
647 while (getline(&buf, &cap, f) >= 0) {
648 (void) strtok(buf, "\r\n");
649 test_debugf(t, "%d: %s", line, buf);
650 line++;
651 }
652 test_debugf(t, "----->> end (%s) <<------", path);
653 (void) fclose(f);
654 }
655
656 static void
657 cleanup(void)
658 {
659 if (ofile != NULL) {
660 (void) unlink(ofile);
661 free(ofile);
662 ofile = NULL;
663 }
664 if (lfile != NULL) {
665 (void) unlink(lfile);
666 free(lfile);
667 lfile = NULL;
668 }
669 if (cfile != NULL) {
670 (void) unlink(cfile);
671 free(cfile);
672 cfile = NULL;
673 }
674 if (efile != NULL) {
675 (void) unlink(efile);
676 free(efile);
677 efile = NULL;
678 }
679 if (dname) {
680 (void) rmdir(dname);
681 free(dname);
682 dname = NULL;
683 }
684 }
685
686 static int
687 mkworkdir(void)
688 {
689 char b[32];
690 char *d;
691
692 cleanup();
693
694 (void) strlcpy(b, "/tmp/symbols_testXXXXXX", sizeof (b));
695 if ((d = mkdtemp(b)) == NULL) {
696 perror("mkdtemp");
697 return (-1);
698 }
699 dname = mystrdup(d);
700 myasprintf(&cfile, "%s/compile_test.c", d);
701 myasprintf(&ofile, "%s/compile_test.o", d);
702 myasprintf(&lfile, "%s/compile_test.log", d);
703 myasprintf(&efile, "%s/compile_test.exe", d);
704 return (0);
705 }
706
707 void
708 find_compiler(void)
709 {
710 test_t t;
711 int i;
712 FILE *cf;
713
714 t = test_start("finding compiler");
715
716 if ((cf = fopen(cfile, "w+")) == NULL) {
717 test_failed(t, "Unable to open %s for write: %s", cfile,
718 strerror(errno));
719 return;
720 }
721 (void) fprintf(cf, "#include <stdio.h>\n");
722 (void) fprintf(cf, "int main(int argc, char **argv) {\n");
723 (void) fprintf(cf, "#if defined(__SUNPRO_C)\n");
724 (void) fprintf(cf, "exit(51);\n");
725 (void) fprintf(cf, "#elif defined(__GNUC__)\n");
726 (void) fprintf(cf, "exit(52);\n");
727 (void) fprintf(cf, "#else\n");
728 (void) fprintf(cf, "exit(99)\n");
729 (void) fprintf(cf, "#endif\n}\n");
730 (void) fclose(cf);
731
732 for (i = 0; compilers[i] != NULL; i++) {
733 char cmd[256];
734 int rv;
735
736 (void) snprintf(cmd, sizeof (cmd),
737 "%s %s %s -o %s >/dev/null 2>&1",
738 compilers[i], MFLAG, cfile, efile);
739 test_debugf(t, "trying %s", cmd);
740 rv = system(cmd);
741
742 test_debugf(t, "result: %d", rv);
743
744 if ((rv < 0) || !WIFEXITED(rv) || WEXITSTATUS(rv) != 0)
745 continue;
746
747 rv = system(efile);
748 if (rv >= 0 && WIFEXITED(rv)) {
749 rv = WEXITSTATUS(rv);
750 } else {
751 rv = -1;
752 }
753
754 switch (rv) {
755 case 51: /* STUDIO */
756 test_debugf(t, "Found Studio C");
757 c89flags = "-Xc -errwarn=%all -v -xc99=%none " MFLAG;
758 c99flags = "-Xc -errwarn=%all -v -xc99=%all " MFLAG;
759 if (extra_debug) {
760 test_debugf(t, "c89flags: %s", c89flags);
761 test_debugf(t, "c99flags: %s", c99flags);
762 }
763 test_passed(t);
764 break;
765 case 52: /* GCC */
766 test_debugf(t, "Found GNU C");
767 c89flags = "-Wall -Werror -std=c89 " MFLAG;
768 c99flags = "-Wall -Werror -std=c99 " MFLAG;
769 if (extra_debug) {
770 test_debugf(t, "c89flags: %s", c89flags);
771 test_debugf(t, "c99flags: %s", c99flags);
772 }
773 test_passed(t);
774 break;
775 default:
776 continue;
777 }
778 myasprintf(&compiler, "%s", compilers[i]);
779 test_debugf(t, "compiler: %s", compiler);
780 return;
781 }
782 test_failed(t, "No compiler found.");
783 }
784
785 int
786 do_compile(test_t t, struct sym_test *st, struct compile_env *cenv, int need)
787 {
788 char *cmd;
789 FILE *logf;
790 FILE *dotc;
791 const char *prog;
792
793 full_count++;
794
795 if ((dotc = fopen(cfile, "w+")) == NULL) {
796 test_failed(t, "fopen(%s): %s", cfile, strerror(errno));
797 return (-1);
798 }
799 prog = sym_test_prog(st);
800 if (fwrite(prog, 1, strlen(prog), dotc) < strlen(prog)) {
801 test_failed(t, "fwrite: %s", strerror(errno));
802 (void) fclose(dotc);
803 return (-1);
804 }
805 if (fclose(dotc) < 0) {
806 test_failed(t, "fclose: %s", strerror(errno));
807 return (-1);
808 }
809
810 (void) unlink(ofile);
811
812 myasprintf(&cmd, "%s %s %s -c %s -o %s >>%s 2>&1",
813 compiler, strcmp(env_lang(cenv), "c99") == 0 ? c99flags : c89flags,
814 env_defs(cenv), cfile, ofile, lfile);
815
816 if (extra_debug) {
817 test_debugf(t, "command: %s", cmd);
818 }
819
820 if ((logf = fopen(lfile, "w+")) == NULL) {
821 test_failed(t, "fopen: %s", strerror(errno));
822 return (-1);
823 }
824 (void) fprintf(logf, "===================\n");
825 (void) fprintf(logf, "PROGRAM:\n%s\n", sym_test_prog(st));
826 (void) fprintf(logf, "COMMAND: %s\n", cmd);
827 (void) fprintf(logf, "EXPECT: %s\n", need ? "OK" : "FAIL");
828 (void) fclose(logf);
829
830 switch (system(cmd)) {
831 case -1:
832 test_failed(t, "error compiling in %s: %s", env_name(cenv),
833 strerror(errno));
834 return (-1);
835 case 0:
836 if (!need) {
837 fail_count++;
838 show_file(t, lfile);
839 test_failed(t, "symbol visible in %s", env_name(cenv));
840 return (-1);
841 }
842 break;
843 default:
844 if (need) {
845 fail_count++;
846 show_file(t, lfile);
847 test_failed(t, "error compiling in %s", env_name(cenv));
848 return (-1);
849 }
850 break;
851 }
852 good_count++;
853 return (0);
854 }
855
856 void
857 test_compile(void)
858 {
859 struct sym_test *st;
860 struct compile_env *cenv;
861 test_t t;
862 int need;
863
864 for (st = next_sym_test(NULL); st; st = next_sym_test(st)) {
865 if ((sym != NULL) && strcmp(sym, sym_test_name(st))) {
866 continue;
867 }
868 /* XXX: we really want a sym_test_desc() */
869 for (cenv = sym_test_env(st, NULL, &need);
870 cenv != NULL;
913 (void) fprintf(stderr, "Usage: %s [-df]\n", argv[0]);
914 exit(1);
915 }
916 }
917
918 if (test_load_config(NULL, compilation,
919 "env", do_env, "env_group", do_env_group, NULL) < 0) {
920 exit(1);
921 }
922
923 while (optind < argc) {
924 if (test_load_config(NULL, argv[optind++],
925 "type", do_type,
926 "value", do_value,
927 "func", do_func,
928 NULL) < 0) {
929 exit(1);
930 }
931 }
932
933 if (atexit(cleanup) != 0) {
934 perror("atexit");
935 exit(1);
936 }
937
938 if (mkworkdir() < 0) {
939 perror("mkdir");
940 exit(1);
941 }
942
943 find_compiler();
944 if (!optC)
945 test_compile();
946
947 exit(0);
948 }
|