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