1 /* $OpenBSD: crunchgen.c,v 1.16 2015/11/11 02:52:46 deraadt 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 46 #define CRUNCH_VERSION "1.3" 47 48 #define MAXLINELEN 16384 49 #define MAXFIELDS 2048 50 51 /* XXX - This should be runtime configurable */ 52 /* 53 * We might have more than one makefile 54 * name on any given platform. Make sure 55 * default name is last though. 56 */ 57 char *mf_name[] = { 58 "Makefile.bsd-wrapper", 59 "Makefile", 60 NULL 61 }; 62 63 /* internal representation of conf file: */ 64 65 /* simple lists of strings suffice for most parms */ 66 67 typedef struct strlst { 68 struct strlst *next; 69 char *str; 70 } strlst_t; 71 72 /* progs have structure, each field can be set with "special" or calculated */ 73 74 typedef struct prog { 75 struct prog *next; 76 char *name, *ident, *mf_name; 77 char *srcdir, *objdir; 78 strlst_t *objs, *objpaths; 79 strlst_t *links; 80 int goterror; 81 } prog_t; 82 83 strlst_t *srcdirs = NULL; 84 strlst_t *libs = NULL; 85 strlst_t *libdirs = NULL; 86 char objdir[PATH_MAX] = "obj"; 87 prog_t *progs = NULL; 88 89 char line[MAXLINELEN]; 90 91 char confname[PATH_MAX], infilename[PATH_MAX]; 92 char outmkname[PATH_MAX], outcfname[PATH_MAX]; 93 char cachename[PATH_MAX], curfilename[PATH_MAX]; 94 char topdir[PATH_MAX], execfname[PATH_MAX]; 95 int linenum = -1; 96 int goterror = 0; 97 98 extern char *__progname; 99 100 int verbose = 1, readcache = 1, elf_names, elf_mangle; /* options */ 101 int reading_cache; 102 103 void out_of_memory(void); 104 void add_string(strlst_t ** listp, char *str); 105 int is_dir(char *pathname); 106 int is_nonempty_file(char *pathname); 107 void usage(void); 108 void parse_conf_file(void); 109 void gen_outputs(void); 110 111 extern int crunchide_main(int, char *[]); 112 113 int 114 main(int argc, char *argv[]) 115 { 116 char *p; 117 int optc; 118 extern int optind; 119 extern char *optarg; 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]; 661 int fd, 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 snprintf(line, sizeof(line), "make -f %s crunchgen_objs 2>&1", tempfname); 682 if ((f = popen(line, "r")) == NULL) { 683 perror("submake pipe"); 684 goterror = 1; 685 return; 686 } 687 while (fgets(line, MAXLINELEN, f)) { 688 if (strncmp(line, "OBJS= ", 6)) { 689 if (strcmp(line, 690 "sh: warning: running as root with dot in PATH\n") == 0) 691 continue; 692 fprintf(stderr, "make error: %s", line); 693 goterror = 1; 694 continue; 695 } 696 cp = line + 6; 697 while (isspace((unsigned char)*cp)) 698 cp++; 699 while (*cp) { 700 obj = cp; 701 while (*cp && !isspace((unsigned char)*cp)) 702 cp++; 703 if (*cp) 704 *cp++ = '\0'; 705 add_string(&p->objs, obj); 706 while (isspace((unsigned char)*cp)) 707 cp++; 708 } 709 } 710 if ((rc = pclose(f)) != 0) { 711 fprintf(stderr, "make error: make returned %d\n", rc); 712 goterror = 1; 713 } 714 unlink(tempfname); 715 } 716 717 void 718 remove_error_progs(void) 719 { 720 prog_t *p1, *p2; 721 722 p1 = NULL; 723 p2 = progs; 724 while (p2 != NULL) { 725 if (!p2->goterror) 726 p1 = p2, p2 = p2->next; 727 else { 728 /* delete it from linked list */ 729 fprintf(stderr, "%s: %s: ignoring program because of errors.\n", 730 infilename, p2->name); 731 if (p1) 732 p1->next = p2->next; 733 else 734 progs = p2->next; 735 p2 = p2->next; 736 } 737 } 738 } 739 740 void 741 gen_specials_cache(void) 742 { 743 FILE *cachef; 744 prog_t *p; 745 746 if ((cachef = fopen(cachename, "w")) == NULL) { 747 perror(cachename); 748 goterror = 1; 749 return; 750 } 751 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n", 752 cachename, infilename, CRUNCH_VERSION); 753 754 for (p = progs; p != NULL; p = p->next) { 755 fprintf(cachef, "\n"); 756 if (p->srcdir) 757 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir); 758 if (p->mf_name) 759 fprintf(cachef, "special %s mf_name %s\n", p->name, p->mf_name); 760 if (p->objdir) 761 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir); 762 if (p->objs) { 763 fprintf(cachef, "special %s objs", p->name); 764 output_strlst(cachef, p->objs); 765 } 766 fprintf(cachef, "special %s objpaths", p->name); 767 output_strlst(cachef, p->objpaths); 768 } 769 fclose(cachef); 770 } 771 772 void 773 gen_output_makefile(void) 774 { 775 prog_t *p; 776 FILE *outmk; 777 778 if ((outmk = fopen(outmkname, "w")) == NULL) { 779 perror(outmkname); 780 goterror = 1; 781 return; 782 } 783 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n", 784 outmkname, infilename, CRUNCH_VERSION); 785 786 top_makefile_rules(outmk); 787 788 for (p = progs; p != NULL; p = p->next) 789 prog_makefile_rules(outmk, p); 790 791 fprintf(outmk, "\n# ========\n"); 792 fclose(outmk); 793 } 794 795 void 796 gen_output_cfile(void) 797 { 798 extern char *crunched_skel[]; 799 char **cp; 800 FILE *outcf; 801 prog_t *p; 802 strlst_t *s; 803 804 if ((outcf = fopen(outcfname, "w")) == NULL) { 805 perror(outcfname); 806 goterror = 1; 807 return; 808 } 809 fprintf(outcf, "/* %s - generated from %s by crunchgen %s */\n", 810 outcfname, infilename, CRUNCH_VERSION); 811 812 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname); 813 for (cp = crunched_skel; *cp != NULL; cp++) 814 fprintf(outcf, "%s\n", *cp); 815 816 for (p = progs; p != NULL; p = p->next) 817 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident); 818 819 fprintf(outcf, "\nstruct stub entry_points[] = {\n"); 820 for (p = progs; p != NULL; p = p->next) { 821 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 822 p->name, p->ident); 823 for (s = p->links; s != NULL; s = s->next) 824 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 825 s->str, p->ident); 826 } 827 828 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n"); 829 fprintf(outcf, "\t{ NULL, NULL }\n};\n"); 830 fclose(outcf); 831 } 832 833 char * 834 genident(char *str) 835 { 836 char *n, *s, *d; 837 838 /* 839 * generates a Makefile/C identifier from a program name, mapping '-' to 840 * '_' and ignoring all other non-identifier characters. This leads to 841 * programs named "foo.bar" and "foobar" to map to the same identifier. 842 */ 843 844 if ((n = strdup(str)) == NULL) 845 return NULL; 846 for (d = s = n; *s != '\0'; s++) { 847 if (*s == '-') 848 *d++ = '_'; 849 else if (*s == '_' || isalnum((unsigned char)*s)) 850 *d++ = *s; 851 } 852 *d = '\0'; 853 return n; 854 } 855 856 char * 857 dir_search(char *progname) 858 { 859 char path[PATH_MAX]; 860 strlst_t *dir; 861 862 for (dir = srcdirs; dir != NULL; dir = dir->next) { 863 snprintf(path, sizeof(path), "%s/%s", dir->str, progname); 864 if (is_dir(path)) 865 return dir->str; 866 } 867 return NULL; 868 } 869 870 void 871 top_makefile_rules(FILE * outmk) 872 { 873 prog_t *p; 874 strlst_t *l; 875 876 877 fprintf(outmk, ".include <bsd.own.mk>\n"); 878 fprintf(outmk, "CFLAGS+=$(NOPIE_FLAGS)\n"); 879 fprintf(outmk, "LDFLAGS+=$(NOPIE_LDFLAGS)\n"); 880 fprintf(outmk, "STRIP?=strip\n"); 881 fprintf(outmk, "LINK=$(LD) -dc -r ${LDFLAGS}\n"); 882 fprintf(outmk, "LIBS="); 883 for (l = libdirs; l != NULL; l = l->next) 884 fprintf(outmk, " -L%s", l->str); 885 output_strlst(outmk, libs); 886 887 fprintf(outmk, "CRUNCHED_OBJS="); 888 for (p = progs; p != NULL; p = p->next) 889 fprintf(outmk, " %s.lo", p->name); 890 fprintf(outmk, "\n"); 891 892 fprintf(outmk, "SUBMAKE_TARGETS="); 893 for (p = progs; p != NULL; p = p->next) 894 fprintf(outmk, " %s_make", p->ident); 895 fprintf(outmk, "\n\n"); 896 897 fprintf(outmk, "CLIB=\n"); 898 fprintf(outmk, ".if defined(SRCLIBDIR)\n"); 899 fprintf(outmk, ". for lib in ${LIBS:M-l*:S/-l//}\n"); 900 fprintf(outmk, ". if exists(${SRCLIBDIR}/lib${lib})\n"); 901 fprintf(outmk, "CLIB+=lib${lib}.a\n"); 902 fprintf(outmk, ". endif\n"); 903 fprintf(outmk, ". endfor\n"); 904 fprintf(outmk, ".endif\n\n"); 905 906 fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS) ${CLIB}\n", 907 execfname, execfname); 908 fprintf(outmk, "\t$(CC) -static -L. ${LDFLAGS} -o $@ %s.o $(CRUNCHED_OBJS) $(LIBS)\n", 909 execfname); 910 fprintf(outmk, "\t$(STRIP) %s\n", execfname); 911 912 fprintf(outmk, "\n"); 913 fprintf(outmk, ".if !empty(CLIB)\n"); 914 fprintf(outmk, ".SUFFIXES: .a .olist\n"); 915 fprintf(outmk, ".olist.a:\n"); 916 fprintf(outmk, "\t@rm -f ${.TARGET}\n"); 917 fprintf(outmk, "\t@cd ${SRCLIBDIR}/${.PREFIX} &&\t\t\t\t\\\n"); 918 fprintf(outmk, "\t${MAKE} DIST_OBJS=\"`cat ${.OBJDIR}/${.IMPSRC}`\"\t\t\\\n"); 919 fprintf(outmk, "\t DIST_LIB=${.OBJDIR}/${.TARGET} ${.OBJDIR}/${.TARGET}\n\n"); 920 921 fprintf(outmk, "%s.map: %s.o $(CRUNCHED_OBJS)\n", execfname, execfname); 922 fprintf(outmk, "\t$(CC) -static ${LDFLAGS} -o %s.o.o %s.o $(CRUNCHED_OBJS) \\\n", execfname, execfname); 923 fprintf(outmk, "\t $(LIBS) -Wl,-M | sed -e '/^Allocating/q' >${.TARGET}\n\n"); 924 925 fprintf(outmk, "${CLIB:.a=.olist}: %s.map\n", execfname); 926 fprintf(outmk, "\tsed -n -e 's!^${DESTDIR}/usr/lib/${.TARGET:R}\\.a(\\([^)]*\\.o\\)).*!\\1!p' \\\n"); 927 fprintf(outmk, "\t < %s.map | tr '\\012' ' ' >$@\n", execfname); 928 fprintf(outmk, ".endif\n\n"); 929 930 fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n"); 931 fprintf(outmk, "exe: %s\n", execfname); 932 fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n", 933 execfname); 934 fprintf(outmk, ".PHONY: all objs exe clean $(SUBMAKE_TARGETS)\n\n"); 935 } 936 937 void 938 prog_makefile_rules(FILE * outmk, prog_t * p) 939 { 940 fprintf(outmk, "\n# -------- %s\n\n", p->name); 941 942 if (p->srcdir && p->objs) { 943 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir); 944 fprintf(outmk, "%s_OBJS=", p->ident); 945 output_strlst(outmk, p->objs); 946 fprintf(outmk, "%s_make:\n", p->ident); 947 fprintf(outmk, "\tcd $(%s_SRCDIR) && exec $(MAKE) -f %s $(%s_OBJS)\n\n", 948 p->ident, p->mf_name, p->ident); 949 } else 950 fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n", 951 p->ident, p->name); 952 953 fprintf(outmk, "%s_OBJPATHS=", p->ident); 954 output_strlst(outmk, p->objpaths); 955 956 fprintf(outmk, "%s_stub.c:\n", p->name); 957 fprintf(outmk, "\t@echo \"" 958 "int _crunched_%s_stub(int argc, char **argv, char **envp)" 959 " { return main(argc, argv, envp); }\" >$@\n", 960 p->ident); 961 fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n", 962 p->name, p->name, p->ident); 963 fprintf(outmk, "\t$(LINK) -o $@ %s_stub.o $(%s_OBJPATHS)\n", 964 p->name, p->ident); 965 fprintf(outmk, "\tcrunchgen -h %s -k %s_crunched_%s_stub $@\n", 966 elf_mangle ? "-M" : "", 967 elf_names ? "" : "_", p->ident); 968 } 969 970 void 971 output_strlst(FILE * outf, strlst_t * lst) 972 { 973 for (; lst != NULL; lst = lst->next) 974 fprintf(outf, " %s", lst->str); 975 fprintf(outf, "\n"); 976 } 977 978 void 979 out_of_memory(void) 980 { 981 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum); 982 exit(1); 983 } 984 985 void 986 add_string(strlst_t ** listp, char *str) 987 { 988 strlst_t *p1, *p2; 989 990 /* add to end, but be smart about dups */ 991 992 for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next) 993 if (!strcmp(p2->str, str)) 994 return; 995 996 p2 = calloc(1, sizeof(strlst_t)); 997 if (p2) 998 p2->str = strdup(str); 999 if (!p2 || !p2->str) 1000 out_of_memory(); 1001 1002 p2->next = NULL; 1003 if (p1 == NULL) 1004 *listp = p2; 1005 else 1006 p1->next = p2; 1007 } 1008 1009 int 1010 is_dir(char *pathname) 1011 { 1012 struct stat buf; 1013 1014 if (stat(pathname, &buf) == -1) 1015 return 0; 1016 return S_ISDIR(buf.st_mode); 1017 } 1018 1019 int 1020 is_nonempty_file(char *pathname) 1021 { 1022 struct stat buf; 1023 1024 if (stat(pathname, &buf) == -1) 1025 return 0; 1026 1027 return S_ISREG(buf.st_mode) && buf.st_size > 0; 1028 } 1029