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