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