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 }