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