Incorporate rmustacc's review feedback.
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;
104 };
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;
146 return (0);
147 }
148 }
149 return (-1);
150 }
151
152
153 static int
154 expand_env(char *list, uint64_t *mask, char **erritem)
155 {
156 char *item;
157 for (item = strtok(list, WS); item != NULL; item = strtok(NULL, WS)) {
158 if (find_env_mask(item, mask) < 0) {
159 if (erritem != NULL) {
160 *erritem = item;
161 }
162 return (-1);
163 }
164 }
165 return (0);
166 }
167
168 static int
169 expand_env_list(char *list, uint64_t *test, uint64_t *need, char **erritem)
170 {
171 uint64_t mask = 0;
172 int act;
173 char *item;
174 for (item = strtok(list, WS); item != NULL; item = strtok(NULL, WS)) {
175 switch (item[0]) {
176 case '+':
177 act = 1;
178 item++;
179 break;
180 case '-':
181 act = 0;
182 item++;
183 break;
184 default:
185 act = 1;
186 break;
187 }
188
189 mask = 0;
190 if (find_env_mask(item, &mask) < 0) {
191 if (erritem != NULL) {
192 *erritem = item;
193 }
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 *
559 env_name(struct compile_env *cenv)
560 {
561 return (cenv->name);
562 }
563
564 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;
801 cenv = sym_test_env(st, cenv, &need)) {
802 t = test_start("%s : %c%s", st->name,
803 need ? '+' : '-', env_name(cenv));
804 if (do_compile(t, st, cenv, need) == 0) {
805 test_passed(t);
806 }
807 }
808 }
809
810 if (full_count > 0) {
811 test_summary();
812 }
813 }
814
815 int
816 main(int argc, char **argv)
817 {
818 int optc;
819 int optC = 0;
820
821 while ((optc = getopt(argc, argv, "DdfCs:c:")) != EOF) {
822 switch (optc) {
823 case 'd':
824 test_set_debug();
825 break;
826 case 'f':
827 test_set_force();
828 break;
829 case 'D':
830 test_set_debug();
831 extra_debug++;
832 break;
833 case 'c':
834 compilation = optarg;
835 break;
836 case 'C':
837 optC++;
838 break;
839 case 's':
840 sym = optarg;
841 break;
842 default:
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 }
--- EOF ---