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