xref: /original-bsd/old/as.vax/asmain.c (revision d25e1985)
1 /* Copyright (c) 1980 Regents of the University of California */
2 static	char sccsid[] = "@(#)asmain.c 4.8 08/20/80";
3 #include <stdio.h>
4 #include <ctype.h>
5 #include <signal.h>
6 
7 #include "as.h"
8 #include "assyms.h"
9 #include "asexpr.h"
10 #include "asscan.h"
11 
12 #ifdef UNIX
13 #define	unix_lang_name "VAX/UNIX Assembler V08/20/80 4.8"
14 #endif
15 
16 #ifdef VMS
17 #define vms_lang_name "VAX/VMS C Assembler V1.00"
18 #endif VMS
19 
20 /*
21  *	variables to manage reading the assembly source files
22  */
23 char	*dotsname;	/*the current file name; managed by the parser*/
24 int	lineno;		/*current line number; managed by the parser*/
25 char	**innames;	/*names of the files being assembled*/
26 int	ninfiles;	/*how many interesting files there are*/
27 /*
28  *	Flags settable from the argv process argument list
29  */
30 int	silent = 0;	/*don't complain about any errors*/
31 int	savelabels = 0;	/*write the labels to the a.out file*/
32 int 	d124 = 4;	/*default allocate 4 bytes for unknown pointers*/
33 int	anyerrs = 0;	/*no errors yet*/
34 int	orgwarn = 0;	/*Bad origins*/
35 int	passno = 1;	/* current pass*/
36 int	jxxxJUMP = 0;	/* in jxxxes that branch too far, use jmp instead of brw */
37 int	readonlydata = 0;	/* initialzed data -> text space */
38 
39 #ifdef DEBUG
40 int 	debug = 0;
41 int	toktrace = 0;
42 #endif
43 
44 int	useVM =		/*put the temp file in virtual memory*/
45 #ifdef VMS
46 	1;		/*VMS has virtual memory (duh)*/
47 #endif VMS
48 #ifdef UNIX
49  	0;
50 #endif
51 
52 char	*endcore;	/*where to get more symbol space*/
53 
54 /*
55  *	Managers of the a.out file.
56  */
57 struct	exec	hdr;
58 #define	MAGIC	0407
59 u_long	tsize;		/* total text size */
60 u_long	dsize;		/* total data size */
61 u_long	datbase;	/* base of the data segment */
62 u_long	trsize;		/* total text relocation size */
63 u_long	drsize;		/* total data relocation size */
64 
65 /*
66  *	Information about the current segment is accumulated in
67  *	usedot; the most important information stored is the
68  *	accumulated size of each of the text and data segments
69  *
70  *	dotp points to the correct usedot expression for the current segment
71  */
72 struct	exp	usedot[NLOC+NLOC];	/* info about all segments */
73 struct	exp	*dotp;			/* data/text location pointer */
74 /*
75  *	The inter pass temporary file is opened and closed by stdio, but
76  *	is written to using direct read/write, as the temporary file
77  *	is composed of buffers exactly BUFSIZ long.
78  */
79 FILE	*tmpfil;			/* interpass communication file */
80 /*
81  *	a.out is created during the second pass.
82  *	It is opened by stdio, but is filled with the parallel
83  *	block I/O library
84  */
85 char	*outfile = "a.out";
86 FILE	*a_out_file;
87 off_t	a_out_off;			/* cumulative offsets for segments */
88 /*
89  *	The logical files containing the assembled data for each of
90  *	the text and data segments are
91  *	managed by the parallel block I/O library.
92  *	a.out is logically opened in many places at once to
93  *	receive the assembled data from the various segments as
94  *	it all trickles in, but is physically opened only once
95  *	to minimize file overhead.
96  */
97 BFILE	*usefile[NLOC+NLOC];		/* text/data files */
98 BFILE	*txtfil;			/* current text/data file */
99 /*
100  *	Relocation information is accumulated seperately for each
101  *	segment.  This is required by the old loader (from BTL),
102  *	but not by the new loader (Bill Joy).
103  *
104  *	However, the size of the relocation information can not be computed
105  *	during or after the 1st pass because the ''absoluteness' of values
106  *	is unknown until all locally declared symbols have been seen.
107  *	Thus, the size of the relocation information is only
108  *	known after the second pass is finished.
109  *	This obviates the use of the block I/O
110  *	library, which requires knowing the exact offsets in a.out.
111  *
112  *	So, we save the relocation information internally (we don't
113  *	go to internal files to minimize overhead).
114  *
115  *	Empirically, we studied 259 files composing the system,
116  *	two compilers and a compiler generator: (all of which have
117  *	fairly large source files)
118  *
119  *	Number of files = 259
120  *		Number of non zero text reloc files: 233
121  *		Number of non zero data reloc files: 53
122  *	Average text relocation = 889
123  *	Average data relocation = 346
124  *	Number of files > BUFSIZ text relocation = 71
125  *	Number of files > BUFSIZ data relocation = 6
126  *
127  *	For compiled C code, there is usually one text segment and two
128  *	data segments; we see that allocating our own buffers and
129  *	doing our internal handling of relocation information will,
130  *	on the average, not use more memory than taken up by the buffers
131  *	allocated for doing file I/O in parallel to a number of file.
132  *
133  *	If we are assembling with the -V option, we
134  *	use the left over token buffers from the 2nd pass,
135  *	otherwise, we create our own.
136  *
137  *	When the 2nd pass is complete, closeoutrel flushes the token
138  *	buffers out to a BFILE.
139  *
140  *	The internals to relbufdesc are known only in assyms.c
141  *
142  *	outrel constructs the relocation information.
143  *	closeoutrel flushes the relocation information to relfil.
144  */
145 struct	relbufdesc	*rusefile[NLOC+NLOC];
146 struct	relbufdesc 	*relfil;	/* un concatnated relocation info */
147 BFILE	*relocfile;			/* concatnated relocation info */
148 /*
149  *	Once the relocation information has been written,
150  *	we can write out the symbol table using the Block I/O
151  *	mechanisms, as we once again know the offsets into
152  *	the a.out file.
153  *
154  *	We use relfil to output the symbol table information.
155  */
156 
157 char	*tmpdirprefix =
158 #ifdef UNIX
159 			"/tmp/";
160 #else VMS
161 			"/usr/tmp/";
162 #endif
163 
164 #define		TMP_SUFFIX	"asXXXXXX"
165 char		tmpn1[TNAMESIZE];
166 
167 int delexit();
168 
169 main(argc, argv)
170 	int	argc;
171 	char 	**argv;
172 {
173 
174 	tmpn1[0] = 0;
175 	endcore = (char *)sbrk(0);
176 
177 	argprocess(argc, argv);		/* process argument lists */
178 	if (anyerrs) exit(1);
179 
180 	initialize();
181 	zeroorigins();			/* set origins to zero */
182 	zerolocals();			/* fix local label counters */
183 
184 	i_pass1();			/* open temp files, etc */
185 	pass1();			/* first pass through .s files */
186 	testlocals();			/* check for undefined locals */
187 	if (anyerrs) delexit();
188 
189 	pass1_5();			/* resolve jxxx */
190 	if (anyerrs) delexit();
191 
192 	open_a_out();			/* open a.out */
193 	roundsegments();		/* round segments to FW */
194 	build_hdr();			/* build initial header, and output */
195 
196 	i_pass2();			/* reopen temporary file, etc */
197 	pass2();			/* second pass through the virtual .s */
198 	if (anyerrs) delexit();
199 
200 	fillsegments();			/* fill segments with 0 to FW */
201 	reloc_syms();			/* dump relocation and symbol table */
202 
203 	delete();			/* remove tmp file */
204 	bflush();			/* close off block I/O view of a.out */
205 	fix_a_out();			/* add in text and data reloc counts */
206 
207 	if (anyerrs == 0 && orgwarn)
208 		yyerror("Caution: absolute origins.\n");
209 	exit(anyerrs != 0);
210 }	/*end of UNIX main*/
211 
212 argprocess(argc, argv)
213 	int	argc;
214 	char	*argv[];
215 {
216 	register	char	*cp;
217 
218 	ninfiles = 0;
219 	silent = 0;
220 #ifdef DEBUG
221 	debug = 0;
222 #endif
223 	innames = (char **)ClearCalloc(argc+1, sizeof (innames[0]));
224 	dotsname = "<argv error>";
225 	while (argc > 1) {
226 		if (argv[1][0] != '-')
227 			innames[ninfiles++] = argv[1];
228 		else {
229 			cp = argv[1] + 1;
230 			/*
231 			 *	We can throw away single minus signs, so
232 			 *	that make scripts for the PDP 11 assembler work
233 			 *	on this assembler too
234 			 */
235 			while (*cp){
236 				switch(*cp++){
237 				 default:
238 					yyerror("Unknown flag: %c", *--cp);
239 					cp++;
240 					break;
241 				 case 'd':
242 					d124 = *cp++ - '0';
243 					if ( (d124 != 1) && (d124 != 2) &&
244 					     (d124 != 4)){
245 						yyerror("-d[124] only");
246 						exit(1);
247 					}
248 					break;
249 				 case 'o':
250 					if (argc < 3){
251 						yyerror("-o what???");
252 						exit(1);
253 					}
254 					outfile = argv[2];
255 				   bumpone:
256 					argc -= 2;
257 					argv += 2;
258 					goto nextarg;
259 
260 				 case 't':
261 					if (argc < 3){
262 						yyerror("-t what???");
263 						exit(1);
264 					}
265 					tmpdirprefix = argv[2];
266 					goto bumpone;
267 
268 				 case 'V':
269 					useVM = 1;
270 					break;
271 				 case 'W':
272 					silent = 1;
273 					break;
274 				 case 'L':
275 					savelabels = 1;
276 					break;
277 				 case 'J':
278 					jxxxJUMP = 1;
279 					break;
280 #ifdef DEBUG
281 				 case 'D':
282 					debug = 1;
283 					break;
284 				 case 'T':
285 					toktrace = 1;
286 					break;
287 #endif
288 				 case 'R':
289 					readonlydata = 1;
290 					break;
291 				}	/*end of the switch*/
292 			}	/*end of pulling out all arguments*/
293 		}	/*end of a flag argument*/
294 		--argc; ++argv;
295 	   nextarg:;
296 	}
297 	/* innames[ninfiles] = 0; */
298 }
299 
300 initialize()
301 {
302 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
303 		signal(SIGINT, delexit);
304 	/*
305 	 *	Install symbols in the table
306 	 */
307 	symtabinit();
308 	syminstall();
309 	/*
310 	 *	Build the expression parser accelerator token sets
311 	 */
312 	buildtokensets();
313 }
314 
315 zeroorigins()
316 {
317 	register	int	locindex;
318 	/*
319 	 *	Mark usedot: the first NLOC slots are for named text segments,
320 	 *	the next for named data segments.
321 	 */
322 	for (locindex = 0; locindex < NLOC; locindex++){
323 		usedot[locindex].e_xtype = XTEXT;
324 		usedot[NLOC + locindex].e_xtype = XDATA;
325 		usedot[locindex].e_xvalue = 0;
326 		usedot[NLOC + locindex].e_xvalue = 0;
327 		usedot[locindex].e_yvalue = 0;
328 		usedot[NLOC + locindex].e_yvalue = 0;
329 	}
330 }
331 
332 zerolocals()
333 {
334 	register	int	i;
335 
336 	for (i = 0; i <= 9; i++) {
337 		lgensym[i] = 1;
338 		genref[i] = 0;
339 	}
340 }
341 
342 i_pass1()
343 {
344 	if (useVM == 0){
345 		strcat(tmpn1, tmpdirprefix);
346 		if (tmpdirprefix[strlen(tmpdirprefix)-1] != '/')
347 			strcat(tmpn1, "/");
348 		strcat(tmpn1, TMP_SUFFIX);
349 		mktemp(tmpn1);
350 		tmpfil = fopen(tmpn1, "w");
351 		if (tmpfil==NULL) {
352 		  yyerror("Bad pass 1 temporary file for writing %s", tmpn1);
353 		  delexit();
354 		}
355 	}
356 
357 	inittmpfile();
358 	initijxxx();
359 }
360 
361 pass1()
362 {
363 	register	int	i;
364 
365 	passno = 1;
366 	dotp = &usedot[0];
367 	txtfil = (BFILE *)0;
368 	relfil = (struct relbufdesc *)0;
369 
370 	if (ninfiles == 0){		/*take the input from stdin directly*/
371 		lineno = 1;
372 		dotsname = "<stdin>";
373 
374 		yyparse();
375 	} else {		/*we have the names tanked*/
376 		for (i = 0; i < ninfiles; i++){
377 			new_dot_s(innames[i]);
378 			if (freopen(innames[i], "r", stdin) == NULL) {
379 				yyerror( "Can't open source file %s\n",
380 					innames[i]);
381 				exit(2);
382 			}
383 			/* stdio is NOT used to read the input characters */
384 			/* we use read directly, into our own buffers */
385 			yyparse();
386 		}
387 	}
388 
389 	closetmpfile();		/*kick out the last buffered intermediate text*/
390 }
391 
392 testlocals()
393 {
394 	register	int	i;
395 	for (i = 0; i <= 9; i++) {
396 		if (genref[i])
397 			yyerror("Reference to undefined local label %df", i);
398 		lgensym[i] = 1;
399 		genref[i] = 0;
400 	}
401 }
402 
403 pass1_5()
404 {
405 	sortsymtab();
406 #ifdef DEBUG
407 	if (debug) dumpsymtab();
408 #endif
409 	jxxxfix();
410 #ifdef DEBUG
411 	if (debug) dumpsymtab();
412 #endif
413 }
414 
415 open_a_out()
416 {
417 	/*
418 	 *	Open up the a.out file now, and get set to build
419 	 *	up offsets into it for all of the various text,data
420 	 *	text relocation and data relocation segments.
421 	 */
422 	a_out_file = fopen(outfile, "w");
423 	if (a_out_file == NULL) {
424 		yyerror("Cannot create %s", outfile);
425 		delexit();
426 	}
427 	biofd = a_out_file->_file;
428 	a_out_off = 0;
429 }
430 
431 roundsegments()
432 {
433 	register	int	locindex;
434 	register	long	v;
435 	/*
436 	 *	round and assign text segment origins
437 	 *	the exec header always goes in usefile[0]
438 	 */
439 	tsize = 0;
440 	for (locindex=0; locindex<NLOC; locindex++) {
441 		v = round(usedot[locindex].e_xvalue, FW);
442 		usedot[locindex].e_xvalue = tsize;
443 		if ((locindex == 0) || (v != 0) ){
444 			usefile[locindex] = (BFILE *)Calloc(1, sizeof(BFILE));
445 			bopen(usefile[locindex], a_out_off);
446 			if (locindex == 0)
447 				a_out_off = sizeof (struct exec);
448 		} else {
449 			usefile[locindex] = (BFILE *)-1;
450 		}
451 		tsize += v;
452 		a_out_off += v;
453 	}
454 	/*
455 	 *		Round and assign data segment origins.
456 	 */
457 	datbase = round(tsize, FW);
458 	for (locindex=0; locindex<NLOC; locindex++) {
459 		v = round(usedot[NLOC+locindex].e_xvalue, FW);
460 		usedot[NLOC+locindex].e_xvalue = datbase + dsize;
461 		if (v != 0){
462 			usefile[NLOC + locindex] = (BFILE *)Calloc(1,sizeof(BFILE));
463 			bopen(usefile[NLOC + locindex], a_out_off);
464 		} else {
465 			usefile[NLOC + locindex] = (BFILE *)-1;
466 		}
467 		dsize += v;
468 		a_out_off += v;
469 	}
470 	/*
471 	 *	Assign final values to symbols
472 	 */
473 	hdr.a_bss = dsize;
474 	freezesymtab();		/* this touches hdr.a_bss */
475 	stabfix();
476 	/*
477 	 *	Set up the relocation information "files" to
478 	 *	be zero; outrel takes care of the rest
479 	 */
480 	for (locindex = 0; locindex < NLOC + NLOC; locindex++){
481 		rusefile[locindex] = (struct relbufdesc *)0;
482 	}
483 }
484 
485 build_hdr()
486 {
487 	/*
488 	 *	Except for the text and data relocation sizes,
489 	 *	calculate the final values for the header
490 	 *
491 	 *	Write out the initial copy; we to come
492 	 *	back later and patch up a_trsize and a_drsize,
493 	 *	and overwrite this first version of the header.
494 	 */
495 	hdr.a_magic = MAGIC;
496 	hdr.a_text = tsize;
497 	hdr.a_data = dsize;
498 	hdr.a_bss -= dsize;
499 	hdr.a_syms = sizesymtab();	/* Does not include string pool length */
500 	hdr.a_entry = 0;
501 	hdr.a_trsize = 0;
502 	hdr.a_drsize = 0;
503 
504 	bwrite((char *)&hdr, sizeof(hdr), usefile[0]);
505 }
506 
507 i_pass2()
508 {
509 	if (useVM == 0) {
510 		fclose(tmpfil);
511 		tmpfil = fopen(tmpn1, "r");
512 		if (tmpfil==NULL) {
513 		   yyerror("Bad pass 2 temporary file for reading %s", tmpn1);
514 		   delexit();
515 		}
516 	}
517 }
518 
519 pass2()
520 {
521 #ifdef DEBUG
522 	if (debug)
523 		printf("\n\n\n\t\tPASS 2\n\n\n\n");
524 #endif DEBUG
525 	passno = 2;
526 	lineno = 1;
527 	dotp = &usedot[0];
528 	txtfil = usefile[0];	/* already opened (always!) */
529 	relfil = 0;		/* outrel takes care of the rest */
530 	initoutrel();
531 
532 	inittmpfile();
533 
534 	yyparse();
535 
536 	closetmpfile();
537 }
538 
539 fillsegments()
540 {
541 	int	locindex;
542 	/*
543 	 *	Round text and data segments to FW by appending zeros
544 	 */
545 	for (locindex = 0; locindex < NLOC + NLOC; locindex++) {
546 		if (usefile[locindex]) {
547 			txtfil = usefile[locindex];
548 			dotp = &usedot[locindex];
549 			while (usedot[locindex].e_xvalue & FW)
550 				outb(0);
551 		}
552 	}
553 }
554 
555 reloc_syms()
556 {
557 	u_long	closerelfil();
558 	/*
559 	 *	Move the relocation information to a.out
560 	 *	a_out_off is the offset so far:
561 	 *	exec + text segments + data segments
562 	 */
563 	relocfile = (BFILE *)Calloc(1,sizeof(BFILE));
564 	bopen(relocfile, a_out_off);
565 	a_out_off += closeoutrel(relocfile);
566 
567 	hdr.a_trsize = trsize;
568 	hdr.a_drsize = drsize;
569 	if (readonlydata) {
570 		hdr.a_text += hdr.a_data;
571 		hdr.a_data = 0;
572 		hdr.a_trsize += hdr.a_drsize;
573 		hdr.a_drsize = 0;
574 	}
575 	/*
576 	 *	Output the symbol table
577 	 *	and if FLEXNAMES is set, the string pool
578 	 */
579 	symwrite(relocfile);
580 }
581 
582 fix_a_out()
583 {
584 	if (lseek(a_out_file->_file, 0L, 0) < 0)
585 		yyerror("Reposition for header rewrite fails");
586 	if (write(a_out_file->_file, (char *)&hdr, sizeof (struct exec)) < 0)
587 		yyerror("Rewrite of header fails");
588 }
589 
590 delexit()
591 {
592 	delete();
593 	if (passno == 2){
594 		unlink(outfile);
595 	}
596 	exit(1);
597 }
598 
599 delete()
600 {
601 	if (useVM == 0 || tmpn1[0])
602 		unlink(tmpn1);
603 }
604 
605 sawabort()
606 {
607 	char	*fillinbuffer();
608 	while (fillinbuffer() != (char *)0)
609 		continue;
610 	delete();
611 	exit(1);	/*although the previous pass will also exit non zero*/
612 }
613 
614 panic(fmt, a1, a2, a3, a4)
615 	char	*fmt;
616 	/*VARARGS 1*/
617 {
618 	yyerror("Assembler panic: bad internal data structure.");
619 	yyerror(fmt, a1, a2, a3, a4);
620 	delete();
621 	abort();
622 }
623