xref: /openbsd/usr.sbin/crunchgen/crunchgen.c (revision ea00e92b)
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