xref: /titanic_51/usr/src/tools/aw/aw.c (revision 6f45ec7b0b964c3be967c4880e8867ac1e7763a5)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Wrapper for the GNU assembler to make it accept the Sun assembler
31  * arguments where possible.
32  *
33  * There are several limitations; the Sun assembler takes multiple
34  * source files, we only take one.
35  *
36  * -b, -s, -xF, -T plain not supported.
37  * -S isn't supported either, because while GNU as does generate
38  * listings with -a, there's no obvious mapping between sub-options.
39  * -K pic, -K PIC not supported either, though it's not clear what
40  * these actually do ..
41  * -Qy (not supported) adds a string to the .comment section
42  * describing the assembler version, while
43  * -Qn (supported) suppresses the string (also the default).
44  *
45  * We also add '-#' support to see invocation lines..
46  * We also add '-xarch=amd64' in case we need to feed the assembler
47  * something different (or in case we need to invoke a different binary
48  * altogether!)
49  */
50 
51 #include <sys/types.h>
52 #include <sys/wait.h>
53 #include <stdio.h>
54 #include <unistd.h>
55 #include <string.h>
56 #include <stdlib.h>
57 
58 static const char *progname;
59 static int verbose;
60 
61 struct aelist {
62 	int ael_argc;
63 	struct ae {
64 		struct ae *ae_next;
65 		char *ae_arg;
66 	} *ael_head, *ael_tail;
67 };
68 
69 static struct aelist *
70 newael(void)
71 {
72 	return (calloc(sizeof (struct aelist), 1));
73 }
74 
75 static void
76 newae(struct aelist *ael, const char *arg)
77 {
78 	struct ae *ae;
79 
80 	ae = calloc(sizeof (*ae), 1);
81 	ae->ae_arg = strdup(arg);
82 	if (ael->ael_tail == NULL)
83 		ael->ael_head = ae;
84 	else
85 		ael->ael_tail->ae_next = ae;
86 	ael->ael_tail = ae;
87 	ael->ael_argc++;
88 }
89 
90 static void
91 fixae_arg(struct ae *ae, const char *newarg)
92 {
93 	free(ae->ae_arg);
94 	ae->ae_arg = strdup(newarg);
95 }
96 
97 static char **
98 aeltoargv(struct aelist *ael)
99 {
100 	struct ae *ae;
101 	char **argv;
102 	int argc;
103 
104 	argv = calloc(sizeof (*argv), ael->ael_argc + 1);
105 
106 	for (argc = 0, ae = ael->ael_head; ae; ae = ae->ae_next, argc++) {
107 		argv[argc] = ae->ae_arg;
108 		if (ae == ael->ael_tail)
109 			break;
110 	}
111 
112 	return (argv);
113 }
114 
115 static int
116 error(const char *arg)
117 {
118 	(void) fprintf(stderr,
119 	    "%s: as->gas mapping failed at or near arg '%s'\n", progname, arg);
120 	return (2);
121 }
122 
123 static int
124 usage(const char *arg)
125 {
126 	if (arg != NULL)
127 		(void) fprintf(stderr, "error: %s\n", arg);
128 	(void) fprintf(stderr, "Usage: %s [-V] [-#]\n"
129 	    "\t[-xarch=architecture]\n"
130 	    "\t[-o objfile] [-L]\n"
131 	    "\t[-P [[-Ipath] [-Dname] [-Dname=def] [-Uname]]...]\n"
132 	    "\t[-m] [-n] file.s ...\n", progname);
133 	return (3);
134 }
135 
136 static void
137 copyuntil(FILE *in, FILE *out, int termchar)
138 {
139 	int c;
140 
141 	while ((c = fgetc(in)) != EOF) {
142 		if (out && fputc(c, out) == EOF)
143 			exit(1);
144 		if (c == termchar)
145 			break;
146 	}
147 }
148 
149 /*
150  * The idea here is to take directives like this emitted
151  * by cpp:
152  *
153  *	# num
154  *
155  * and convert them to directives like this that are
156  * understood by the GNU assembler:
157  *
158  *	.line num
159  *
160  * and similarly:
161  *
162  *	# num "string" optional stuff
163  *
164  * is converted to
165  *
166  *	.line num
167  *	.file "string"
168  *
169  * While this could be done with a sequence of sed
170  * commands, this is simpler and faster..
171  */
172 static pid_t
173 filter(int pipein, int pipeout)
174 {
175 	pid_t pid;
176 	FILE *in, *out;
177 
178 	if (verbose)
179 		(void) fprintf(stderr, "{#line filter} ");
180 
181 	switch (pid = fork()) {
182 	case 0:
183 		if (dup2(pipein, 0) == -1 ||
184 		    dup2(pipeout, 1) == -1) {
185 			perror("dup2");
186 			exit(1);
187 		}
188 		closefrom(3);
189 		break;
190 	case -1:
191 		perror("fork");
192 	default:
193 		return (pid);
194 	}
195 
196 	in = fdopen(0, "r");
197 	out = fdopen(1, "w");
198 
199 	while (!feof(in)) {
200 		int c, num;
201 
202 		switch (c = fgetc(in)) {
203 		case '#':
204 			switch (fscanf(in, " %d", &num)) {
205 			case 0:
206 				/*
207 				 * discard comment lines completely
208 				 * discard ident strings completely too.
209 				 * (GNU as politely ignores them..)
210 				 */
211 				copyuntil(in, NULL, '\n');
212 				break;
213 			default:
214 				(void) fprintf(stderr, "fscanf botch?");
215 				/*FALLTHROUGH*/
216 			case EOF:
217 				exit(1);
218 				/*NOTREACHED*/
219 			case 1:
220 				/*
221 				 * This line has a number at the beginning;
222 				 * if it has a string after the number, then
223 				 * it's a filename.
224 				 */
225 				if (fgetc(in) == ' ' && fgetc(in) == '"') {
226 					(void) fprintf(out, "\t.file \"");
227 					copyuntil(in, out, '"');
228 					(void) fputc('\n', out);
229 				}
230 				(void) fprintf(out, "\t.line %d\n", num - 1);
231 				/*
232 				 * discard the rest of the line
233 				 */
234 				copyuntil(in, NULL, '\n');
235 				break;
236 			}
237 			break;
238 		case '\n':
239 			/*
240 			 * preserve newlines
241 			 */
242 			(void) fputc(c, out);
243 			break;
244 		case EOF:
245 			/*
246 			 * don't write EOF!
247 			 */
248 			break;
249 		default:
250 			/*
251 			 * lines that don't begin with '#' are copied
252 			 */
253 			(void) fputc(c, out);
254 			copyuntil(in, out, '\n');
255 			break;
256 		}
257 
258 		if (ferror(out))
259 			exit(1);
260 	}
261 
262 	exit(0);
263 	/*NOTREACHED*/
264 }
265 
266 static pid_t
267 invoke(char **argv, int pipein, int pipeout)
268 {
269 	pid_t pid;
270 
271 	if (verbose) {
272 		char **dargv = argv;
273 
274 		while (*dargv)
275 			(void) fprintf(stderr, "%s ", *dargv++);
276 	}
277 
278 	switch (pid = fork()) {
279 	case 0:
280 		if (pipein >= 0 && dup2(pipein, 0) == -1) {
281 			perror("dup2");
282 			exit(1);
283 		}
284 		if (pipeout >= 0 && dup2(pipeout, 1) == -1) {
285 			perror("dup2");
286 			exit(1);
287 		}
288 		closefrom(3);
289 		(void) execvp(argv[0], argv);
290 		perror("execvp");
291 		(void) fprintf(stderr, "%s: couldn't run %s\n",
292 		    progname, argv[0]);
293 		break;
294 	case -1:
295 		perror("fork");
296 	default:
297 		return (pid);
298 	}
299 	exit(2);
300 	/*NOTREACHED*/
301 }
302 
303 static int
304 pipeline(char **ppargv, char **asargv)
305 {
306 	int pipedes[4];
307 	int active = 0;
308 	int rval = 0;
309 	pid_t pid_pp, pid_f, pid_as;
310 
311 	if (pipe(pipedes) == -1 || pipe(pipedes + 2) == -1) {
312 		perror("pipe");
313 		return (4);
314 	}
315 
316 	if ((pid_pp = invoke(ppargv, -1, pipedes[0])) > 0)
317 		active++;
318 
319 	if (verbose)
320 		(void) fprintf(stderr, "| ");
321 
322 	if ((pid_f = filter(pipedes[1], pipedes[2])) > 0)
323 		active++;
324 
325 	if (verbose)
326 		(void) fprintf(stderr, "| ");
327 
328 	if ((pid_as = invoke(asargv, pipedes[3], -1)) > 0)
329 		active++;
330 
331 	if (verbose) {
332 		(void) fprintf(stderr, "\n");
333 		(void) fflush(stderr);
334 	}
335 
336 	closefrom(3);
337 
338 	if (active != 3)
339 		return (5);
340 
341 	while (active != 0) {
342 		pid_t pid;
343 		int stat;
344 
345 		if ((pid = wait(&stat)) == -1) {
346 			rval++;
347 			break;
348 		}
349 
350 		if (!WIFEXITED(stat))
351 			continue;
352 
353 		if (pid == pid_pp || pid == pid_f || pid == pid_as) {
354 			active--;
355 			if (WEXITSTATUS(stat) != 0)
356 				rval++;
357 		}
358 	}
359 
360 	return (rval);
361 }
362 
363 int
364 main(int argc, char *argv[])
365 {
366 	struct aelist *cpp = NULL;
367 	struct aelist *m4 = NULL;
368 	struct aelist *as = newael();
369 	char **asargv;
370 	char *outfile = NULL;
371 	char *srcfile = NULL;
372 	const char *as_dir, *as64_dir, *m4_dir, *m4_lib_dir, *cpp_dir;
373 	char *as_pgm, *as64_pgm, *m4_pgm, *m4_cmdefs, *cpp_pgm;
374 	int as64 = 0;
375 	int code;
376 
377 	if ((progname = strrchr(argv[0], '/')) == NULL)
378 		progname = argv[0];
379 	else
380 		progname++;
381 
382 	/*
383 	 * Helpful when debugging, or when changing tool versions..
384 	 */
385 	if ((as_dir = getenv("AW_AS_DIR")) == NULL)
386 		as_dir = DEFAULT_AS_DIR;	/* /usr/sfw/bin */
387 	as_pgm = malloc(strlen(as_dir) + strlen("/gas") + 1);
388 	(void) sprintf(as_pgm, "%s/gas", as_dir);
389 
390 	if ((as64_dir = getenv("AW_AS64_DIR")) == NULL)
391 		as64_dir = DEFAULT_AS64_DIR;	/* /usr/sfw/bin */
392 	as64_pgm = malloc(strlen(as64_dir) + strlen("/gas") + 1);
393 	(void) sprintf(as64_pgm, "%s/gas", as64_dir);
394 
395 	if ((m4_dir = getenv("AW_M4_DIR")) == NULL)
396 		m4_dir = DEFAULT_M4_DIR;	/* /usr/ccs/bin */
397 	m4_pgm = malloc(strlen(m4_dir) + strlen("/m4") + 1);
398 	(void) sprintf(m4_pgm, "%s/m4", m4_dir);
399 
400 	if ((m4_lib_dir = getenv("AW_M4LIB_DIR")) == NULL)
401 		m4_lib_dir = DEFAULT_M4LIB_DIR;	/* /usr/ccs/lib */
402 	m4_cmdefs = malloc(strlen(m4_lib_dir) + strlen("/cmdefs") + 1);
403 	(void) sprintf(m4_cmdefs, "%s/cmdefs", m4_lib_dir);
404 
405 	if ((cpp_dir = getenv("AW_CPP_DIR")) == NULL)
406 		cpp_dir = DEFAULT_CPP_DIR;	/* /usr/ccs/lib */
407 	cpp_pgm = malloc(strlen(cpp_dir) + strlen("/cpp") + 1);
408 	(void) sprintf(cpp_pgm, "%s/cpp", cpp_dir);
409 
410 	newae(as, as_pgm);
411 	newae(as, "--warn");
412 	newae(as, "--fatal-warnings");
413 	newae(as, "--traditional-format");
414 
415 	/*
416 	 * This is a support hack to rewrite code for the compiler
417 	 * which should probably cause an assembler programmer to recode
418 	 * - so, generate a warning in this case.
419 	 */
420 	newae(as, "-K");
421 
422 	/*
423 	 * Walk the argument list, translating as we go ..
424 	 */
425 	while (--argc > 0) {
426 		char *arg;
427 		int arglen;
428 
429 		arg = *++argv;
430 		arglen = strlen(arg);
431 
432 		if (*arg != '-') {
433 			char *filename;
434 
435 			/*
436 			 * filenames ending in '.s' are taken to be
437 			 * assembler files, and provide the default
438 			 * basename of the output file.
439 			 *
440 			 * other files are passed through to the
441 			 * preprocessor, if present, or to gas if not.
442 			 */
443 			filename = arg;
444 			if (arglen > 2 &&
445 			    strcmp(arg + arglen - 2, ".s") == 0) {
446 				/*
447 				 * Though 'as' allows multiple assembler
448 				 * files to be processed in one invocation
449 				 * of the assembler, ON only processes one
450 				 * file at a time, which makes things a lot
451 				 * simpler!
452 				 */
453 				if (srcfile == NULL)
454 					srcfile = arg;
455 				else
456 					return (usage(
457 					    "one assembler file at a time"));
458 
459 				/*
460 				 * If we haven't seen a -o option yet,
461 				 * default the output to the basename
462 				 * of the input, substituting a .o on the end
463 				 */
464 				if (outfile == NULL) {
465 					char *argcopy;
466 
467 					argcopy = strdup(arg);
468 					argcopy[arglen - 1] = 'o';
469 
470 					if ((outfile = strrchr(
471 					    argcopy, '/')) == NULL)
472 						outfile = argcopy;
473 					else
474 						outfile++;
475 				}
476 			}
477 			if (cpp)
478 				newae(cpp, filename);
479 			else if (m4)
480 				newae(m4, filename);
481 			else
482 				newae(as, filename);
483 			continue;
484 		} else
485 			arglen--;
486 
487 		switch (arg[1]) {
488 		case 'K':
489 			/*
490 			 * -K pic
491 			 * -K PIC
492 			 */
493 			if (arglen == 1) {
494 				if ((arg = *++argv) == NULL || *arg == '\0')
495 					return (usage("malformed -K"));
496 				argc--;
497 			} else {
498 				arg += 2;
499 			}
500 			if (strcmp(arg, "PIC") != 0 && strcmp(arg, "pic") != 0)
501 				return (usage("malformed -K"));
502 			break;		/* just ignore -Kpic for gcc */
503 		case 'Q':
504 			if (strcmp(arg, "-Qn") == 0)
505 				break;
506 			/*FALLTHROUGH*/
507 		case 'b':
508 		case 's':
509 		case 'T':
510 			/*
511 			 * -b	Extra symbol table for source browser ..
512 			 *	not relevant to gas, thus should error.
513 			 * -s	Put stabs in .stabs section not stabs.excl
514 			 *	not clear if there's an equivalent
515 			 * -T	4.x migration option
516 			 */
517 		default:
518 			return (error(arg));
519 		case 'x':
520 			/*
521 			 * Accept -xarch special case to invoke alternate
522 			 * assemblers or assembler flags for different
523 			 * architectures.
524 			 */
525 			if (strcmp(arg, "-xarch=amd64") == 0 ||
526 			    strcmp(arg, "-xarch=generic64") == 0) {
527 				as64++;
528 				fixae_arg(as->ael_head, as64_pgm);
529 				break;
530 			}
531 			/*
532 			 * XX64: Is this useful to gas?
533 			 */
534 			if (strcmp(arg, "-xmodel=kernel") == 0)
535 				break;
536 
537 			/*
538 			 * -xF	Generates performance analysis data
539 			 *	no equivalent
540 			 */
541 			return (error(arg));
542 		case 'V':
543 			newae(as, arg);
544 			break;
545 		case '#':
546 			verbose++;
547 			break;
548 		case 'L':
549 			newae(as, "--keep-locals");
550 			break;
551 		case 'n':
552 			newae(as, "--no-warn");
553 			break;
554 		case 'o':
555 			if (arglen != 1)
556 				return (usage("bad -o flag"));
557 			if ((arg = *++argv) == NULL || *arg == '\0')
558 				return (usage("bad -o flag"));
559 			outfile = arg;
560 			argc--;
561 			arglen = strlen(arg + 1);
562 			break;
563 		case 'm':
564 			if (cpp)
565 				return (usage("-m conflicts with -P"));
566 			if (m4 == NULL) {
567 				m4 = newael();
568 				newae(m4, m4_pgm);
569 				newae(m4, m4_cmdefs);
570 			}
571 			break;
572 		case 'P':
573 			if (m4)
574 				return (usage("-P conflicts with -m"));
575 			if (cpp == NULL) {
576 				cpp = newael();
577 				newae(cpp, cpp_pgm);
578 				newae(cpp, "-D__GNUC_AS__");
579 			}
580 			break;
581 		case 'D':
582 		case 'U':
583 			if (cpp)
584 				newae(cpp, arg);
585 			else if (m4)
586 				newae(m4, arg);
587 			else
588 				newae(as, arg);
589 			break;
590 		case 'I':
591 			if (cpp)
592 				newae(cpp, arg);
593 			else
594 				newae(as, arg);
595 			break;
596 		case '-':	/* a gas-specific option */
597 			newae(as, arg);
598 			break;
599 		}
600 	}
601 
602 #if defined(__i386)
603 	if (as64)
604 		newae(as, "--64");
605 	else
606 		newae(as, "--32");
607 #endif
608 
609 	if (srcfile == NULL)
610 		return (usage("no source file(s) specified"));
611 	if (outfile == NULL)
612 		outfile = "a.out";
613 	newae(as, "-o");
614 	newae(as, outfile);
615 
616 	asargv = aeltoargv(as);
617 	if (cpp) {
618 #if defined(__sparc)
619 		newae(cpp, "-Dsparc");
620 		newae(cpp, "-D__sparc");
621 		if (as64)
622 			newae(cpp, "-D__sparcv9");
623 		else
624 			newae(cpp, "-D__sparcv8");
625 #elif defined(__i386) || defined(__x86)
626 		if (as64) {
627 			newae(cpp, "-D__x86_64");
628 			newae(cpp, "-D__amd64");
629 		} else {
630 			newae(cpp, "-Di386");
631 			newae(cpp, "-D__i386");
632 		}
633 #else
634 #error	"need isa-dependent defines"
635 #endif
636 		code = pipeline(aeltoargv(cpp), asargv);
637 	} else if (m4)
638 		code = pipeline(aeltoargv(m4), asargv);
639 	else {
640 		/*
641 		 * XXX	should arrange to fork/exec so that we
642 		 *	can unlink the output file if errors are
643 		 *	detected..
644 		 */
645 		(void) execvp(asargv[0], asargv);
646 		perror("execvp");
647 		(void) fprintf(stderr, "%s: couldn't run %s\n",
648 		    progname, asargv[0]);
649 		code = 7;
650 	}
651 	if (code != 0)
652 		(void) unlink(outfile);
653 	return (code);
654 }
655