1 /* $OpenBSD: crunchgen.c,v 1.25 2021/11/15 15:14:24 millert Exp $ */ 2 3 /* 4 * Copyright (c) 1994 University of Maryland 5 * All Rights Reserved. 6 * 7 * Permission to use, copy, modify, distribute, and sell this software and its 8 * documentation for any purpose is hereby granted without fee, provided that 9 * the above copyright notice appear in all copies and that both that 10 * copyright notice and this permission notice appear in supporting 11 * documentation, and that the name of U.M. not be used in advertising or 12 * publicity pertaining to distribution of the software without specific, 13 * written prior permission. U.M. makes no representations about the 14 * suitability of this software for any purpose. It is provided "as is" 15 * without express or implied warranty. 16 * 17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. 19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 23 * 24 * Author: James da Silva, Systems Design and Analysis Group 25 * Computer Science Department 26 * University of Maryland at College Park 27 */ 28 /* 29 * ======================================================================== 30 * crunchgen.c 31 * 32 * Generates a Makefile and main C file for a crunched executable, 33 * from specs given in a .conf file. 34 */ 35 #include <sys/param.h> /* MACHINE */ 36 #include <sys/types.h> 37 #include <sys/stat.h> 38 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <unistd.h> 42 #include <ctype.h> 43 #include <string.h> 44 #include <limits.h> 45 #include <fcntl.h> 46 #include <libgen.h> 47 48 #define CRUNCH_VERSION "1.3" 49 50 #define MAXLINELEN 16384 51 #define MAXFIELDS 2048 52 53 /* XXX - This should be runtime configurable */ 54 /* 55 * We might have more than one makefile 56 * name on any given platform. Make sure 57 * default name is last though. 58 */ 59 char *mf_name[] = { 60 "Makefile.bsd-wrapper", 61 "Makefile", 62 NULL 63 }; 64 65 /* internal representation of conf file: */ 66 67 /* simple lists of strings suffice for most parms */ 68 69 typedef struct strlst { 70 struct strlst *next; 71 char *str; 72 } strlst_t; 73 74 /* progs have structure, each field can be set with "special" or calculated */ 75 76 typedef struct prog { 77 struct prog *next; 78 char *name, *ident, *mf_name; 79 char *srcdir, *objdir; 80 strlst_t *objs, *objpaths; 81 strlst_t *links; 82 int goterror; 83 } prog_t; 84 85 strlst_t *srcdirs = NULL; 86 strlst_t *libs = NULL; 87 strlst_t *libdirs = NULL; 88 char objdir[PATH_MAX] = "obj"; 89 prog_t *progs = NULL; 90 91 char line[MAXLINELEN]; 92 93 char confname[PATH_MAX], infilename[PATH_MAX]; 94 char outmkname[PATH_MAX], outcfname[PATH_MAX]; 95 char cachename[PATH_MAX], curfilename[PATH_MAX]; 96 char topdir[PATH_MAX], execfname[PATH_MAX]; 97 int linenum = -1; 98 int goterror = 0; 99 100 extern char *__progname; 101 102 int verbose = 1, readcache = 1, elf_names, elf_mangle; /* options */ 103 int reading_cache; 104 105 void out_of_memory(void); 106 void add_string(strlst_t ** listp, char *str); 107 int is_dir(char *pathname); 108 int is_nonempty_file(char *pathname); 109 void usage(void); 110 void parse_conf_file(void); 111 void gen_outputs(void); 112 113 extern int crunchide_main(int, char *[]); 114 115 int 116 main(int argc, char *argv[]) 117 { 118 char *p; 119 int optc; 120 121 if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1) { 122 perror("pledge"); 123 exit(1); 124 } 125 126 while ((optc = getopt(argc, argv, "hm:c:e:fqD:EL:O:M")) != -1) { 127 switch (optc) { 128 case 'h': 129 optreset = 1; 130 return (crunchide_main(argc, argv)); 131 break; 132 case 'f': 133 readcache = 0; 134 break; 135 case 'q': 136 verbose = 0; 137 break; 138 139 case 'm': 140 if (strlcpy(outmkname, optarg, sizeof(outmkname)) >= 141 sizeof(outmkname)) 142 usage(); 143 break; 144 case 'c': 145 if (strlcpy(outcfname, optarg, sizeof(outcfname)) >= 146 sizeof(outcfname)) 147 usage(); 148 break; 149 case 'e': 150 if (strlcpy(execfname, optarg, sizeof(execfname)) >= 151 sizeof(execfname)) 152 usage(); 153 break; 154 155 case 'D': 156 if (strlcpy(topdir, optarg, sizeof(topdir)) >= sizeof(topdir)) 157 usage(); 158 break; 159 case 'E': 160 elf_names = 1; 161 break; 162 case 'L': 163 if (strlen(optarg) >= PATH_MAX) 164 usage(); 165 add_string(&libdirs, optarg); 166 break; 167 case 'O': 168 if (strlcpy(objdir, optarg, sizeof(objdir)) >= 169 sizeof(objdir)) 170 usage(); 171 break; 172 case 'M': 173 elf_mangle = 1; 174 break; 175 default: 176 usage(); 177 } 178 } 179 180 argc -= optind; 181 argv += optind; 182 183 if (argc != 1) 184 usage(); 185 186 if (libdirs == NULL) 187 add_string(&libdirs, "/usr/lib"); 188 /* 189 * generate filenames 190 */ 191 192 if (strlcpy(infilename, argv[0], sizeof(infilename)) >= 193 sizeof(infilename)) 194 usage(); 195 196 /* confname = `basename infilename .conf` */ 197 198 if ((p = strrchr(infilename, '/')) != NULL) 199 strlcpy(confname, p + 1, sizeof confname); 200 else 201 strlcpy(confname, infilename, sizeof confname); 202 if ((p = strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) 203 *p = '\0'; 204 205 if (!*outmkname) 206 snprintf(outmkname, sizeof(outmkname), "%s.mk", confname); 207 if (!*outcfname) 208 snprintf(outcfname, sizeof(outcfname), "%s.c", confname); 209 if (!*execfname) 210 snprintf(execfname, sizeof(execfname), "%s", confname); 211 snprintf(cachename, sizeof(cachename), "%s.cache", confname); 212 213 parse_conf_file(); 214 gen_outputs(); 215 216 exit(goterror); 217 } 218 219 void 220 usage(void) 221 { 222 fprintf(stderr, 223 "usage: crunchgen [-EfMq] [-c c-file-name] [-D src-root] [-e exec-file-name]\n" 224 "\t[-L lib-dir] [-m makefile-name] [-O objdir-name] conf-file\n"); 225 fprintf(stderr, 226 " crunchgen -h [-M] [-f keep-list-file] [-k keep-symbol] object-file ...\n"); 227 exit(1); 228 } 229 230 void parse_one_file(char *filename); 231 void parse_line(char *line, int *fc, char **fv, int nf); 232 void add_srcdirs(int argc, char **argv); 233 void add_progs(int argc, char **argv); 234 void add_link(int argc, char **argv); 235 void add_libs(int argc, char **argv); 236 void add_libdirs(int argc, char **argv); 237 void add_special(int argc, char **argv); 238 239 prog_t *find_prog(char *str); 240 void add_prog(char *progname); 241 242 void 243 parse_conf_file(void) 244 { 245 if (!is_nonempty_file(infilename)) { 246 fprintf(stderr, "%s: fatal: input file \"%s\" not found.\n", 247 __progname, infilename); 248 exit(1); 249 } 250 parse_one_file(infilename); 251 if (readcache && is_nonempty_file(cachename)) { 252 reading_cache = 1; 253 parse_one_file(cachename); 254 } 255 } 256 257 void 258 parse_one_file(char *filename) 259 { 260 char *fieldv[MAXFIELDS]; 261 int fieldc; 262 void (*f) (int c, char **v); 263 FILE *cf; 264 265 strlcpy(curfilename, filename, sizeof curfilename); 266 267 if ((cf = fopen(curfilename, "r")) == NULL) { 268 perror(curfilename); 269 goterror = 1; 270 return; 271 } 272 linenum = 0; 273 while (fgets(line, MAXLINELEN, cf) != NULL) { 274 linenum++; 275 parse_line(line, &fieldc, fieldv, MAXFIELDS); 276 if (fieldc < 1) 277 continue; 278 if (!strcmp(fieldv[0], "srcdirs")) 279 f = add_srcdirs; 280 else if (!strcmp(fieldv[0], "progs")) 281 f = add_progs; 282 else if (!strcmp(fieldv[0], "ln")) 283 f = add_link; 284 else if (!strcmp(fieldv[0], "libs")) 285 f = add_libs; 286 else if (!strcmp(fieldv[0], "special")) 287 f = add_special; 288 else if (!strcmp(fieldv[0], "libdirs")) 289 f = add_libdirs; 290 else { 291 fprintf(stderr, "%s:%d: skipping unknown command `%s'.\n", 292 curfilename, linenum, fieldv[0]); 293 goterror = 1; 294 continue; 295 } 296 if (fieldc < 2) { 297 fprintf(stderr, 298 "%s:%d: %s command needs at least 1 " 299 "argument, skipping.\n", 300 curfilename, linenum, fieldv[0]); 301 goterror = 1; 302 continue; 303 } 304 f(fieldc, fieldv); 305 } 306 307 if (ferror(cf)) { 308 perror(curfilename); 309 goterror = 1; 310 } 311 fclose(cf); 312 } 313 314 void 315 parse_line(char *line, int *fc, char **fv, int nf) 316 { 317 char *p; 318 319 p = line; 320 *fc = 0; 321 while (1) { 322 while (isspace((unsigned char)*p)) 323 p++; 324 if (*p == '\0' || *p == '#') 325 break; 326 327 if (*fc < nf) 328 fv[(*fc)++] = p; 329 while (*p && !isspace((unsigned char)*p) && *p != '#') 330 p++; 331 if (*p == '\0' || *p == '#') 332 break; 333 *p++ = '\0'; 334 } 335 if (*p) 336 *p = '\0'; /* needed for '#' case */ 337 } 338 339 void 340 add_srcdirs(int argc, char **argv) 341 { 342 int i; 343 char tmppath[PATH_MAX]; 344 int overflow; 345 346 for (i = 1; i < argc; i++) { 347 overflow = 0; 348 if (argv[i][0] == '/' || topdir[0] == '\0') { 349 if (strlcpy(tmppath, argv[i], sizeof(tmppath)) >= 350 sizeof(tmppath)) 351 overflow = 1; 352 } else { 353 if (strlcpy(tmppath, topdir, sizeof(tmppath)) >= 354 sizeof(tmppath) || 355 strlcat(tmppath, "/", sizeof(tmppath)) >= 356 sizeof(tmppath) || 357 strlcat(tmppath, argv[i], sizeof(tmppath)) >= 358 sizeof(tmppath)) 359 overflow = 1; 360 } 361 if (overflow) { 362 goterror = 1; 363 fprintf(stderr, "%s:%d: `%.40s...' is too long, skipping it.\n", 364 curfilename, linenum, argv[i]); 365 continue; 366 } 367 if (is_dir(tmppath)) 368 add_string(&srcdirs, tmppath); 369 else { 370 fprintf(stderr, "%s:%d: `%s' is not a directory, skipping it.\n", 371 curfilename, linenum, tmppath); 372 goterror = 1; 373 } 374 } 375 } 376 377 void 378 add_libdirs(int argc, char **argv) 379 { 380 int i; 381 char tmppath[PATH_MAX]; 382 char tmppath2[PATH_MAX]; 383 int overflow; 384 385 for (i = 1; i < argc; i++) { 386 overflow = 0; 387 if (argv[i][0] == '/' || topdir[0] == '\0') { 388 if (strlcpy(tmppath, argv[i], sizeof(tmppath)) >= 389 sizeof(tmppath)) 390 overflow = 1; 391 } else { 392 if (strlcpy(tmppath, topdir, sizeof(tmppath)) >= 393 sizeof(tmppath) || 394 strlcat(tmppath, "/", sizeof(tmppath)) >= 395 sizeof(tmppath) || 396 strlcat(tmppath, argv[i], sizeof(tmppath)) >= 397 sizeof(tmppath)) 398 overflow = 1; 399 } 400 if (overflow) { 401 goterror = 1; 402 fprintf(stderr, "%s:%d: `%.40s...' is too long, skipping it.\n", 403 curfilename, linenum, argv[i]); 404 continue; 405 } 406 if (is_dir(tmppath)) { 407 snprintf(tmppath2, sizeof(tmppath2), "%s/%s", tmppath, 408 objdir); 409 if (is_dir(tmppath2)) 410 add_string(&libdirs, tmppath2); 411 else { 412 snprintf(tmppath2, sizeof(tmppath2), 413 "%s/obj.%s", tmppath, MACHINE); 414 if (is_dir(tmppath2)) 415 add_string(&libdirs, tmppath2); 416 else 417 add_string(&libdirs, tmppath); 418 } 419 } 420 else { 421 fprintf(stderr, "%s:%d: `%s' is not a directory, skipping it.\n", 422 curfilename, linenum, tmppath); 423 goterror = 1; 424 } 425 } 426 } 427 428 429 void 430 add_progs(int argc, char **argv) 431 { 432 int i; 433 434 for (i = 1; i < argc; i++) 435 add_prog(argv[i]); 436 } 437 438 void 439 add_prog(char *progname) 440 { 441 prog_t *p1, *p2; 442 443 /* add to end, but be smart about dups */ 444 445 for (p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next) 446 if (!strcmp(p2->name, progname)) 447 return; 448 449 p2 = calloc(1, sizeof(prog_t)); 450 if (p2) 451 p2->name = strdup(progname); 452 if (!p2 || !p2->name) 453 out_of_memory(); 454 455 p2->next = NULL; 456 if (p1 == NULL) 457 progs = p2; 458 else 459 p1->next = p2; 460 461 p2->ident = p2->srcdir = p2->objdir = NULL; 462 p2->links = p2->objs = NULL; 463 p2->goterror = 0; 464 } 465 466 void 467 add_link(int argc, char **argv) 468 { 469 int i; 470 prog_t *p = find_prog(argv[1]); 471 472 if (p == NULL) { 473 fprintf(stderr, 474 "%s:%d: no prog %s previously declared, skipping link.\n", 475 curfilename, linenum, argv[1]); 476 goterror = 1; 477 return; 478 } 479 for (i = 2; i < argc; i++) 480 add_string(&p->links, argv[i]); 481 } 482 483 void 484 add_libs(int argc, char **argv) 485 { 486 int i; 487 488 for (i = 1; i < argc; i++) 489 add_string(&libs, argv[i]); 490 } 491 492 void 493 add_special(int argc, char **argv) 494 { 495 int i; 496 prog_t *p = find_prog(argv[1]); 497 498 if (p == NULL) { 499 if (reading_cache) 500 return; 501 fprintf(stderr, 502 "%s:%d: no prog %s previously declared, skipping special.\n", 503 curfilename, linenum, argv[1]); 504 goterror = 1; 505 return; 506 } 507 if (!strcmp(argv[2], "ident")) { 508 if (argc != 4) 509 goto argcount; 510 if ((p->ident = strdup(argv[3])) == NULL) 511 out_of_memory(); 512 } else if (!strcmp(argv[2], "srcdir")) { 513 if (argc != 4) 514 goto argcount; 515 if ((p->srcdir = strdup(argv[3])) == NULL) 516 out_of_memory(); 517 } else if (!strcmp(argv[2], "mf_name")) { 518 if (argc != 4) 519 goto argcount; 520 if ((p->mf_name = strdup(argv[3])) == NULL) 521 out_of_memory(); 522 } else if (!strcmp(argv[2], "objdir")) { 523 if (argc != 4) 524 goto argcount; 525 if ((p->objdir = strdup(argv[3])) == NULL) 526 out_of_memory(); 527 } else if (!strcmp(argv[2], "objs")) { 528 p->objs = NULL; 529 for (i = 3; i < argc; i++) 530 add_string(&p->objs, argv[i]); 531 } else if (!strcmp(argv[2], "objpaths")) { 532 p->objpaths = NULL; 533 for (i = 3; i < argc; i++) 534 add_string(&p->objpaths, argv[i]); 535 } else { 536 fprintf(stderr, "%s:%d: bad parameter name `%s', skipping line.\n", 537 curfilename, linenum, argv[2]); 538 goterror = 1; 539 } 540 return; 541 542 argcount: 543 fprintf(stderr, 544 "%s:%d: too %s arguments, expected \"special %s %s <string>\".\n", 545 curfilename, linenum, argc < 4 ? "few" : "many", argv[1], argv[2]); 546 goterror = 1; 547 } 548 549 prog_t * 550 find_prog(char *str) 551 { 552 prog_t *p; 553 554 for (p = progs; p != NULL; p = p->next) 555 if (!strcmp(p->name, str)) 556 return p; 557 return NULL; 558 } 559 560 void remove_error_progs(void); 561 void fillin_program(prog_t * p); 562 void gen_specials_cache(void); 563 void gen_output_makefile(void); 564 void gen_output_cfile(void); 565 566 void fillin_program_objs(prog_t * p, char *path); 567 void top_makefile_rules(FILE * outmk); 568 void prog_makefile_rules(FILE * outmk, prog_t * p); 569 void output_strlst(FILE * outf, strlst_t * lst); 570 char *genident(char *str); 571 char *dir_search(char *progname); 572 573 void 574 gen_outputs(void) 575 { 576 prog_t *p; 577 578 for (p = progs; p != NULL; p = p->next) 579 fillin_program(p); 580 581 remove_error_progs(); 582 gen_specials_cache(); 583 gen_output_cfile(); 584 gen_output_makefile(); 585 } 586 587 void 588 fillin_program(prog_t * p) 589 { 590 char path[PATH_MAX]; 591 char *srcparent; 592 strlst_t *s; 593 int i; 594 595 if (!p->ident) 596 p->ident = genident(p->name); 597 if (!p->srcdir) { 598 srcparent = dir_search(p->name); 599 if (srcparent) 600 snprintf(path, sizeof(path), "%s/%s", srcparent, p->name); 601 if (is_dir(path)) 602 p->srcdir = strdup(path); 603 } 604 if (!p->objdir && p->srcdir) { 605 snprintf(path, sizeof(path), "%s/%s", p->srcdir, objdir); 606 if (is_dir(path)) 607 p->objdir = strdup(path); 608 else { 609 snprintf(path, sizeof(path), "%s/obj.%s", p->srcdir, MACHINE); 610 if (is_dir(path)) 611 p->objdir = strdup(path); 612 else 613 p->objdir = p->srcdir; 614 } 615 /* Fill p->mf_name so it is not a null pointer */ 616 for (i = 0; mf_name[i] != NULL; i++) { 617 snprintf(path, sizeof(path), "%s/%s", p->srcdir, mf_name[i]); 618 if (is_nonempty_file(path)) { 619 p->mf_name = mf_name[i]; 620 break; 621 } 622 } 623 } 624 /* We have a sourcedir and no explicit objs, try */ 625 /* to find makefile and get objs from it. */ 626 if (p->srcdir && !p->objs) { 627 for (i = 0; mf_name[i] != NULL; i++) { 628 snprintf(path, sizeof(path), "%s/%s", p->srcdir, mf_name[i]); 629 if (is_nonempty_file(path)) { 630 p->mf_name = mf_name[i]; 631 fillin_program_objs(p, path); 632 break; 633 } 634 } 635 } 636 if (!p->objpaths && p->objdir && p->objs) 637 for (s = p->objs; s != NULL; s = s->next) { 638 snprintf(line, sizeof(line), "%s/%s", p->objdir, s->str); 639 add_string(&p->objpaths, line); 640 } 641 642 if (!p->srcdir && verbose) 643 fprintf(stderr, "%s: %s: warning: could not find source directory.\n", 644 infilename, p->name); 645 if (!p->objs && verbose) 646 fprintf(stderr, "%s: %s: warning: could not find any .o files.\n", 647 infilename, p->name); 648 649 if (!p->objpaths) { 650 fprintf(stderr, 651 "%s: %s: error: no objpaths specified or calculated.\n", 652 infilename, p->name); 653 p->goterror = goterror = 1; 654 } 655 } 656 657 void 658 fillin_program_objs(prog_t * p, char *path) 659 { 660 char *cp, *obj, tempfname[PATH_MAX], cwd[PATH_MAX]; 661 int fd, dotfd, rc; 662 FILE *f; 663 664 /* discover the objs from the srcdir Makefile */ 665 666 snprintf(tempfname, sizeof(tempfname), ".tmp_%sXXXXXXXXXX", confname); 667 if ((fd = mkstemp(tempfname)) == -1 || (f = fdopen(fd, "w")) == NULL) { 668 if (fd != -1) 669 close(fd); 670 perror(tempfname); 671 goterror = 1; 672 return; 673 } 674 fprintf(f, ".include \"%s\"\n", path); 675 fprintf(f, ".if defined(PROG) && !defined(OBJS)\n"); 676 fprintf(f, "OBJS=${PROG}.o\n"); 677 fprintf(f, ".endif\n"); 678 fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n"); 679 fclose(f); 680 681 if ((dotfd = open(".", O_RDONLY)) == -1 || 682 getcwd(cwd, sizeof(cwd)) == NULL) { 683 perror("get cwd"); 684 goterror = 1; 685 return; 686 } 687 if (chdir(dirname(path)) == -1) { 688 perror("chdir target dir"); 689 goterror = 1; 690 return; 691 } 692 snprintf(line, sizeof(line), "make -f %s/%s crunchgen_objs 2>&1", cwd, tempfname); 693 if ((f = popen(line, "r")) == NULL) { 694 perror("submake pipe"); 695 goterror = 1; 696 return; 697 } 698 if (fchdir(dotfd) == -1) { 699 perror("fchdir back"); 700 goterror = 1; 701 return; 702 } 703 while (fgets(line, MAXLINELEN, f)) { 704 if (strncmp(line, "OBJS= ", 6)) { 705 if (strcmp(line, 706 "sh: warning: running as root with dot in PATH\n") == 0) 707 continue; 708 fprintf(stderr, "make error: %s", line); 709 goterror = 1; 710 continue; 711 } 712 cp = line + 6; 713 while (isspace((unsigned char)*cp)) 714 cp++; 715 while (*cp) { 716 obj = cp; 717 while (*cp && !isspace((unsigned char)*cp)) 718 cp++; 719 if (*cp) 720 *cp++ = '\0'; 721 add_string(&p->objs, obj); 722 while (isspace((unsigned char)*cp)) 723 cp++; 724 } 725 } 726 if ((rc = pclose(f)) != 0) { 727 fprintf(stderr, "make error: make returned %d\n", rc); 728 goterror = 1; 729 } 730 unlink(tempfname); 731 } 732 733 void 734 remove_error_progs(void) 735 { 736 prog_t *p1, *p2; 737 738 p1 = NULL; 739 p2 = progs; 740 while (p2 != NULL) { 741 if (!p2->goterror) 742 p1 = p2, p2 = p2->next; 743 else { 744 /* delete it from linked list */ 745 fprintf(stderr, "%s: %s: ignoring program because of errors.\n", 746 infilename, p2->name); 747 if (p1) 748 p1->next = p2->next; 749 else 750 progs = p2->next; 751 p2 = p2->next; 752 } 753 } 754 } 755 756 void 757 gen_specials_cache(void) 758 { 759 FILE *cachef; 760 prog_t *p; 761 762 if ((cachef = fopen(cachename, "w")) == NULL) { 763 perror(cachename); 764 goterror = 1; 765 return; 766 } 767 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n", 768 cachename, infilename, CRUNCH_VERSION); 769 770 for (p = progs; p != NULL; p = p->next) { 771 fprintf(cachef, "\n"); 772 if (p->srcdir) 773 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir); 774 if (p->mf_name) 775 fprintf(cachef, "special %s mf_name %s\n", p->name, p->mf_name); 776 if (p->objdir) 777 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir); 778 if (p->objs) { 779 fprintf(cachef, "special %s objs", p->name); 780 output_strlst(cachef, p->objs); 781 } 782 fprintf(cachef, "special %s objpaths", p->name); 783 output_strlst(cachef, p->objpaths); 784 } 785 fclose(cachef); 786 } 787 788 void 789 gen_output_makefile(void) 790 { 791 prog_t *p; 792 FILE *outmk; 793 794 if ((outmk = fopen(outmkname, "w")) == NULL) { 795 perror(outmkname); 796 goterror = 1; 797 return; 798 } 799 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n", 800 outmkname, infilename, CRUNCH_VERSION); 801 802 top_makefile_rules(outmk); 803 804 for (p = progs; p != NULL; p = p->next) 805 prog_makefile_rules(outmk, p); 806 807 fprintf(outmk, "\n# ========\n"); 808 fclose(outmk); 809 } 810 811 void 812 gen_output_cfile(void) 813 { 814 extern char *crunched_skel[]; 815 char **cp; 816 FILE *outcf; 817 prog_t *p; 818 strlst_t *s; 819 820 if ((outcf = fopen(outcfname, "w")) == NULL) { 821 perror(outcfname); 822 goterror = 1; 823 return; 824 } 825 fprintf(outcf, "/* %s - generated from %s by crunchgen %s */\n", 826 outcfname, infilename, CRUNCH_VERSION); 827 828 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname); 829 for (cp = crunched_skel; *cp != NULL; cp++) 830 fprintf(outcf, "%s\n", *cp); 831 832 for (p = progs; p != NULL; p = p->next) 833 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident); 834 835 fprintf(outcf, "\nstruct stub entry_points[] = {\n"); 836 for (p = progs; p != NULL; p = p->next) { 837 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 838 p->name, p->ident); 839 for (s = p->links; s != NULL; s = s->next) 840 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 841 s->str, p->ident); 842 } 843 844 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n"); 845 fprintf(outcf, "\t{ NULL, NULL }\n};\n"); 846 fclose(outcf); 847 } 848 849 char * 850 genident(char *str) 851 { 852 char *n, *s, *d; 853 854 /* 855 * generates a Makefile/C identifier from a program name, mapping '-' to 856 * '_' and ignoring all other non-identifier characters. This leads to 857 * programs named "foo.bar" and "foobar" to map to the same identifier. 858 */ 859 860 if ((n = strdup(str)) == NULL) 861 return NULL; 862 for (d = s = n; *s != '\0'; s++) { 863 if (*s == '-') 864 *d++ = '_'; 865 else if (*s == '_' || isalnum((unsigned char)*s)) 866 *d++ = *s; 867 } 868 *d = '\0'; 869 return n; 870 } 871 872 char * 873 dir_search(char *progname) 874 { 875 char path[PATH_MAX]; 876 strlst_t *dir; 877 878 for (dir = srcdirs; dir != NULL; dir = dir->next) { 879 snprintf(path, sizeof(path), "%s/%s", dir->str, progname); 880 if (is_dir(path)) 881 return dir->str; 882 } 883 return NULL; 884 } 885 886 void 887 top_makefile_rules(FILE * outmk) 888 { 889 prog_t *p; 890 strlst_t *l; 891 892 893 fprintf(outmk, ".include <bsd.own.mk>\n"); 894 fprintf(outmk, "CFLAGS+=$(NOPIE_FLAGS)\n"); 895 fprintf(outmk, "CFLAGS+=-Oz\n"); 896 fprintf(outmk, "CFLAGS+=-fno-stack-protector\n"); 897 fprintf(outmk, "CFLAGS+=-fno-unwind-tables\n"); 898 fprintf(outmk, "CFLAGS+=-fno-asynchronous-unwind-tables\n"); 899 fprintf(outmk, "LDFLAGS+=$(NOPIE_LDFLAGS)\n"); 900 fprintf(outmk, "STRIP?=strip\n"); 901 fprintf(outmk, "LINK=$(LD) -dc -r ${LDFLAGS}\n"); 902 fprintf(outmk, "LIBS="); 903 for (l = libdirs; l != NULL; l = l->next) 904 fprintf(outmk, " -L%s", l->str); 905 output_strlst(outmk, libs); 906 907 fprintf(outmk, "CRUNCHED_OBJS="); 908 for (p = progs; p != NULL; p = p->next) 909 fprintf(outmk, " %s.lo", p->name); 910 fprintf(outmk, "\n"); 911 912 fprintf(outmk, "SUBMAKE_TARGETS="); 913 for (p = progs; p != NULL; p = p->next) 914 fprintf(outmk, " %s_make", p->ident); 915 fprintf(outmk, "\n\n"); 916 917 fprintf(outmk, "CLIB=\n"); 918 fprintf(outmk, ".if defined(SRCLIBDIR)\n"); 919 fprintf(outmk, ". for lib in ${LIBS:M-l*:S/-l//}\n"); 920 fprintf(outmk, ". if exists(${SRCLIBDIR}/lib${lib})\n"); 921 fprintf(outmk, "CLIB+=lib${lib}.a\n"); 922 fprintf(outmk, ". endif\n"); 923 fprintf(outmk, ". endfor\n"); 924 fprintf(outmk, ".endif\n\n"); 925 926 fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS) ${CLIB}\n", 927 execfname, execfname); 928 fprintf(outmk, "\t$(CC) -static -L. ${LDFLAGS} -o $@ %s.o $(CRUNCHED_OBJS) $(LIBS)\n", 929 execfname); 930 fprintf(outmk, "\t$(STRIP) -R .comment %s\n", execfname); 931 932 fprintf(outmk, "\n"); 933 fprintf(outmk, ".if !empty(CLIB)\n"); 934 fprintf(outmk, ".SUFFIXES: .a .olist\n"); 935 fprintf(outmk, ".olist.a:\n"); 936 fprintf(outmk, "\t@rm -f ${.TARGET}\n"); 937 fprintf(outmk, "\t@cd ${SRCLIBDIR}/${.PREFIX} &&\t\t\t\t\\\n"); 938 fprintf(outmk, "\t${MAKE} DIST_CFLAGS=\"${CFLAGS}\"\t\t\t\t\\\n"); 939 fprintf(outmk, "\t DIST_OBJS=\"`cat ${.OBJDIR}/${.IMPSRC}`\"\t\t\\\n"); 940 fprintf(outmk, "\t DIST_LIB=${.OBJDIR}/${.TARGET} ${.OBJDIR}/${.TARGET}\n\n"); 941 942 fprintf(outmk, "%s.map: %s.o $(CRUNCHED_OBJS)\n", execfname, execfname); 943 fprintf(outmk, "\t$(CC) -static ${LDFLAGS} -o %s.o.o %s.o $(CRUNCHED_OBJS) \\\n", execfname, execfname); 944 fprintf(outmk, "\t $(LIBS) -Wl,--trace | sed -e 's/^(\\(..*\\))\\(..*\\)/\\1(\\2)/' >${.TARGET}\n\n"); 945 946 fprintf(outmk, "${CLIB:.a=.olist}: %s.map\n", execfname); 947 fprintf(outmk, "\tsed -n -e 's!^${DESTDIR}/usr/lib/${.TARGET:R}\\.a(\\([^)]*\\.o\\)).*!\\1!p' \\\n"); 948 fprintf(outmk, "\t < %s.map | tr '\\012' ' ' >$@\n", execfname); 949 fprintf(outmk, ".endif\n\n"); 950 951 fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n"); 952 fprintf(outmk, "exe: %s\n", execfname); 953 fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n", 954 execfname); 955 fprintf(outmk, ".PHONY: all objs exe clean $(SUBMAKE_TARGETS)\n\n"); 956 } 957 958 void 959 prog_makefile_rules(FILE * outmk, prog_t * p) 960 { 961 fprintf(outmk, "\n# -------- %s\n\n", p->name); 962 963 if (p->srcdir && p->objs) { 964 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir); 965 fprintf(outmk, "%s_OBJS=", p->ident); 966 output_strlst(outmk, p->objs); 967 fprintf(outmk, "%s_make:\n", p->ident); 968 fprintf(outmk, "\tcd $(%s_SRCDIR) && exec $(MAKE) -f %s $(%s_OBJS)\n\n", 969 p->ident, p->mf_name, p->ident); 970 } else 971 fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n", 972 p->ident, p->name); 973 974 fprintf(outmk, "%s_OBJPATHS=", p->ident); 975 output_strlst(outmk, p->objpaths); 976 977 fprintf(outmk, "%s_stub.c:\n", p->name); 978 fprintf(outmk, "\t@echo \"" 979 "extern int main(int argc, char **argv, char **envp);\\\n" 980 "int _crunched_%s_stub(int argc, char **argv, char **envp)" 981 " { return main(argc, argv, envp); }\" >$@\n", 982 p->ident); 983 fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n", 984 p->name, p->name, p->ident); 985 fprintf(outmk, "\t$(LINK) -o $@ %s_stub.o $(%s_OBJPATHS)\n", 986 p->name, p->ident); 987 fprintf(outmk, "\tcrunchgen -h %s -k %s_crunched_%s_stub $@\n", 988 elf_mangle ? "-M" : "", 989 elf_names ? "" : "_", p->ident); 990 } 991 992 void 993 output_strlst(FILE * outf, strlst_t * lst) 994 { 995 for (; lst != NULL; lst = lst->next) 996 fprintf(outf, " %s", lst->str); 997 fprintf(outf, "\n"); 998 } 999 1000 void 1001 out_of_memory(void) 1002 { 1003 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum); 1004 exit(1); 1005 } 1006 1007 void 1008 add_string(strlst_t ** listp, char *str) 1009 { 1010 strlst_t *p1, *p2; 1011 1012 /* add to end, but be smart about dups */ 1013 1014 for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next) 1015 if (!strcmp(p2->str, str)) 1016 return; 1017 1018 p2 = calloc(1, sizeof(strlst_t)); 1019 if (p2) 1020 p2->str = strdup(str); 1021 if (!p2 || !p2->str) 1022 out_of_memory(); 1023 1024 p2->next = NULL; 1025 if (p1 == NULL) 1026 *listp = p2; 1027 else 1028 p1->next = p2; 1029 } 1030 1031 int 1032 is_dir(char *pathname) 1033 { 1034 struct stat buf; 1035 1036 if (stat(pathname, &buf) == -1) 1037 return 0; 1038 return S_ISDIR(buf.st_mode); 1039 } 1040 1041 int 1042 is_nonempty_file(char *pathname) 1043 { 1044 struct stat buf; 1045 1046 if (stat(pathname, &buf) == -1) 1047 return 0; 1048 1049 return S_ISREG(buf.st_mode) && buf.st_size > 0; 1050 } 1051