1 /* $OpenBSD: crunchgen.c,v 1.30 2024/06/02 18:47:17 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 #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
main(int argc,char * argv[])116 main(int argc, char *argv[])
117 {
118 char *p;
119 int optc;
120
121 if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1) {
122 perror("pledge");
123 exit(1);
124 }
125
126 while ((optc = getopt(argc, argv, "hm:c:e:fqD:EL:O:M")) != -1) {
127 switch (optc) {
128 case 'h':
129 optreset = 1;
130 return (crunchide_main(argc, argv));
131 break;
132 case 'f':
133 readcache = 0;
134 break;
135 case 'q':
136 verbose = 0;
137 break;
138
139 case 'm':
140 if (strlcpy(outmkname, optarg, sizeof(outmkname)) >=
141 sizeof(outmkname))
142 usage();
143 break;
144 case 'c':
145 if (strlcpy(outcfname, optarg, sizeof(outcfname)) >=
146 sizeof(outcfname))
147 usage();
148 break;
149 case 'e':
150 if (strlcpy(execfname, optarg, sizeof(execfname)) >=
151 sizeof(execfname))
152 usage();
153 break;
154
155 case 'D':
156 if (strlcpy(topdir, optarg, sizeof(topdir)) >= sizeof(topdir))
157 usage();
158 break;
159 case 'E':
160 elf_names = 1;
161 break;
162 case 'L':
163 if (strlen(optarg) >= PATH_MAX)
164 usage();
165 add_string(&libdirs, optarg);
166 break;
167 case 'O':
168 if (strlcpy(objdir, optarg, sizeof(objdir)) >=
169 sizeof(objdir))
170 usage();
171 break;
172 case 'M':
173 elf_mangle = 1;
174 break;
175 default:
176 usage();
177 }
178 }
179
180 argc -= optind;
181 argv += optind;
182
183 if (argc != 1)
184 usage();
185
186 if (libdirs == NULL)
187 add_string(&libdirs, "/usr/lib");
188 /*
189 * generate filenames
190 */
191
192 if (strlcpy(infilename, argv[0], sizeof(infilename)) >=
193 sizeof(infilename))
194 usage();
195
196 /* confname = `basename infilename .conf` */
197
198 if ((p = strrchr(infilename, '/')) != NULL)
199 strlcpy(confname, p + 1, sizeof confname);
200 else
201 strlcpy(confname, infilename, sizeof confname);
202 if ((p = strrchr(confname, '.')) != NULL && !strcmp(p, ".conf"))
203 *p = '\0';
204
205 if (!*outmkname)
206 snprintf(outmkname, sizeof(outmkname), "%s.mk", confname);
207 if (!*outcfname)
208 snprintf(outcfname, sizeof(outcfname), "%s.c", confname);
209 if (!*execfname)
210 snprintf(execfname, sizeof(execfname), "%s", confname);
211 snprintf(cachename, sizeof(cachename), "%s.cache", confname);
212
213 parse_conf_file();
214 gen_outputs();
215
216 exit(goterror);
217 }
218
219 void
usage(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
parse_conf_file(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
parse_one_file(char * filename)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
parse_line(char * line,int * fc,char ** fv,int nf)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
add_srcdirs(int argc,char ** argv)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
add_libdirs(int argc,char ** argv)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
add_progs(int argc,char ** argv)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
add_prog(char * progname)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
add_link(int argc,char ** argv)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
add_libs(int argc,char ** argv)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
add_special(int argc,char ** argv)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 *
find_prog(char * str)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
gen_outputs(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
fillin_program(prog_t * p)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
fillin_program_objs(prog_t * p,char * path)658 fillin_program_objs(prog_t * p, char *path)
659 {
660 char *cp, *obj, tempfname[PATH_MAX], cwd[PATH_MAX];
661 int fd, dotfd, rc;
662 FILE *f;
663
664 /* discover the objs from the srcdir Makefile */
665
666 snprintf(tempfname, sizeof(tempfname), ".tmp_%sXXXXXXXXXX", confname);
667 if ((fd = mkstemp(tempfname)) == -1 || (f = fdopen(fd, "w")) == NULL) {
668 if (fd != -1)
669 close(fd);
670 perror(tempfname);
671 goterror = 1;
672 return;
673 }
674 fprintf(f, ".include \"%s\"\n", path);
675 fprintf(f, ".if defined(PROG) && !defined(OBJS)\n");
676 fprintf(f, "OBJS=${PROG}.o\n");
677 fprintf(f, ".endif\n");
678 fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n");
679 fclose(f);
680
681 if ((dotfd = open(".", O_RDONLY)) == -1 ||
682 getcwd(cwd, sizeof(cwd)) == NULL) {
683 perror("get cwd");
684 goterror = 1;
685 return;
686 }
687 if (chdir(dirname(path)) == -1) {
688 perror("chdir target dir");
689 goterror = 1;
690 return;
691 }
692 snprintf(line, sizeof(line), "make -f %s/%s crunchgen_objs 2>&1", cwd, tempfname);
693 if ((f = popen(line, "r")) == NULL) {
694 perror("submake pipe");
695 goterror = 1;
696 return;
697 }
698 if (fchdir(dotfd) == -1) {
699 perror("fchdir back");
700 goterror = 1;
701 return;
702 }
703 while (fgets(line, MAXLINELEN, f)) {
704 if (strncmp(line, "OBJS= ", 6)) {
705 if (strcmp(line,
706 "sh: warning: running as root with dot in PATH\n") == 0)
707 continue;
708 fprintf(stderr, "make error: %s", line);
709 goterror = 1;
710 continue;
711 }
712 cp = line + 6;
713 while (isspace((unsigned char)*cp))
714 cp++;
715 while (*cp) {
716 obj = cp;
717 while (*cp && !isspace((unsigned char)*cp))
718 cp++;
719 if (*cp)
720 *cp++ = '\0';
721 add_string(&p->objs, obj);
722 while (isspace((unsigned char)*cp))
723 cp++;
724 }
725 }
726 if ((rc = pclose(f)) != 0) {
727 fprintf(stderr, "make error: make returned %d\n", rc);
728 goterror = 1;
729 }
730 unlink(tempfname);
731 }
732
733 void
remove_error_progs(void)734 remove_error_progs(void)
735 {
736 prog_t *p1, *p2;
737
738 p1 = NULL;
739 p2 = progs;
740 while (p2 != NULL) {
741 if (!p2->goterror)
742 p1 = p2, p2 = p2->next;
743 else {
744 /* delete it from linked list */
745 fprintf(stderr, "%s: %s: ignoring program because of errors.\n",
746 infilename, p2->name);
747 if (p1)
748 p1->next = p2->next;
749 else
750 progs = p2->next;
751 p2 = p2->next;
752 }
753 }
754 }
755
756 void
gen_specials_cache(void)757 gen_specials_cache(void)
758 {
759 FILE *cachef;
760 prog_t *p;
761
762 if ((cachef = fopen(cachename, "w")) == NULL) {
763 perror(cachename);
764 goterror = 1;
765 return;
766 }
767 fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n",
768 cachename, infilename, CRUNCH_VERSION);
769
770 for (p = progs; p != NULL; p = p->next) {
771 fprintf(cachef, "\n");
772 if (p->srcdir)
773 fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir);
774 if (p->mf_name)
775 fprintf(cachef, "special %s mf_name %s\n", p->name, p->mf_name);
776 if (p->objdir)
777 fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir);
778 if (p->objs) {
779 fprintf(cachef, "special %s objs", p->name);
780 output_strlst(cachef, p->objs);
781 }
782 fprintf(cachef, "special %s objpaths", p->name);
783 output_strlst(cachef, p->objpaths);
784 }
785 fclose(cachef);
786 }
787
788 void
gen_output_makefile(void)789 gen_output_makefile(void)
790 {
791 prog_t *p;
792 FILE *outmk;
793
794 if ((outmk = fopen(outmkname, "w")) == NULL) {
795 perror(outmkname);
796 goterror = 1;
797 return;
798 }
799 fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
800 outmkname, infilename, CRUNCH_VERSION);
801
802 top_makefile_rules(outmk);
803
804 for (p = progs; p != NULL; p = p->next)
805 prog_makefile_rules(outmk, p);
806
807 fprintf(outmk, "\n# ========\n");
808 fclose(outmk);
809 }
810
811 void
gen_output_cfile(void)812 gen_output_cfile(void)
813 {
814 extern char *crunched_skel[];
815 char **cp;
816 FILE *outcf;
817 prog_t *p;
818 strlst_t *s;
819
820 if ((outcf = fopen(outcfname, "w")) == NULL) {
821 perror(outcfname);
822 goterror = 1;
823 return;
824 }
825 fprintf(outcf, "/* %s - generated from %s by crunchgen %s */\n",
826 outcfname, infilename, CRUNCH_VERSION);
827
828 fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
829 for (cp = crunched_skel; *cp != NULL; cp++)
830 fprintf(outcf, "%s\n", *cp);
831
832 for (p = progs; p != NULL; p = p->next)
833 fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
834
835 fprintf(outcf, "\nstruct stub entry_points[] = {\n");
836 for (p = progs; p != NULL; p = p->next) {
837 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
838 p->name, p->ident);
839 for (s = p->links; s != NULL; s = s->next)
840 fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
841 s->str, p->ident);
842 }
843
844 fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
845 fprintf(outcf, "\t{ NULL, NULL }\n};\n");
846 fclose(outcf);
847 }
848
849 char *
genident(char * str)850 genident(char *str)
851 {
852 char *n, *s, *d;
853
854 /*
855 * generates a Makefile/C identifier from a program name, mapping '-' to
856 * '_' and ignoring all other non-identifier characters. This leads to
857 * programs named "foo.bar" and "foobar" to map to the same identifier.
858 */
859
860 if ((n = strdup(str)) == NULL)
861 return NULL;
862 for (d = s = n; *s != '\0'; s++) {
863 if (*s == '-')
864 *d++ = '_';
865 else if (*s == '_' || isalnum((unsigned char)*s))
866 *d++ = *s;
867 }
868 *d = '\0';
869 return n;
870 }
871
872 char *
dir_search(char * progname)873 dir_search(char *progname)
874 {
875 char path[PATH_MAX];
876 strlst_t *dir;
877
878 for (dir = srcdirs; dir != NULL; dir = dir->next) {
879 snprintf(path, sizeof(path), "%s/%s", dir->str, progname);
880 if (is_dir(path))
881 return dir->str;
882 }
883 return NULL;
884 }
885
886 void
top_makefile_rules(FILE * outmk)887 top_makefile_rules(FILE * outmk)
888 {
889 prog_t *p;
890 strlst_t *l;
891
892
893 fprintf(outmk, ".include <bsd.own.mk>\n");
894 fprintf(outmk, "CFLAGS+=$(NOPIE_FLAGS)\n");
895 fprintf(outmk, "CFLAGS+=-Oz\n");
896 fprintf(outmk, "CFLAGS+=-fno-stack-protector\n");
897 fprintf(outmk, "CFLAGS+=-fno-unwind-tables\n");
898 fprintf(outmk, ".if ${MACHINE} == \"amd64\"\n");
899 fprintf(outmk, "CFLAGS+=-fcf-protection=none\n");
900 fprintf(outmk, "CFLAGS+=-fno-ret-clean\n");
901 fprintf(outmk, ".endif\n");
902 fprintf(outmk, ".if ${MACHINE} == \"arm64\"\n");
903 fprintf(outmk, "CFLAGS+=-mbranch-protection=none\n");
904 fprintf(outmk, ".endif\n");
905 fprintf(outmk, "CFLAGS+=-fno-asynchronous-unwind-tables\n");
906 fprintf(outmk, "LDFLAGS+=$(NOPIE_LDFLAGS)\n");
907 fprintf(outmk, "STRIP?=strip\n");
908 fprintf(outmk, "LINK=$(LD) -d -r ${LDFLAGS}\n");
909 fprintf(outmk, "LIBS=");
910 for (l = libdirs; l != NULL; l = l->next)
911 fprintf(outmk, " -L%s", l->str);
912 output_strlst(outmk, libs);
913
914 fprintf(outmk, "CRUNCHED_OBJS=");
915 for (p = progs; p != NULL; p = p->next)
916 fprintf(outmk, " %s.lo", p->name);
917 fprintf(outmk, "\n");
918
919 fprintf(outmk, "SUBMAKE_TARGETS=");
920 for (p = progs; p != NULL; p = p->next)
921 fprintf(outmk, " %s_make", p->ident);
922 fprintf(outmk, "\n\n");
923
924 fprintf(outmk, "CLIB=\n");
925 fprintf(outmk, ".if defined(SRCLIBDIR)\n");
926 fprintf(outmk, ". for lib in ${LIBS:M-l*:S/-l//}\n");
927 fprintf(outmk, ". if exists(${SRCLIBDIR}/lib${lib})\n");
928 fprintf(outmk, "CLIB+=lib${lib}.a\n");
929 fprintf(outmk, ". endif\n");
930 fprintf(outmk, ". endfor\n");
931 fprintf(outmk, ".endif\n\n");
932
933 fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS) ${CLIB}\n",
934 execfname, execfname);
935 fprintf(outmk, "\t$(CC) -static -L. ${LDFLAGS} -o $@ %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
936 execfname);
937 fprintf(outmk, "\t$(STRIP) -R .comment %s\n", execfname);
938
939 fprintf(outmk, "\n");
940 fprintf(outmk, ".if !empty(CLIB)\n");
941 fprintf(outmk, ".SUFFIXES: .a .olist\n");
942 fprintf(outmk, ".olist.a:\n");
943 fprintf(outmk, "\t@rm -f ${.TARGET}\n");
944 fprintf(outmk, "\t@cd ${SRCLIBDIR}/${.PREFIX} &&\t\t\t\t\\\n");
945 fprintf(outmk, "\t${MAKE} DIST_CFLAGS=\"${CFLAGS}\"\t\t\t\t\\\n");
946 fprintf(outmk, "\t DIST_OBJS=\"`cat ${.OBJDIR}/${.IMPSRC}`\"\t\t\\\n");
947 fprintf(outmk, "\t DIST_LIB=${.OBJDIR}/${.TARGET} ${.OBJDIR}/${.TARGET}\n\n");
948
949 fprintf(outmk, "%s.map: %s.o $(CRUNCHED_OBJS)\n", execfname, execfname);
950 fprintf(outmk, "\t$(CC) -static ${LDFLAGS} -o %s.o.o %s.o $(CRUNCHED_OBJS) \\\n", execfname, execfname);
951 fprintf(outmk, "\t $(LIBS) -Wl,--trace | sed -e 's/^(\\(..*\\))\\(..*\\)/\\1(\\2)/' >${.TARGET}\n\n");
952
953 fprintf(outmk, "${CLIB:.a=.olist}: %s.map\n", execfname);
954 fprintf(outmk, "\tsed -n -e 's!^${DESTDIR}/usr/lib/${.TARGET:R}\\.a(\\([^)]*\\.o\\)).*!\\1!p' \\\n");
955 fprintf(outmk, "\t < %s.map | tr '\\012' ' ' >$@\n", execfname);
956 fprintf(outmk, ".endif\n\n");
957
958 fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
959 fprintf(outmk, "exe: %s\n", execfname);
960 fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n",
961 execfname);
962 fprintf(outmk, ".PHONY: all objs exe clean $(SUBMAKE_TARGETS)\n\n");
963 }
964
965 void
prog_makefile_rules(FILE * outmk,prog_t * p)966 prog_makefile_rules(FILE * outmk, prog_t * p)
967 {
968 fprintf(outmk, "\n# -------- %s\n\n", p->name);
969
970 if (p->srcdir && p->objs) {
971 fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
972 fprintf(outmk, "%s_OBJS=", p->ident);
973 output_strlst(outmk, p->objs);
974 fprintf(outmk, "%s_make:\n", p->ident);
975 fprintf(outmk, "\tcd $(%s_SRCDIR) && exec $(MAKE) -f %s $(%s_OBJS)\n\n",
976 p->ident, p->mf_name, p->ident);
977 } else
978 fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n",
979 p->ident, p->name);
980
981 fprintf(outmk, "%s_OBJPATHS=", p->ident);
982 output_strlst(outmk, p->objpaths);
983
984 fprintf(outmk, "%s_stub.c:\n", p->name);
985 fprintf(outmk, "\t@echo \""
986 "extern int main(int argc, char **argv, char **envp);\\\n"
987 "int _crunched_%s_stub(int argc, char **argv, char **envp)"
988 " { return main(argc, argv, envp); }\" >$@\n",
989 p->ident);
990 fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n",
991 p->name, p->name, p->ident);
992 fprintf(outmk, "\t$(LINK) -o $@ %s_stub.o $(%s_OBJPATHS)\n",
993 p->name, p->ident);
994 fprintf(outmk, "\tcrunchgen -h %s -k %s_crunched_%s_stub $@\n",
995 elf_mangle ? "-M" : "",
996 elf_names ? "" : "_", p->ident);
997 }
998
999 void
output_strlst(FILE * outf,strlst_t * lst)1000 output_strlst(FILE * outf, strlst_t * lst)
1001 {
1002 for (; lst != NULL; lst = lst->next)
1003 fprintf(outf, " %s", lst->str);
1004 fprintf(outf, "\n");
1005 }
1006
1007 void
out_of_memory(void)1008 out_of_memory(void)
1009 {
1010 fprintf(stderr, "%s: %d: out of memory, stopping.\n", infilename, linenum);
1011 exit(1);
1012 }
1013
1014 void
add_string(strlst_t ** listp,char * str)1015 add_string(strlst_t ** listp, char *str)
1016 {
1017 strlst_t *p1, *p2;
1018
1019 /* add to end, but be smart about dups */
1020
1021 for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
1022 if (!strcmp(p2->str, str))
1023 return;
1024
1025 p2 = calloc(1, sizeof(strlst_t));
1026 if (p2)
1027 p2->str = strdup(str);
1028 if (!p2 || !p2->str)
1029 out_of_memory();
1030
1031 p2->next = NULL;
1032 if (p1 == NULL)
1033 *listp = p2;
1034 else
1035 p1->next = p2;
1036 }
1037
1038 int
is_dir(char * pathname)1039 is_dir(char *pathname)
1040 {
1041 struct stat buf;
1042
1043 if (stat(pathname, &buf) == -1)
1044 return 0;
1045 return S_ISDIR(buf.st_mode);
1046 }
1047
1048 int
is_nonempty_file(char * pathname)1049 is_nonempty_file(char *pathname)
1050 {
1051 struct stat buf;
1052
1053 if (stat(pathname, &buf) == -1)
1054 return 0;
1055
1056 return S_ISREG(buf.st_mode) && buf.st_size > 0;
1057 }
1058