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
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.
13 * Copyright 2015 Garrett D'Amore <garrett@damore.org>
14 * Copyright 2018 Joyent, Inc.
18 * This program tests symbol visibility in different compilation environments.
27 #include <sys/types.h>
32 #include "test_common.h"
40 const char *sym
= NULL
;
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";
54 const char *compilers
[] = {
57 "/opt/SUNWspro/bin/cc",
58 "/opt/gcc/4.4.4/bin/gcc",
59 "/opt/sunstudio12.1/bin/cc",
65 char *compiler
= NULL
;
66 const char *c89flags
= NULL
;
67 const char *c99flags
= NULL
;
68 const char *c11flags
= NULL
;
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 */
76 static int next_env
= 0;
85 static struct compile_env compile_env
[MAXENV
];
90 struct env_group
*eg_next
;
103 char *st_hdrs
[MAXHDR
];
105 char *st_atypes
[MAXARG
];
107 uint64_t st_test_mask
;
108 uint64_t st_need_mask
;
110 struct sym_test
*st_next
;
113 struct env_group
*env_groups
= NULL
;
115 struct sym_test
*sym_tests
= NULL
;
116 struct sym_test
**sym_insert
= &sym_tests
;
119 mystrdup(const char *s
)
122 if ((r
= strdup(s
)) == NULL
) {
133 if ((buf
= calloc(1, sz
)) == NULL
) {
141 myasprintf(char **buf
, const char *fmt
, ...)
146 rv
= vasprintf(buf
, fmt
, va
);
155 append_sym_test(struct sym_test
*st
)
158 sym_insert
= &st
->st_next
;
162 find_env_mask(const char *name
, uint64_t *mask
)
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
);
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
;
183 expand_env(char *list
, uint64_t *mask
, char **erritem
)
186 for (item
= strtok(list
, WS
); item
!= NULL
; item
= strtok(NULL
, WS
)) {
187 if (find_env_mask(item
, mask
) < 0) {
188 if (erritem
!= NULL
) {
198 expand_env_list(char *list
, uint64_t *test
, uint64_t *need
, char **erritem
)
203 for (item
= strtok(list
, WS
); item
!= NULL
; item
= strtok(NULL
, WS
)) {
219 if (find_env_mask(item
, &mask
) < 0) {
220 if (erritem
!= NULL
) {
236 do_env(char **fields
, int nfields
, char **err
)
243 myasprintf(err
, "number of fields (%d) != 3", nfields
);
247 if (next_env
>= MAXENV
) {
248 myasprintf(err
, "too many environments");
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
;
265 do_env_group(char **fields
, int nfields
, char **err
)
269 struct env_group
*eg
;
274 myasprintf(err
, "number of fields (%d) != 2", nfields
);
282 if (expand_env(list
, &mask
, &item
) < 0) {
283 myasprintf(err
, "reference to undefined env %s", item
);
287 eg
= myzalloc(sizeof (*eg
));
288 eg
->eg_name
= mystrdup(name
);
290 eg
->eg_next
= env_groups
;
295 static custr_t
*st_custr
;
300 if (custr_appendc(st_custr
, c
) == -1) {
301 perror("custr_appendc");
309 if (custr_append(st_custr
, s
) == -1) {
310 perror("custr_append");
316 addprogfmt(const char *fmt
, ...)
320 if (custr_append_vprintf(st_custr
, fmt
, va
) == -1) {
321 perror("custr_append_vprintf");
328 mkprog(struct sym_test
*st
)
332 custr_reset(st_custr
);
334 for (int i
= 0; i
< MAXHDR
&& st
->st_hdrs
[i
] != NULL
; i
++) {
335 addprogfmt("#include <%s>\n", st
->st_hdrs
[i
]);
338 if (st
->st_rtype
!= NULL
) {
339 for (s
= st
->st_rtype
; *s
; s
++) {
351 /* for function pointers, s is closing suffix, otherwise empty */
353 switch (st
->st_type
) {
355 addprogstr("test_type;");
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
);
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",
370 addprogfmt("\n#endif\n");
374 addprogstr("\ntest_func(");
375 for (int i
= 0; st
->st_atypes
[i
] != NULL
&& i
< MAXARG
; i
++) {
380 if (strcmp(st
->st_atypes
[i
], "void") == 0) {
383 if (strcmp(st
->st_atypes
[i
], "") == 0) {
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
);
394 } else if (*a
== '[' && !didname
) {
395 addprogfmt("a%d[", i
);
402 addprogfmt(" a%d", i
);
406 if (st
->st_atypes
[0] == NULL
) {
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
416 addprogfmt(")%s\n{\n\t", s
);
418 if (strcmp(st
->st_rtype
, "") != 0 &&
419 strcmp(st
->st_rtype
, "void") != 0) {
420 addprogstr("return ");
423 /* add the function call */
424 addprogfmt("%s(", st
->st_name
);
425 for (int i
= 0; st
->st_atypes
[i
] != NULL
&& i
< MAXARG
; i
++) {
426 if (strcmp(st
->st_atypes
[i
], "") != 0 &&
427 strcmp(st
->st_atypes
[i
], "void") != 0) {
428 addprogfmt("%sa%d", i
> 0 ? ", " : "", i
);
438 st
->st_prog
= custr_cstr(st_custr
);
442 add_envs(struct sym_test
*st
, char *envs
, char **err
)
445 if (expand_env_list(envs
, &st
->st_test_mask
, &st
->st_need_mask
,
447 myasprintf(err
, "bad env action %s", item
);
454 add_headers(struct sym_test
*st
, char *hdrs
, char **err
)
458 for (char *h
= strsep(&hdrs
, ";"); h
!= NULL
; h
= strsep(&hdrs
, ";")) {
460 myasprintf(err
, "too many headers");
464 st
->st_hdrs
[i
++] = mystrdup(h
);
471 add_arg_types(struct sym_test
*st
, char *atype
, char **err
)
475 for (a
= strsep(&atype
, ";"); a
!= NULL
; a
= strsep(&atype
, ";")) {
477 myasprintf(err
, "too many arguments");
481 st
->st_atypes
[i
++] = mystrdup(a
);
488 do_type(char **fields
, int nfields
, char **err
)
496 myasprintf(err
, "number of fields (%d) != 3", nfields
);
503 st
= myzalloc(sizeof (*st
));
504 st
->st_type
= SYM_TYPE
;
505 st
->st_name
= mystrdup(decl
);
506 st
->st_rtype
= mystrdup(decl
);
508 if ((add_envs(st
, envs
, err
) < 0) ||
509 (add_headers(st
, hdrs
, err
) < 0)) {
518 do_value(char **fields
, int nfields
, char **err
)
527 myasprintf(err
, "number of fields (%d) != 4", nfields
);
535 st
= myzalloc(sizeof (*st
));
536 st
->st_type
= SYM_VALUE
;
537 st
->st_name
= mystrdup(name
);
538 st
->st_rtype
= mystrdup(type
);
540 if ((add_envs(st
, envs
, err
) < 0) ||
541 (add_headers(st
, hdrs
, err
) < 0)) {
550 do_define(char **fields
, int nfields
, char **err
)
552 char *name
, *value
, *hdrs
, *envs
;
556 myasprintf(err
, "number of fields (%d) != 4", nfields
);
565 st
= myzalloc(sizeof (*st
));
566 st
->st_type
= SYM_DEFINE
;
567 st
->st_name
= mystrdup(name
);
570 * A value to compare against is optional. trim will leave it as a null
571 * pointer if there's nothing there.
575 st
->st_defval
= mystrdup(value
);
577 if ((add_envs(st
, envs
, err
) < 0) ||
578 (add_headers(st
, hdrs
, err
) < 0)) {
588 do_func(char **fields
, int nfields
, char **err
)
598 myasprintf(err
, "number of fields (%d) != 5", nfields
);
607 st
= myzalloc(sizeof (*st
));
608 st
->st_type
= SYM_FUNC
;
609 st
->st_name
= mystrdup(name
);
610 st
->st_rtype
= mystrdup(rtype
);
612 if ((add_envs(st
, envs
, err
) < 0) ||
613 (add_headers(st
, hdrs
, err
) < 0) ||
614 (add_arg_types(st
, atype
, err
) < 0)) {
623 next_sym_test(struct sym_test
*st
)
625 return (st
== NULL
? sym_tests
: st
->st_next
);
629 sym_test_prog(struct sym_test
*st
)
631 if (st
->st_prog
== NULL
) {
634 return (st
->st_prog
);
638 sym_test_name(struct sym_test
*st
)
640 return (st
->st_name
);
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.
649 sym_test_env(struct sym_test
*st
, struct compile_env
*cenv
, int *need
)
651 int i
= cenv
? cenv
->ce_index
+ 1: 0;
652 uint64_t b
= 1ULL << i
;
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;
667 env_name(struct compile_env
*cenv
)
669 return (cenv
->ce_name
);
673 env_lang(struct compile_env
*cenv
)
675 return (cenv
->ce_lang
);
679 env_defs(struct compile_env
*cenv
)
681 return (cenv
->ce_defs
);
685 show_file(test_t t
, const char *path
)
692 f
= fopen(path
, "r");
694 test_debugf(t
, "fopen(%s): %s", path
, strerror(errno
));
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
);
704 test_debugf(t
, "----->> end (%s) <<------", path
);
712 (void) unlink(ofile
);
717 (void) unlink(lfile
);
722 (void) unlink(cfile
);
727 (void) unlink(efile
);
746 (void) strlcpy(b
, "/tmp/symbols_testXXXXXX", sizeof (b
));
747 if ((d
= mkdtemp(b
)) == NULL
) {
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
);
766 t
= test_start("finding compiler");
768 if ((cf
= fopen(cfile
, "w+")) == NULL
) {
769 test_failed(t
, "Unable to open %s for write: %s", cfile
,
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");
784 for (i
= 0; compilers
[i
] != NULL
; i
++) {
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
);
794 test_debugf(t
, "result: %d", rv
);
796 if ((rv
< 0) || !WIFEXITED(rv
) || WEXITSTATUS(rv
) != 0)
800 if (rv
>= 0 && WIFEXITED(rv
)) {
801 rv
= WEXITSTATUS(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
;
813 test_debugf(t
, "c89flags: %s", c89flags
);
814 test_debugf(t
, "c99flags: %s", c99flags
);
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
;
827 test_debugf(t
, "c89flags: %s", c89flags
);
828 test_debugf(t
, "c99flags: %s", c99flags
);
833 test_debugf(t
, "Found unknown (unsupported) compiler");
838 myasprintf(&compiler
, "%s", compilers
[i
]);
839 test_debugf(t
, "compiler: %s", compiler
);
842 test_failed(t
, "No compiler found.");
846 do_compile(test_t t
, struct sym_test
*st
, struct compile_env
*cenv
, int need
)
851 const char *prog
, *cflags
, *lang
;
855 if ((dotc
= fopen(cfile
, "w+")) == NULL
) {
856 test_failed(t
, "fopen(%s): %s", cfile
, strerror(errno
));
859 prog
= sym_test_prog(st
);
860 if (fwrite(prog
, 1, strlen(prog
), dotc
) < strlen(prog
)) {
861 test_failed(t
, "fwrite: %s", strerror(errno
));
865 if (fclose(dotc
) < 0) {
866 test_failed(t
, "fclose: %s", strerror(errno
));
870 (void) unlink(ofile
);
872 if (strcmp(env_lang(cenv
), "c99") == 0) {
875 } else if (strcmp(env_lang(cenv
), "c11") == 0) {
883 if (cflags
== NULL
) {
884 test_failed(t
, "compiler %s does not support %s", compiler
,
889 myasprintf(&cmd
, "%s %s %s -c %s -o %s >>%s 2>&1",
890 compiler
, cflags
, env_defs(cenv
), cfile
, ofile
, lfile
);
893 test_debugf(t
, "command: %s", cmd
);
896 if ((logf
= fopen(lfile
, "w+")) == NULL
) {
897 test_failed(t
, "fopen: %s", strerror(errno
));
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");
906 switch (system(cmd
)) {
908 test_failed(t
, "error compiling in %s: %s", env_name(cenv
),
915 test_failed(t
, "symbol visible in %s", env_name(cenv
));
923 test_failed(t
, "error compiling in %s", env_name(cenv
));
936 struct compile_env
*cenv
;
940 for (st
= next_sym_test(NULL
); st
; st
= next_sym_test(st
)) {
941 if ((sym
!= NULL
) && strcmp(sym
, sym_test_name(st
))) {
944 /* XXX: we really want a sym_test_desc() */
945 for (cenv
= sym_test_env(st
, NULL
, &need
);
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) {
956 if (full_count
> 0) {
962 main(int argc
, char **argv
)
967 while ((optc
= getopt(argc
, argv
, "DdfCs:c:")) != EOF
) {
980 compilation
= optarg
;
989 (void) fprintf(stderr
, "Usage: %s [-df]\n", argv
[0]);
994 if (test_load_config(NULL
, compilation
,
995 "env", do_env
, "env_group", do_env_group
, NULL
) < 0) {
999 while (optind
< argc
) {
1000 if (test_load_config(NULL
, argv
[optind
++],
1003 "define", do_define
,
1010 if (atexit(cleanup
) != 0) {
1015 if (custr_alloc(&st_custr
) == -1) {
1020 if (mkworkdir() < 0) {