1 /* $NetBSD: crunchgen.c,v 1.87 2018/06/21 10:55:54 kamil Exp $ */ 2 /* 3 * Copyright (c) 1994 University of Maryland 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify, distribute, and sell this software and its 7 * documentation for any purpose is hereby granted without fee, provided that 8 * the above copyright notice appear in all copies and that both that 9 * copyright notice and this permission notice appear in supporting 10 * documentation, and that the name of U.M. not be used in advertising or 11 * publicity pertaining to distribution of the software without specific, 12 * written prior permission. U.M. makes no representations about the 13 * suitability of this software for any purpose. It is provided "as is" 14 * without express or implied warranty. 15 * 16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M. 18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Author: James da Silva, Systems Design and Analysis Group 24 * Computer Science Department 25 * University of Maryland at College Park 26 */ 27 /* 28 * ======================================================================== 29 * crunchgen.c 30 * 31 * Generates a Makefile and main C file for a crunched executable, 32 * from specs given in a .conf file. 33 */ 34 35 #if HAVE_NBTOOL_CONFIG_H 36 #include "nbtool_config.h" 37 #endif 38 39 #include <sys/cdefs.h> 40 #if !defined(lint) 41 __RCSID("$NetBSD: crunchgen.c,v 1.87 2018/06/21 10:55:54 kamil Exp $"); 42 #endif 43 44 #include <stdlib.h> 45 #include <unistd.h> 46 #include <stdio.h> 47 #include <ctype.h> 48 #include <string.h> 49 #include <errno.h> 50 #include <err.h> 51 #include <util.h> 52 53 #include <sys/types.h> 54 #include <sys/stat.h> 55 #include <sys/param.h> 56 #include <sys/utsname.h> 57 58 #define CRUNCH_VERSION "20180508" 59 60 #define MAXLINELEN 16384 61 #define MAXFIELDS 2048 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; 77 char *srcdir, *objdir; 78 strlst_t *objs, *objpaths; 79 strlst_t *links, *keepsymbols; 80 int goterror; 81 } prog_t; 82 83 84 /* global state */ 85 86 static strlst_t *srcdirs = NULL; 87 static strlst_t *libs = NULL; 88 static strlst_t *vars = NULL; 89 static prog_t *progs = NULL; 90 91 static char line[MAXLINELEN]; 92 93 static char confname[MAXPATHLEN], infilename[MAXPATHLEN]; 94 static char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN]; 95 static char cachename[MAXPATHLEN], curfilename[MAXPATHLEN]; 96 static char curdir[MAXPATHLEN]; 97 static char topdir[MAXPATHLEN]; 98 static char libdir[MAXPATHLEN] = "/usr/lib"; 99 static char dbg[MAXPATHLEN] = "-Os"; 100 static int linenum = -1; 101 static int goterror = 0; 102 103 static const char *pname = "crunchgen"; 104 105 static int verbose, readcache, useobjs, oneobj, pie, sanitizer; /* options */ 106 static int reading_cache; 107 static char *machine; 108 static char *makeobjdirprefix; 109 static char *makebin; 110 static char *makeflags; 111 112 /* general library routines */ 113 114 static void status(const char *str); 115 __dead static void out_of_memory(void); 116 static void add_string(strlst_t **listp, char *str); 117 static int is_dir(const char *pathname); 118 static int is_nonempty_file(const char *pathname); 119 120 /* helper routines for main() */ 121 122 __dead static void usage(void); 123 static void parse_conf_file(void); 124 static void gen_outputs(void); 125 126 extern char *crunched_skel[]; 127 128 int 129 main(int argc, char **argv) 130 { 131 char *p; 132 int optc; 133 134 if ((makebin = getenv("MAKE")) == NULL) 135 makebin = strdup("make"); 136 137 if ((makeflags = getenv("MAKEFLAGS")) == NULL) 138 makeflags = strdup(""); 139 140 if ((machine = getenv("MACHINE")) == NULL) { 141 static struct utsname utsname; 142 143 if (uname(&utsname) == -1) { 144 perror("uname"); 145 exit(1); 146 } 147 machine = utsname.machine; 148 } 149 makeobjdirprefix = getenv("MAKEOBJDIRPREFIX"); 150 verbose = 1; 151 readcache = 1; 152 useobjs = 0; 153 oneobj = 1; 154 pie = 0; 155 *outmkname = *outcfname = *execfname = '\0'; 156 157 if (argc > 0) 158 pname = argv[0]; 159 160 while ((optc = getopt(argc, argv, "m:c:d:e:fopqsD:L:Ov:")) != -1) { 161 switch(optc) { 162 case 'f': readcache = 0; break; 163 case 'p': pie = 1; break; 164 case 'q': verbose = 0; break; 165 case 'O': oneobj = 0; break; 166 case 'o': useobjs = 1, oneobj = 0; break; 167 case 's': sanitizer = 1; break; 168 169 case 'm': (void)estrlcpy(outmkname, optarg, sizeof(outmkname)); break; 170 case 'c': (void)estrlcpy(outcfname, optarg, sizeof(outcfname)); break; 171 case 'e': (void)estrlcpy(execfname, optarg, sizeof(execfname)); break; 172 case 'd': (void)estrlcpy(dbg, optarg, sizeof(dbg)); break; 173 174 case 'D': (void)estrlcpy(topdir, optarg, sizeof(topdir)); break; 175 case 'L': (void)estrlcpy(libdir, optarg, sizeof(libdir)); break; 176 case 'v': add_string(&vars, optarg); break; 177 178 case '?': 179 default: usage(); 180 } 181 } 182 183 argc -= optind; 184 argv += optind; 185 186 if (argc != 1) 187 usage(); 188 189 /* 190 * generate filenames 191 */ 192 193 (void)estrlcpy(infilename, argv[0], sizeof(infilename)); 194 getcwd(curdir, MAXPATHLEN); 195 196 /* confname = `basename infilename .conf` */ 197 198 if ((p = strrchr(infilename, '/')) != NULL) 199 (void)estrlcpy(confname, p + 1, sizeof(confname)); 200 else 201 (void)estrlcpy(confname, infilename, sizeof(confname)); 202 if ((p = strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) 203 *p = '\0'; 204 205 if (!*outmkname) 206 (void)snprintf(outmkname, sizeof(outmkname), "%s.mk", confname); 207 if (!*outcfname) 208 (void)snprintf(outcfname, sizeof(outcfname), "%s.c", confname); 209 if (!*execfname) 210 (void)snprintf(execfname, sizeof(execfname), "%s", confname); 211 212 (void)snprintf(cachename, sizeof(cachename), "%s.cache", confname); 213 214 parse_conf_file(); 215 gen_outputs(); 216 217 exit(goterror); 218 } 219 220 221 void 222 usage(void) 223 { 224 fprintf(stderr, 225 "%s [-fOoq] [-c c-file-name] [-D src-root] [-d build-options]\n" 226 "\t [-e exec-file-name] [-L lib-dir] [-m makefile-name]\n" 227 "\t [-v var-spec] conf-file\n", pname); 228 exit(1); 229 } 230 231 232 /* 233 * ======================================================================== 234 * parse_conf_file subsystem 235 * 236 */ 237 238 /* helper routines for parse_conf_file */ 239 240 static void parse_one_file(char *filename); 241 static void parse_line(char *line, int *fc, char **fv, int nf); 242 static void add_srcdirs(int argc, char **argv); 243 static void add_progs(int argc, char **argv); 244 static void add_link(int argc, char **argv); 245 static void add_libs(int argc, char **argv); 246 static void add_special(int argc, char **argv); 247 248 static prog_t *find_prog(char *str); 249 static void add_prog(char *progname); 250 251 252 static void 253 parse_conf_file(void) 254 { 255 if (!is_nonempty_file(infilename)) { 256 fprintf(stderr, "%s: fatal: input file \"%s\" not found.\n", 257 pname, infilename); 258 exit(1); 259 } 260 parse_one_file(infilename); 261 if (readcache && is_nonempty_file(cachename)) { 262 reading_cache = 1; 263 parse_one_file(cachename); 264 } 265 } 266 267 268 static void 269 parse_one_file(char *filename) 270 { 271 char *fieldv[MAXFIELDS]; 272 int fieldc; 273 void (*f)(int c, char **v); 274 FILE *cf; 275 276 (void)snprintf(line, sizeof(line), "reading %s", filename); 277 status(line); 278 (void)estrlcpy(curfilename, filename, sizeof(curfilename)); 279 280 if ((cf = fopen(curfilename, "r")) == NULL) { 281 perror(curfilename); 282 goterror = 1; 283 return; 284 } 285 286 linenum = 0; 287 while (fgets(line, MAXLINELEN, cf) != NULL) { 288 linenum++; 289 parse_line(line, &fieldc, fieldv, MAXFIELDS); 290 if (fieldc < 1) 291 continue; 292 if (!strcmp(fieldv[0], "srcdirs")) f = add_srcdirs; 293 else if (!strcmp(fieldv[0], "progs")) f = add_progs; 294 else if (!strcmp(fieldv[0], "ln")) f = add_link; 295 else if (!strcmp(fieldv[0], "libs")) f = add_libs; 296 else if (!strcmp(fieldv[0], "special")) f = add_special; 297 else { 298 fprintf(stderr, "%s:%d: skipping unknown command `%s'.\n", 299 curfilename, linenum, fieldv[0]); 300 goterror = 1; 301 continue; 302 } 303 if (fieldc < 2) { 304 fprintf(stderr, 305 "%s:%d: %s command needs at least 1 argument, skipping.\n", 306 curfilename, linenum, fieldv[0]); 307 goterror = 1; 308 continue; 309 } 310 f(fieldc, fieldv); 311 } 312 313 if (ferror(cf)) { 314 perror(curfilename); 315 goterror = 1; 316 } 317 fclose(cf); 318 } 319 320 321 static void 322 parse_line(char *pline, int *fc, char **fv, int nf) 323 { 324 char *p; 325 326 p = pline; 327 *fc = 0; 328 for (;;) { 329 while (isspace((unsigned char)*p)) 330 p++; 331 if (*p == '\0' || *p == '#') 332 break; 333 334 if (*fc < nf) 335 fv[(*fc)++] = p; 336 while (*p && !isspace((unsigned char)*p) && *p != '#') 337 p++; 338 if (*p == '\0' || *p == '#') 339 break; 340 *p++ = '\0'; 341 } 342 if (*p) 343 *p = '\0'; /* needed for '#' case */ 344 } 345 346 347 static void 348 add_srcdirs(int argc, char **argv) 349 { 350 int i; 351 char tmppath[MAXPATHLEN]; 352 353 for (i = 1; i < argc; i++) { 354 if (argv[i][0] == '/') 355 (void)estrlcpy(tmppath, argv[i], sizeof(tmppath)); 356 else { 357 if (topdir[0] == '\0') 358 (void)estrlcpy(tmppath, curdir, sizeof(tmppath)); 359 else 360 (void)estrlcpy(tmppath, topdir, sizeof(tmppath)); 361 (void)estrlcat(tmppath, "/", sizeof(tmppath)); 362 (void)estrlcat(tmppath, argv[i], sizeof(tmppath)); 363 } 364 if (is_dir(tmppath)) 365 add_string(&srcdirs, tmppath); 366 else { 367 fprintf(stderr, "%s:%d: `%s' is not a directory, skipping it.\n", 368 curfilename, linenum, tmppath); 369 goterror = 1; 370 } 371 } 372 } 373 374 375 static void 376 add_progs(int argc, char **argv) 377 { 378 int i; 379 380 for (i = 1; i < argc; i++) 381 add_prog(argv[i]); 382 } 383 384 385 static void 386 add_prog(char *progname) 387 { 388 prog_t *p1, *p2; 389 390 /* add to end, but be smart about dups */ 391 392 for (p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next) 393 if (!strcmp(p2->name, progname)) 394 return; 395 396 p2 = malloc(sizeof(prog_t)); 397 if (p2) 398 p2->name = strdup(progname); 399 if (!p2 || !p2->name) 400 out_of_memory(); 401 402 p2->next = NULL; 403 if (p1 == NULL) 404 progs = p2; 405 else 406 p1->next = p2; 407 408 p2->ident = p2->srcdir = p2->objdir = NULL; 409 p2->objs = p2->objpaths = p2->links = p2->keepsymbols = NULL; 410 p2->goterror = 0; 411 } 412 413 414 static void 415 add_link(int argc, char **argv) 416 { 417 int i; 418 prog_t *p = find_prog(argv[1]); 419 420 if (p == NULL) { 421 fprintf(stderr, 422 "%s:%d: no prog %s previously declared, skipping link.\n", 423 curfilename, linenum, argv[1]); 424 goterror = 1; 425 return; 426 } 427 for (i = 2; i < argc; i++) 428 add_string(&p->links, argv[i]); 429 } 430 431 432 static void 433 add_libs(int argc, char **argv) 434 { 435 int i; 436 437 for (i = 1; i < argc; i++) 438 add_string(&libs, argv[i]); 439 } 440 441 442 static void 443 add_special(int argc, char **argv) 444 { 445 int i; 446 prog_t *p = find_prog(argv[1]); 447 448 if (p == NULL) { 449 if (reading_cache) 450 return; 451 fprintf(stderr, 452 "%s:%d: no prog %s previously declared, skipping special.\n", 453 curfilename, linenum, argv[1]); 454 goterror = 1; 455 return; 456 } 457 458 if (!strcmp(argv[2], "ident")) { 459 if (argc != 4) 460 goto argcount; 461 if ((p->ident = strdup(argv[3])) == NULL) 462 out_of_memory(); 463 return; 464 } 465 466 if (!strcmp(argv[2], "srcdir")) { 467 if (argc != 4) 468 goto argcount; 469 if (argv[3][0] == '/') { 470 if ((p->srcdir = strdup(argv[3])) == NULL) 471 out_of_memory(); 472 } else { 473 char tmppath[MAXPATHLEN]; 474 if (topdir[0] == '\0') 475 (void)estrlcpy(tmppath, curdir, sizeof(tmppath)); 476 else 477 (void)estrlcpy(tmppath, topdir, sizeof(tmppath)); 478 (void)estrlcat(tmppath, "/", sizeof(tmppath)); 479 (void)estrlcat(tmppath, argv[3], sizeof(tmppath)); 480 if ((p->srcdir = strdup(tmppath)) == NULL) 481 out_of_memory(); 482 } 483 return; 484 } 485 486 if (!strcmp(argv[2], "objdir")) { 487 if (argc != 4) 488 goto argcount; 489 if ((p->objdir = strdup(argv[3])) == NULL) 490 out_of_memory(); 491 return; 492 } 493 494 if (!strcmp(argv[2], "objs")) { 495 oneobj = 0; 496 for (i = 3; i < argc; i++) 497 add_string(&p->objs, argv[i]); 498 return; 499 } 500 501 if (!strcmp(argv[2], "objpaths")) { 502 oneobj = 0; 503 for (i = 3; i < argc; i++) 504 add_string(&p->objpaths, argv[i]); 505 return; 506 } 507 508 if (!strcmp(argv[2], "keepsymbols")) { 509 for (i = 3; i < argc; i++) 510 add_string(&p->keepsymbols, argv[i]); 511 return; 512 } 513 514 fprintf(stderr, "%s:%d: bad parameter name `%s', skipping line.\n", 515 curfilename, linenum, argv[2]); 516 goterror = 1; 517 return; 518 519 argcount: 520 fprintf(stderr, 521 "%s:%d: too %s arguments, expected \"special %s %s <string>\".\n", 522 curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]); 523 goterror = 1; 524 } 525 526 527 static prog_t * 528 find_prog(char *str) 529 { 530 prog_t *p; 531 532 for (p = progs; p != NULL; p = p->next) 533 if (!strcmp(p->name, str)) 534 return p; 535 536 return NULL; 537 } 538 539 540 /* 541 * ======================================================================== 542 * gen_outputs subsystem 543 * 544 */ 545 546 /* helper subroutines */ 547 548 static void remove_error_progs(void); 549 static void fillin_program(prog_t *p); 550 static void gen_specials_cache(void); 551 static void gen_output_makefile(void); 552 static void gen_output_cfile(void); 553 554 static void fillin_program_objs(prog_t *p, char *path); 555 static void top_makefile_rules(FILE *outmk); 556 static void bottom_makefile_rules(FILE *outmk); 557 static void prog_makefile_rules(FILE *outmk, prog_t *p); 558 static void output_strlst(FILE *outf, strlst_t *lst); 559 static char *genident(char *str); 560 static char *dir_search(char *progname); 561 562 563 static void 564 gen_outputs(void) 565 { 566 prog_t *p; 567 568 for (p = progs; p != NULL; p = p->next) 569 fillin_program(p); 570 571 remove_error_progs(); 572 gen_specials_cache(); 573 gen_output_cfile(); 574 gen_output_makefile(); 575 status(""); 576 fprintf(stderr, 577 "Run \"make -f %s objs exe\" to build crunched binary.\n", 578 outmkname); 579 } 580 581 582 static void 583 fillin_program(prog_t *p) 584 { 585 char path[MAXPATHLEN]; 586 char *srcparent; 587 strlst_t *s; 588 589 (void)snprintf(line, sizeof(line), "filling in parms for %s", p->name); 590 status(line); 591 592 if (!p->ident) 593 p->ident = genident(p->name); 594 if (!p->srcdir) { 595 srcparent = dir_search(p->name); 596 if (srcparent) { 597 (void)snprintf(path, sizeof(path), "%s/%s", srcparent, p->name); 598 if (is_dir(path)) { 599 if (path[0] == '/') { 600 if ((p->srcdir = strdup(path)) == NULL) 601 out_of_memory(); 602 } else { 603 char tmppath[MAXPATHLEN]; 604 if (topdir[0] == '\0') 605 (void)estrlcpy(tmppath, curdir, sizeof(tmppath)); 606 else 607 (void)estrlcpy(tmppath, topdir, sizeof(tmppath)); 608 (void)estrlcat(tmppath, "/", sizeof(tmppath)); 609 (void)estrlcat(tmppath, path, sizeof(tmppath)); 610 if ((p->srcdir = strdup(tmppath)) == NULL) 611 out_of_memory(); 612 } 613 } 614 } 615 } 616 617 if (!p->srcdir && verbose) 618 fprintf(stderr, "%s: %s: warning: could not find source directory.\n", 619 infilename, p->name); 620 621 if (!p->objdir && p->srcdir && useobjs) { 622 if (makeobjdirprefix) { 623 (void)snprintf(path, sizeof(path), "%s/%s", makeobjdirprefix, p->srcdir); 624 if (is_dir(path)) 625 p->objdir = strdup(path); 626 } 627 if (!p->objdir) { 628 (void)snprintf(path, sizeof(path), "%s/obj.%s", p->srcdir, machine); 629 if (is_dir(path)) 630 p->objdir = strdup(path); 631 } 632 if (!p->objdir) { 633 (void)snprintf(path, sizeof(path), "%s/obj", p->srcdir); 634 if (is_dir(path)) 635 p->objdir = strdup(path); 636 } 637 if (!p->objdir) { 638 p->objdir = p->srcdir; 639 } 640 } 641 642 if (oneobj) 643 return; 644 645 if (p->srcdir) 646 (void)snprintf(path, sizeof(path), "%s/Makefile", p->srcdir); 647 if (!p->objs && p->srcdir && is_nonempty_file(path)) 648 fillin_program_objs(p, p->srcdir); 649 650 if (!p->objpaths && p->objs) { 651 char *objdir; 652 if (p->objdir && useobjs) 653 objdir = p->objdir; 654 else 655 objdir = p->ident; 656 for (s = p->objs; s != NULL; s = s->next) { 657 (void)snprintf(line, sizeof(line), "%s/%s", objdir, s->str); 658 add_string(&p->objpaths, line); 659 } 660 } 661 662 if (!p->objs && verbose) 663 fprintf(stderr, "%s: %s: warning: could not find any .o files.\n", 664 infilename, p->name); 665 666 if (!p->objpaths) { 667 fprintf(stderr, 668 "%s: %s: error: no objpaths specified or calculated.\n", 669 infilename, p->name); 670 p->goterror = goterror = 1; 671 } 672 } 673 674 static void 675 fillin_program_objs(prog_t *p, char *dirpath) 676 { 677 char *obj, *cp; 678 int rc; 679 int fd; 680 FILE *f; 681 char tempfname[MAXPATHLEN]; 682 683 /* discover the objs from the srcdir Makefile */ 684 685 (void)snprintf(tempfname, sizeof(tempfname), "/tmp/%sXXXXXX", confname); 686 if ((fd = mkstemp(tempfname)) < 0) { 687 perror(tempfname); 688 exit(1); 689 } 690 691 if ((f = fdopen(fd, "w")) == NULL) { 692 perror(tempfname); 693 goterror = 1; 694 return; 695 } 696 697 fprintf(f, ".include \"${.CURDIR}/Makefile\"\n"); 698 fprintf(f, ".if defined(PROG)\n"); 699 fprintf(f, "OBJS?= ${PROG}.o\n"); 700 fprintf(f, ".endif\n"); 701 fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n"); 702 fclose(f); 703 704 (void)snprintf(line, sizeof(line), 705 "cd %s && %s -B -f %s %s CRUNCHEDPROG=1 crunchgen_objs 2>&1", dirpath, 706 makebin, tempfname, makeflags); 707 if ((f = popen(line, "r")) == NULL) { 708 perror("submake pipe"); 709 goterror = 1; 710 unlink(tempfname); 711 return; 712 } 713 714 while (fgets(line, MAXLINELEN, f)) { 715 if (strncmp(line, "OBJS= ", 6)) { 716 if (strcmp(line, 717 "sh: warning: running as root with dot in PATH\n") == 0) 718 continue; 719 fprintf(stderr, "make error: %s", line); 720 goterror = 1; 721 continue; 722 } 723 cp = line + 6; 724 while (isspace((unsigned char)*cp)) 725 cp++; 726 while (*cp) { 727 obj = cp; 728 while (*cp && !isspace((unsigned char)*cp)) 729 cp++; 730 if (*cp) 731 *cp++ = '\0'; 732 add_string(&p->objs, obj); 733 while (isspace((unsigned char)*cp)) 734 cp++; 735 } 736 } 737 if ((rc=pclose(f)) != 0) { 738 fprintf(stderr, "make error: make returned %d\n", rc); 739 goterror = 1; 740 } 741 unlink(tempfname); 742 } 743 744 static void 745 remove_error_progs(void) 746 { 747 prog_t *p1, *p2; 748 749 p1 = NULL; p2 = progs; 750 while (p2 != NULL) { 751 if (!p2->goterror) 752 p1 = p2, p2 = p2->next; 753 else { 754 /* delete it from linked list */ 755 fprintf(stderr, "%s: %s: ignoring program because of errors.\n", 756 infilename, p2->name); 757 if (p1) 758 p1->next = p2->next; 759 else 760 progs = p2->next; 761 p2 = p2->next; 762 } 763 } 764 } 765 766 static void 767 gen_specials_cache(void) 768 { 769 FILE *cachef; 770 prog_t *p; 771 772 (void)snprintf(line, sizeof(line), "generating %s", cachename); 773 status(line); 774 775 if ((cachef = fopen(cachename, "w")) == NULL) { 776 perror(cachename); 777 goterror = 1; 778 return; 779 } 780 781 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n", 782 cachename, infilename, CRUNCH_VERSION); 783 784 for (p = progs; p != NULL; p = p->next) { 785 fprintf(cachef, "\n"); 786 if (p->srcdir) 787 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir); 788 if (p->objdir && useobjs) 789 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir); 790 if (p->objs) { 791 fprintf(cachef, "special %s objs", p->name); 792 output_strlst(cachef, p->objs); 793 } 794 if (p->objpaths) { 795 fprintf(cachef, "special %s objpaths", p->name); 796 output_strlst(cachef, p->objpaths); 797 } 798 } 799 fclose(cachef); 800 } 801 802 803 static void 804 gen_output_makefile(void) 805 { 806 prog_t *p; 807 FILE *outmk; 808 809 (void)snprintf(line, sizeof(line), "generating %s", outmkname); 810 status(line); 811 812 if ((outmk = fopen(outmkname, "w")) == NULL) { 813 perror(outmkname); 814 goterror = 1; 815 return; 816 } 817 818 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n", 819 outmkname, infilename, CRUNCH_VERSION); 820 821 top_makefile_rules(outmk); 822 823 for (p = progs; p != NULL; p = p->next) 824 prog_makefile_rules(outmk, p); 825 826 fprintf(outmk, "\n.include <bsd.prog.mk>\n"); 827 fprintf(outmk, "\n# ========\n"); 828 829 bottom_makefile_rules(outmk); 830 831 fclose(outmk); 832 } 833 834 835 static void 836 gen_output_cfile(void) 837 { 838 char **cp; 839 FILE *outcf; 840 prog_t *p; 841 strlst_t *s; 842 843 (void)snprintf(line, sizeof(line), "generating %s", outcfname); 844 status(line); 845 846 if ((outcf = fopen(outcfname, "w")) == NULL) { 847 perror(outcfname); 848 goterror = 1; 849 return; 850 } 851 852 fprintf(outcf, 853 "/* %s - generated from %s by crunchgen %s */\n", 854 outcfname, infilename, CRUNCH_VERSION); 855 856 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname); 857 for (cp = crunched_skel; *cp != NULL; cp++) 858 fprintf(outcf, "%s\n", *cp); 859 860 for (p = progs; p != NULL; p = p->next) 861 fprintf(outcf, "extern int _crunched_%s_stub(int, char **, char **);\n", 862 p->ident); 863 864 fprintf(outcf, "\nstatic const struct stub entry_points[] = {\n"); 865 for (p = progs; p != NULL; p = p->next) { 866 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 867 p->name, p->ident); 868 for (s = p->links; s != NULL; s = s->next) 869 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n", 870 s->str, p->ident); 871 } 872 873 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n"); 874 fprintf(outcf, "\t{ NULL, NULL }\n};\n"); 875 fclose(outcf); 876 } 877 878 879 static char * 880 genident(char *str) 881 { 882 char *n,*s,*d; 883 884 /* 885 * generates a Makefile/C identifier from a program name, mapping '-' to 886 * '_' and ignoring all other non-identifier characters. This leads to 887 * programs named "foo.bar" and "foobar" to map to the same identifier. 888 */ 889 890 if ((n = strdup(str)) == NULL) 891 return NULL; 892 for (d = s = n; *s != '\0'; s++) { 893 if (*s == '-') 894 *d++ = '_'; 895 else 896 if (*s == '_' || isalnum((unsigned char)*s)) 897 *d++ = *s; 898 } 899 *d = '\0'; 900 return n; 901 } 902 903 904 static char * 905 dir_search(char *progname) 906 { 907 char path[MAXPATHLEN]; 908 strlst_t *dir; 909 910 for (dir=srcdirs; dir != NULL; dir=dir->next) { 911 snprintf(path, sizeof(path), "%s/%s/Makefile", dir->str, progname); 912 if (is_nonempty_file(path)) 913 return dir->str; 914 } 915 return NULL; 916 } 917 918 919 static void 920 top_makefile_rules(FILE *outmk) 921 { 922 prog_t *p; 923 924 if (!pie) 925 fprintf(outmk, "NOPIE=\n"); 926 if (!sanitizer) 927 fprintf(outmk, "NOSANITIZER=\n"); 928 fprintf(outmk, "NOMAN=\n\n"); 929 930 fprintf(outmk, "DBG=%s\n", dbg); 931 fprintf(outmk, "MAKE?=make\n"); 932 #ifdef NEW_TOOLCHAIN 933 fprintf(outmk, "OBJCOPY?=objcopy\n"); 934 fprintf(outmk, "NM?=nm\n"); 935 fprintf(outmk, "AWK?=awk\n"); 936 #else 937 fprintf(outmk, "CRUNCHIDE?=crunchide\n"); 938 #endif 939 940 fprintf(outmk, "CRUNCHED_OBJS="); 941 for (p = progs; p != NULL; p = p->next) 942 fprintf(outmk, " %s.cro", p->name); 943 fprintf(outmk, "\n"); 944 fprintf(outmk, "DPADD+= ${CRUNCHED_OBJS}\n"); 945 fprintf(outmk, "LDADD+= ${CRUNCHED_OBJS} "); 946 output_strlst(outmk, libs); 947 fprintf(outmk, "CRUNCHEDOBJSDIRS="); 948 for (p = progs; p != NULL; p = p->next) 949 fprintf(outmk, " %s", p->ident); 950 fprintf(outmk, "\n\n"); 951 952 fprintf(outmk, "SUBMAKE_TARGETS="); 953 for (p = progs; p != NULL; p = p->next) 954 fprintf(outmk, " %s_make", p->ident); 955 fprintf(outmk, "\n\n"); 956 957 fprintf(outmk, "LDSTATIC=-static%s\n\n", pie ? " -pie" : ""); 958 fprintf(outmk, "PROG=%s\n\n", execfname); 959 960 fprintf(outmk, "all: ${PROG}.crunched\n"); 961 fprintf(outmk, "${PROG}.crunched: ${SUBMAKE_TARGETS} .WAIT ${PROG}.strip\n"); 962 fprintf(outmk, "${PROG}.strip:\n"); 963 fprintf(outmk, "\t${MAKE} -f ${PROG}.mk ${PROG}\n"); 964 fprintf(outmk, "\t@[ -f ${PROG}.unstripped -a ! ${PROG} -nt ${PROG}.unstripped ] || { \\\n"); 965 fprintf(outmk, "\t\t${_MKSHMSG:Uecho} \" strip \" ${PROG}; \\\n"); 966 fprintf(outmk, "\t\tcp ${PROG} ${PROG}.unstripped && \\\n"); 967 fprintf(outmk, "\t\t${OBJCOPY} -S -R .eh_frame -R .eh_frame_hdr -R .note -R .note.netbsd.mcmodel -R .note.netbsd.pax -R .ident -R .comment -R .copyright ${PROG} && \\\n"); 968 fprintf(outmk, "\t\ttouch ${PROG}.unstripped; \\\n"); 969 fprintf(outmk, "\t}\n"); 970 fprintf(outmk, "objs: $(SUBMAKE_TARGETS)\n"); 971 fprintf(outmk, "exe: %s\n", execfname); 972 fprintf(outmk, "clean:\n\trm -rf %s *.cro *.cro.syms *.o *_stub.c ${CRUNCHEDOBJSDIRS} ${PROG}.unstripped\n", 973 execfname); 974 } 975 976 static void 977 bottom_makefile_rules(FILE *outmk) 978 { 979 } 980 981 982 static void 983 prog_makefile_rules(FILE *outmk, prog_t *p) 984 { 985 strlst_t *lst; 986 987 fprintf(outmk, "\n# -------- %s\n\n", p->name); 988 989 fprintf(outmk, "%s_OBJPATHS=", p->ident); 990 #ifndef NEW_TOOLCHAIN 991 fprintf(outmk, " %s_stub.o", p->name); 992 #endif 993 if (p->objs) 994 output_strlst(outmk, p->objpaths); 995 else 996 fprintf(outmk, " %s/%s.ro\n", p->ident, p->name); 997 998 if (p->srcdir && !useobjs) { 999 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir); 1000 if (p->objs) { 1001 fprintf(outmk, "%s_OBJS=", p->ident); 1002 output_strlst(outmk, p->objs); 1003 } 1004 fprintf(outmk, "%s_make: %s .PHONY\n", p->ident, p->ident); 1005 fprintf(outmk, "\t( cd %s; printf '.PATH: ${%s_SRCDIR}\\n" 1006 ".CURDIR:= ${%s_SRCDIR}\\n" 1007 ".include \"$${.CURDIR}/Makefile\"\\n", 1008 p->ident, p->ident, p->ident); 1009 for (lst = vars; lst != NULL; lst = lst->next) 1010 fprintf(outmk, "%s\\n", lst->str); 1011 fprintf(outmk, "'\\\n"); 1012 #define MAKECMD \ 1013 "\t| ${MAKE} -f- CRUNCHEDPROG=1 DBG=${DBG:Q} LDSTATIC=${LDSTATIC:Q} " 1014 fprintf(outmk, MAKECMD "depend"); 1015 fprintf(outmk, " )\n"); 1016 fprintf(outmk, "\t( cd %s; printf '.PATH: ${%s_SRCDIR}\\n" 1017 ".CURDIR:= ${%s_SRCDIR}\\n" 1018 ".include \"$${.CURDIR}/Makefile\"\\n", 1019 p->ident, p->ident, p->ident); 1020 for (lst = vars; lst != NULL; lst = lst->next) 1021 fprintf(outmk, "%s\\n", lst->str); 1022 fprintf(outmk, "'\\\n"); 1023 fprintf(outmk, MAKECMD "%s ", sanitizer ? "" : "NOSANITIZER="); 1024 if (p->objs) 1025 fprintf(outmk, "${%s_OBJS} ) \n\n", p->ident); 1026 else 1027 fprintf(outmk, "%s.ro ) \n\n", p->name); 1028 } else 1029 fprintf(outmk, "%s_make:\n\t@echo \"** Using existing objs for %s\"\n\n", 1030 p->ident, p->name); 1031 1032 #ifdef NEW_TOOLCHAIN 1033 fprintf(outmk, "%s:\n\t mkdir %s\n", p->ident, p->ident); 1034 #endif 1035 fprintf(outmk, "%s.cro: %s .WAIT ${%s_OBJPATHS}\n", 1036 p->name, p->ident, p->ident); 1037 1038 #ifdef NEW_TOOLCHAIN 1039 if (p->objs) 1040 fprintf(outmk, "\t${LD} -r -o %s/%s.ro $(%s_OBJPATHS)\n", 1041 p->ident, p->name, p->ident); 1042 /* Use one awk command.... */ 1043 fprintf(outmk, "\t${NM} -ng %s/%s.ro | ${AWK} '/^ *U / { next };", 1044 p->ident, p->name); 1045 fprintf(outmk, " /^[0-9a-fA-F]+ C/ { next };"); 1046 for (lst = p->keepsymbols; lst != NULL; lst = lst->next) 1047 fprintf(outmk, " / %s$$/ { next };", lst->str); 1048 fprintf(outmk, " / main$$/ { print \"main _crunched_%s_stub\"; next };", 1049 p->ident); 1050 /* gdb thinks these are C++ and ignores everthing after the first $$. */ 1051 fprintf(outmk, " { print $$3 \" \" $$3 \"$$$$from$$$$%s\" }' " 1052 "> %s.cro.syms\n", p->name, p->name); 1053 fprintf(outmk, "\t${OBJCOPY} --redefine-syms %s.cro.syms ", p->name); 1054 fprintf(outmk, "%s/%s.ro %s.cro\n", p->ident, p->name, p->name); 1055 #else 1056 fprintf(outmk, "\t${LD} -dc -r -o %s.cro $(%s_OBJPATHS)\n", 1057 p->name, p->ident); 1058 fprintf(outmk, "\t${CRUNCHIDE} -k _crunched_%s_stub ", p->ident); 1059 for (lst = p->keepsymbols; lst != NULL; lst = lst->next) 1060 fprintf(outmk, "-k %s ", lst->str); 1061 fprintf(outmk, "%s.cro\n", p->name); 1062 fprintf(outmk, "%s_stub.c:\n", p->name); 1063 fprintf(outmk, "\techo \"" 1064 "int _crunched_%s_stub(int argc, char **argv, char **envp)" 1065 "{return main(argc,argv,envp);}\" >%s_stub.c\n", 1066 p->ident, p->name); 1067 #endif 1068 } 1069 1070 static void 1071 output_strlst(FILE *outf, strlst_t *lst) 1072 { 1073 for (; lst != NULL; lst = lst->next) 1074 fprintf(outf, " %s", lst->str); 1075 fprintf(outf, "\n"); 1076 } 1077 1078 1079 /* 1080 * ======================================================================== 1081 * general library routines 1082 * 1083 */ 1084 1085 static void 1086 status(const char *str) 1087 { 1088 static int lastlen = 0; 1089 int len, spaces; 1090 1091 if (!verbose) 1092 return; 1093 1094 len = strlen(str); 1095 spaces = lastlen - len; 1096 if (spaces < 1) 1097 spaces = 1; 1098 1099 fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " "); 1100 fflush(stderr); 1101 lastlen = len; 1102 } 1103 1104 1105 static void 1106 out_of_memory(void) 1107 { 1108 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum); 1109 exit(1); 1110 } 1111 1112 1113 static void 1114 add_string(strlst_t **listp, char *str) 1115 { 1116 strlst_t *p1, *p2; 1117 1118 /* add to end, but be smart about dups */ 1119 1120 for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next) 1121 if (!strcmp(p2->str, str)) 1122 return; 1123 1124 p2 = malloc(sizeof(strlst_t)); 1125 if (p2) 1126 p2->str = strdup(str); 1127 if (!p2 || !p2->str) 1128 out_of_memory(); 1129 1130 p2->next = NULL; 1131 if (p1 == NULL) 1132 *listp = p2; 1133 else 1134 p1->next = p2; 1135 } 1136 1137 1138 static int 1139 is_dir(const char *pathname) 1140 { 1141 struct stat buf; 1142 1143 if (stat(pathname, &buf) == -1) 1144 return 0; 1145 return S_ISDIR(buf.st_mode); 1146 } 1147 1148 static int 1149 is_nonempty_file(const char *pathname) 1150 { 1151 struct stat buf; 1152 1153 if (stat(pathname, &buf) == -1) 1154 return 0; 1155 1156 return S_ISREG(buf.st_mode) && buf.st_size > 0; 1157 } 1158