1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)object.c 1.14 10/22/83"; 4 5 static char rcsid[] = "$Header: object.c,v 1.4 84/03/27 10:22:25 linton Exp $"; 6 7 /* 8 * Object code interface, mainly for extraction of symbolic information. 9 */ 10 11 #include "defs.h" 12 #include "object.h" 13 #include "stabstring.h" 14 #include "main.h" 15 #include "symbols.h" 16 #include "names.h" 17 #include "languages.h" 18 #include "mappings.h" 19 #include "lists.h" 20 #include <a.out.h> 21 #include <stab.h> 22 #include <ctype.h> 23 24 #ifndef public 25 26 struct { 27 unsigned int stringsize; /* size of the dumped string table */ 28 unsigned int nsyms; /* number of symbols */ 29 unsigned int nfiles; /* number of files */ 30 unsigned int nlines; /* number of lines */ 31 } nlhdr; 32 33 #include "languages.h" 34 #include "symbols.h" 35 36 #endif 37 38 #ifndef N_MOD2 39 # define N_MOD2 0x50 40 #endif 41 42 public String objname = "a.out"; 43 public integer objsize; 44 45 public Language curlang; 46 public Symbol curmodule; 47 public Symbol curparam; 48 public Symbol curcomm; 49 public Symbol commchain; 50 51 private char *stringtab; 52 private struct nlist *curnp; 53 private Boolean warned; 54 private Boolean strip_ = false; 55 56 private Filetab *filep; 57 private Linetab *linep, *prevlinep; 58 59 public String curfilename () 60 { 61 return ((filep-1)->filename); 62 } 63 64 /* 65 * Blocks are figured out on the fly while reading the symbol table. 66 */ 67 68 #define MAXBLKDEPTH 25 69 70 public Symbol curblock; 71 72 private Symbol blkstack[MAXBLKDEPTH]; 73 private integer curlevel; 74 private integer bnum, nesting; 75 private Address addrstk[MAXBLKDEPTH]; 76 77 public pushBlock (b) 78 Symbol b; 79 { 80 if (curlevel >= MAXBLKDEPTH) { 81 fatal("nesting depth too large (%d)", curlevel); 82 } 83 blkstack[curlevel] = curblock; 84 ++curlevel; 85 curblock = b; 86 if (traceblocks) { 87 printf("entering block %s\n", symname(b)); 88 } 89 } 90 91 public enterblock (b) 92 Symbol b; 93 { 94 if (curblock == nil) { 95 b->level = 1; 96 } else { 97 b->level = curblock->level + 1; 98 } 99 b->block = curblock; 100 pushBlock(b); 101 } 102 103 public exitblock () 104 { 105 if (curblock->class == FUNC or curblock->class == PROC) { 106 if (prevlinep != linep) { 107 curblock->symvalue.funcv.src = true; 108 } 109 } 110 if (curlevel <= 0) { 111 panic("nesting depth underflow (%d)", curlevel); 112 } 113 --curlevel; 114 if (traceblocks) { 115 printf("exiting block %s\n", symname(curblock)); 116 } 117 curblock = blkstack[curlevel]; 118 } 119 120 /* 121 * Enter a source line or file name reference into the appropriate table. 122 * Expanded inline to reduce procedure calls. 123 * 124 * private enterline (linenumber, address) 125 * Lineno linenumber; 126 * Address address; 127 * ... 128 */ 129 130 #define enterline(linenumber, address) \ 131 { \ 132 register Linetab *lp; \ 133 \ 134 lp = linep - 1; \ 135 if (linenumber != lp->line) { \ 136 if (address != lp->addr) { \ 137 ++lp; \ 138 } \ 139 lp->line = linenumber; \ 140 lp->addr = address; \ 141 linep = lp + 1; \ 142 } \ 143 } 144 145 /* 146 * Read in the namelist from the obj file. 147 * 148 * Reads and seeks are used instead of fread's and fseek's 149 * for efficiency sake; there's a lot of data being read here. 150 */ 151 152 public readobj (file) 153 String file; 154 { 155 Fileid f; 156 struct exec hdr; 157 struct nlist nlist; 158 159 f = open(file, 0); 160 if (f < 0) { 161 fatal("can't open %s", file); 162 } 163 read(f, &hdr, sizeof(hdr)); 164 objsize = hdr.a_text; 165 nlhdr.nsyms = hdr.a_syms / sizeof(nlist); 166 nlhdr.nfiles = nlhdr.nsyms; 167 nlhdr.nlines = nlhdr.nsyms; 168 if (nlhdr.nsyms > 0) { 169 lseek(f, (long) N_STROFF(hdr), 0); 170 read(f, &(nlhdr.stringsize), sizeof(nlhdr.stringsize)); 171 nlhdr.stringsize -= 4; 172 stringtab = newarr(char, nlhdr.stringsize); 173 read(f, stringtab, nlhdr.stringsize); 174 allocmaps(nlhdr.nfiles, nlhdr.nlines); 175 lseek(f, (long) N_SYMOFF(hdr), 0); 176 readsyms(f); 177 ordfunctab(); 178 setnlines(); 179 setnfiles(); 180 } 181 close(f); 182 } 183 184 /* 185 * Read in symbols from object file. 186 */ 187 188 private readsyms (f) 189 Fileid f; 190 { 191 struct nlist *namelist; 192 register struct nlist *np, *ub; 193 register String name; 194 register Boolean afterlg; 195 integer index; 196 char *lastchar; 197 198 initsyms(); 199 namelist = newarr(struct nlist, nlhdr.nsyms); 200 read(f, namelist, nlhdr.nsyms * sizeof(struct nlist)); 201 afterlg = false; 202 ub = &namelist[nlhdr.nsyms]; 203 curnp = &namelist[0]; 204 np = curnp; 205 while (np < ub) { 206 index = np->n_un.n_strx; 207 if (index != 0) { 208 name = &stringtab[index - 4]; 209 /* 210 * If the program contains any .f files a trailing _ is stripped 211 * from the name on the assumption it was added by the compiler. 212 * This only affects names that follow the sdb N_SO entry with 213 * the .f name. 214 */ 215 if (strip_ and name[0] != '\0' ) { 216 lastchar = &name[strlen(name) - 1]; 217 if (*lastchar == '_') { 218 *lastchar = '\0'; 219 } 220 } 221 } else { 222 name = nil; 223 } 224 225 /* 226 * Assumptions: 227 * not an N_STAB ==> name != nil 228 * name[0] == '-' ==> name == "-lg" 229 * name[0] != '_' ==> filename or invisible 230 * 231 * The "-lg" signals the beginning of global loader symbols. 232 * 233 */ 234 if ((np->n_type&N_STAB) != 0) { 235 enter_nl(name, np); 236 } else if (name[0] == '-') { 237 afterlg = true; 238 if (curblock->class != PROG) { 239 exitblock(); 240 if (curblock->class != PROG) { 241 exitblock(); 242 } 243 } 244 enterline(0, (linep-1)->addr + 1); 245 } else if (afterlg) { 246 if (name[0] == '_') { 247 check_global(&name[1], np); 248 } 249 } else if (name[0] == '_') { 250 check_local(&name[1], np); 251 } else if ((np->n_type&N_TEXT) == N_TEXT) { 252 check_filename(name); 253 } 254 ++curnp; 255 np = curnp; 256 } 257 if (not afterlg) { 258 fatal("not linked for debugging, use \"cc -g ...\""); 259 } 260 dispose(namelist); 261 } 262 263 /* 264 * Get a continuation entry from the name list. 265 * Return the beginning of the name. 266 */ 267 268 public String getcont () 269 { 270 register integer index; 271 register String name; 272 273 ++curnp; 274 index = curnp->n_un.n_strx; 275 if (index == 0) { 276 panic("continuation followed by empty stab"); 277 } 278 name = &stringtab[index - 4]; 279 return name; 280 } 281 282 /* 283 * Initialize symbol information. 284 */ 285 286 private initsyms () 287 { 288 curblock = nil; 289 curlevel = 0; 290 nesting = 0; 291 program = insert(identname("", true)); 292 program->class = PROG; 293 program->symvalue.funcv.beginaddr = 0; 294 program->symvalue.funcv.inline = false; 295 newfunc(program, codeloc(program)); 296 findbeginning(program); 297 enterblock(program); 298 curmodule = program; 299 t_boolean = maketype("$boolean", 0L, 1L); 300 t_int = maketype("$integer", 0x80000000L, 0x7fffffffL); 301 t_char = maketype("$char", 0L, 255L); 302 t_real = maketype("$real", 8L, 0L); 303 t_nil = maketype("$nil", 0L, 0L); 304 t_open = maketype("integer", 0L, -1L); 305 } 306 307 /* 308 * Free all the object file information that's being stored. 309 */ 310 311 public objfree () 312 { 313 symbol_free(); 314 keywords_free(); 315 names_free(); 316 dispose(stringtab); 317 clrfunctab(); 318 } 319 320 /* 321 * Enter a namelist entry. 322 */ 323 324 private enter_nl (name, np) 325 String name; 326 register struct nlist *np; 327 { 328 register Symbol s; 329 register Name n; 330 331 s = nil; 332 switch (np->n_type) { 333 /* 334 * Build a symbol for the FORTRAN common area. All GSYMS that follow 335 * will be chained in a list with the head kept in common.offset, and 336 * the tail in common.chain. 337 */ 338 case N_BCOMM: 339 if (curcomm) { 340 curcomm->symvalue.common.chain = commchain; 341 } 342 n = identname(name, true); 343 curcomm = lookup(n); 344 if (curcomm == nil) { 345 curcomm = insert(n); 346 curcomm->class = COMMON; 347 curcomm->block = curblock; 348 curcomm->level = program->level; 349 curcomm->symvalue.common.chain = nil; 350 } 351 commchain = curcomm->symvalue.common.chain; 352 break; 353 354 case N_ECOMM: 355 if (curcomm) { 356 curcomm->symvalue.common.chain = commchain; 357 curcomm = nil; 358 } 359 break; 360 361 case N_LBRAC: 362 ++nesting; 363 addrstk[nesting] = (linep - 1)->addr; 364 break; 365 366 case N_RBRAC: 367 --nesting; 368 if (addrstk[nesting] == NOADDR) { 369 exitblock(); 370 newfunc(curblock, (linep - 1)->addr); 371 addrstk[nesting] = (linep - 1)->addr; 372 } 373 break; 374 375 case N_SLINE: 376 enterline((Lineno) np->n_desc, (Address) np->n_value); 377 break; 378 379 /* 380 * Source files. 381 */ 382 case N_SO: 383 n = identname(name, true); 384 enterSourceModule(n, (Address) np->n_value); 385 break; 386 387 /* 388 * Textually included files. 389 */ 390 case N_SOL: 391 enterfile(name, (Address) np->n_value); 392 break; 393 394 /* 395 * These symbols are assumed to have non-nil names. 396 */ 397 case N_GSYM: 398 case N_FUN: 399 case N_STSYM: 400 case N_LCSYM: 401 case N_RSYM: 402 case N_PSYM: 403 case N_LSYM: 404 case N_SSYM: 405 case N_LENG: 406 if (index(name, ':') == nil) { 407 if (not warned) { 408 warned = true; 409 warning("old style symbol information found in \"%s\"", 410 curfilename()); 411 } 412 } else { 413 entersym(name, np); 414 } 415 break; 416 417 case N_PC: 418 case N_MOD2: 419 break; 420 421 default: 422 printf("warning: stab entry unrecognized: "); 423 if (name != nil) { 424 printf("name %s,", name); 425 } 426 printf("ntype %2x, desc %x, value %x'\n", 427 np->n_type, np->n_desc, np->n_value); 428 break; 429 } 430 } 431 432 /* 433 * Try to find the symbol that is referred to by the given name. 434 * Since it's an external, we may want to follow a level of indirection. 435 */ 436 437 private Symbol findsym (n) 438 Name n; 439 { 440 register Symbol r, s; 441 442 find(s, n) where 443 s->level == program->level and 444 (s->class == EXTREF or s->class == VAR or 445 s->class == PROC or s->class == FUNC) 446 endfind(s); 447 if (s != nil and s->class == EXTREF) { 448 r = s->symvalue.extref; 449 delete(s); 450 } else { 451 r = s; 452 } 453 return r; 454 } 455 456 /* 457 * Check to see if a global _name is already in the symbol table, 458 * if not then insert it. 459 */ 460 461 private check_global (name, np) 462 String name; 463 register struct nlist *np; 464 { 465 register Name n; 466 register Symbol t, u; 467 468 if (not streq(name, "end")) { 469 n = identname(name, true); 470 if ((np->n_type&N_TYPE) == N_TEXT) { 471 t = findsym(n); 472 if (t == nil) { 473 t = insert(n); 474 t->language = findlanguage(".s"); 475 t->class = FUNC; 476 t->type = t_int; 477 t->block = curblock; 478 t->level = program->level; 479 t->symvalue.funcv.src = false; 480 t->symvalue.funcv.inline = false; 481 } 482 if (t->class == VAR) { 483 t->symvalue.offset = np->n_value; 484 } else { 485 t->symvalue.funcv.beginaddr = np->n_value; 486 newfunc(t, codeloc(t)); 487 findbeginning(t); 488 } 489 } else if ((np->n_type&N_TYPE) == N_BSS) { 490 find(t, n) where 491 t->class == COMMON 492 endfind(t); 493 if (t != nil) { 494 u = (Symbol) t->symvalue.common.offset; 495 while (u != nil) { 496 u->symvalue.offset = u->symvalue.common.offset+np->n_value; 497 u = u->symvalue.common.chain; 498 } 499 } else { 500 check_var(np, n); 501 } 502 } else { 503 check_var(np, n); 504 } 505 } 506 } 507 508 /* 509 * Check to see if a namelist entry refers to a variable. 510 * If not, create a variable for the entry. In any case, 511 * set the offset of the variable according to the value field 512 * in the entry. 513 */ 514 515 private check_var (np, n) 516 struct nlist *np; 517 register Name n; 518 { 519 register Symbol t; 520 521 t = findsym(n); 522 if (t == nil) { 523 t = insert(n); 524 t->language = findlanguage(".s"); 525 t->class = VAR; 526 t->type = t_int; 527 t->level = program->level; 528 t->block = curblock; 529 } 530 t->symvalue.offset = np->n_value; 531 } 532 533 /* 534 * Check to see if a local _name is known in the current scope. 535 * If not then enter it. 536 */ 537 538 private check_local (name, np) 539 String name; 540 register struct nlist *np; 541 { 542 register Name n; 543 register Symbol t, cur; 544 545 n = identname(name, true); 546 cur = ((np->n_type&N_TYPE) == N_TEXT) ? curmodule : curblock; 547 find(t, n) where t->block == cur endfind(t); 548 if (t == nil) { 549 t = insert(n); 550 t->language = findlanguage(".s"); 551 t->type = t_int; 552 t->block = cur; 553 t->level = cur->level; 554 if ((np->n_type&N_TYPE) == N_TEXT) { 555 t->class = FUNC; 556 t->symvalue.funcv.src = false; 557 t->symvalue.funcv.inline = false; 558 t->symvalue.funcv.beginaddr = np->n_value; 559 newfunc(t, codeloc(t)); 560 findbeginning(t); 561 } else { 562 t->class = VAR; 563 t->symvalue.offset = np->n_value; 564 } 565 } 566 } 567 568 /* 569 * Check to see if a symbol corresponds to a object file name. 570 * For some reason these are listed as in the text segment. 571 */ 572 573 private check_filename (name) 574 String name; 575 { 576 register String mname; 577 register integer i; 578 register Symbol s; 579 580 mname = strdup(name); 581 i = strlen(mname) - 2; 582 if (i >= 0 and mname[i] == '.' and mname[i+1] == 'o') { 583 mname[i] = '\0'; 584 --i; 585 while (mname[i] != '/' and i >= 0) { 586 --i; 587 } 588 s = insert(identname(&mname[i+1], true)); 589 s->language = findlanguage(".s"); 590 s->class = MODULE; 591 s->symvalue.funcv.beginaddr = 0; 592 findbeginning(s); 593 if (curblock->class != PROG) { 594 exitblock(); 595 if (curblock->class != PROG) { 596 exitblock(); 597 } 598 } 599 enterblock(s); 600 curmodule = s; 601 } 602 } 603 604 /* 605 * Check to see if a symbol is about to be defined within an unnamed block. 606 * If this happens, we create a procedure for the unnamed block, make it 607 * "inline" so that tracebacks don't associate an activation record with it, 608 * and enter it into the function table so that it will be detected 609 * by "whatblock". 610 */ 611 612 public chkUnnamedBlock () 613 { 614 register Symbol s; 615 static int bnum = 0; 616 char buf[100]; 617 Address startaddr; 618 619 if (nesting > 0 and addrstk[nesting] != NOADDR) { 620 startaddr = (linep - 1)->addr; 621 ++bnum; 622 sprintf(buf, "$b%d", bnum); 623 s = insert(identname(buf, false)); 624 s->language = curlang; 625 s->class = PROC; 626 s->symvalue.funcv.src = false; 627 s->symvalue.funcv.inline = true; 628 s->symvalue.funcv.beginaddr = startaddr; 629 enterblock(s); 630 newfunc(s, startaddr); 631 addrstk[nesting] = NOADDR; 632 } 633 } 634 635 /* 636 * Compilation unit. C associates scope with filenames 637 * so we treat them as "modules". The filename without 638 * the suffix is used for the module name. 639 * 640 * Because there is no explicit "end-of-block" mark in 641 * the object file, we must exit blocks for the current 642 * procedure and module. 643 */ 644 645 private enterSourceModule (n, addr) 646 Name n; 647 Address addr; 648 { 649 register Symbol s; 650 Name nn; 651 String mname, suffix; 652 653 mname = strdup(ident(n)); 654 if (rindex(mname, '/') != nil) { 655 mname = rindex(mname, '/') + 1; 656 } 657 suffix = rindex(mname, '.'); 658 curlang = findlanguage(suffix); 659 if (curlang == findlanguage(".f")) { 660 strip_ = true; 661 } 662 if (suffix != nil) { 663 *suffix = '\0'; 664 } 665 if (not (*language_op(curlang, L_HASMODULES))()) { 666 if (curblock->class != PROG) { 667 exitblock(); 668 if (curblock->class != PROG) { 669 exitblock(); 670 } 671 } 672 nn = identname(mname, true); 673 if (curmodule == nil or curmodule->name != nn) { 674 s = insert(nn); 675 s->class = MODULE; 676 s->symvalue.funcv.beginaddr = 0; 677 findbeginning(s); 678 } else { 679 s = curmodule; 680 } 681 s->language = curlang; 682 enterblock(s); 683 curmodule = s; 684 } 685 if (program->language == nil) { 686 program->language = curlang; 687 } 688 warned = false; 689 enterfile(ident(n), addr); 690 initTypeTable(); 691 } 692 693 /* 694 * Allocate file and line tables and initialize indices. 695 */ 696 697 private allocmaps (nf, nl) 698 integer nf, nl; 699 { 700 if (filetab != nil) { 701 dispose(filetab); 702 } 703 if (linetab != nil) { 704 dispose(linetab); 705 } 706 filetab = newarr(Filetab, nf); 707 linetab = newarr(Linetab, nl); 708 filep = filetab; 709 linep = linetab; 710 } 711 712 /* 713 * Add a file to the file table. 714 * 715 * If the new address is the same as the previous file address 716 * this routine used to not enter the file, but this caused some 717 * problems so it has been removed. It's not clear that this in 718 * turn may not also cause a problem. 719 */ 720 721 private enterfile (filename, addr) 722 String filename; 723 Address addr; 724 { 725 filep->addr = addr; 726 filep->filename = filename; 727 filep->lineindex = linep - linetab; 728 ++filep; 729 } 730 731 /* 732 * Since we only estimated the number of lines (and it was a poor 733 * estimation) and since we need to know the exact number of lines 734 * to do a binary search, we set it when we're done. 735 */ 736 737 private setnlines () 738 { 739 nlhdr.nlines = linep - linetab; 740 } 741 742 /* 743 * Similarly for nfiles ... 744 */ 745 746 private setnfiles () 747 { 748 nlhdr.nfiles = filep - filetab; 749 setsource(filetab[0].filename); 750 } 751