1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)object.c 1.16 (Berkeley) 03/01/85"; 4 5 static char rcsid[] = "$Header: object.c,v 1.6 84/12/26 10:40:51 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 /* 92 * Change the current block with saving the previous one, 93 * since it is assumed that the symbol for the current one is to be deleted. 94 */ 95 96 public changeBlock (b) 97 Symbol b; 98 { 99 curblock = b; 100 } 101 102 public enterblock (b) 103 Symbol b; 104 { 105 if (curblock == nil) { 106 b->level = 1; 107 } else { 108 b->level = curblock->level + 1; 109 } 110 b->block = curblock; 111 pushBlock(b); 112 } 113 114 public exitblock () 115 { 116 if (curblock->class == FUNC or curblock->class == PROC) { 117 if (prevlinep != linep) { 118 curblock->symvalue.funcv.src = true; 119 } 120 } 121 if (curlevel <= 0) { 122 panic("nesting depth underflow (%d)", curlevel); 123 } 124 --curlevel; 125 if (traceblocks) { 126 printf("exiting block %s\n", symname(curblock)); 127 } 128 curblock = blkstack[curlevel]; 129 } 130 131 /* 132 * Enter a source line or file name reference into the appropriate table. 133 * Expanded inline to reduce procedure calls. 134 * 135 * private enterline (linenumber, address) 136 * Lineno linenumber; 137 * Address address; 138 * ... 139 */ 140 141 #define enterline(linenumber, address) \ 142 { \ 143 register Linetab *lp; \ 144 \ 145 lp = linep - 1; \ 146 if (linenumber != lp->line) { \ 147 if (address != lp->addr) { \ 148 ++lp; \ 149 } \ 150 lp->line = linenumber; \ 151 lp->addr = address; \ 152 linep = lp + 1; \ 153 } \ 154 } 155 156 /* 157 * Read in the namelist from the obj file. 158 * 159 * Reads and seeks are used instead of fread's and fseek's 160 * for efficiency sake; there's a lot of data being read here. 161 */ 162 163 public readobj (file) 164 String file; 165 { 166 Fileid f; 167 struct exec hdr; 168 struct nlist nlist; 169 170 f = open(file, 0); 171 if (f < 0) { 172 fatal("can't open %s", file); 173 } 174 read(f, &hdr, sizeof(hdr)); 175 if (N_BADMAG(hdr)) { 176 objsize = 0; 177 nlhdr.nsyms = 0; 178 nlhdr.nfiles = 0; 179 nlhdr.nlines = 0; 180 } else { 181 objsize = hdr.a_text; 182 nlhdr.nsyms = hdr.a_syms / sizeof(nlist); 183 nlhdr.nfiles = nlhdr.nsyms; 184 nlhdr.nlines = nlhdr.nsyms; 185 } 186 if (nlhdr.nsyms > 0) { 187 lseek(f, (long) N_STROFF(hdr), 0); 188 read(f, &(nlhdr.stringsize), sizeof(nlhdr.stringsize)); 189 nlhdr.stringsize -= 4; 190 stringtab = newarr(char, nlhdr.stringsize); 191 read(f, stringtab, nlhdr.stringsize); 192 allocmaps(nlhdr.nfiles, nlhdr.nlines); 193 lseek(f, (long) N_SYMOFF(hdr), 0); 194 readsyms(f); 195 ordfunctab(); 196 setnlines(); 197 setnfiles(); 198 } else { 199 initsyms(); 200 } 201 close(f); 202 } 203 204 /* 205 * Found the beginning of the externals in the object file 206 * (signified by the "-lg" or find an external), close the 207 * block for the last procedure. 208 */ 209 210 private foundglobals () 211 { 212 if (curblock->class != PROG) { 213 exitblock(); 214 if (curblock->class != PROG) { 215 exitblock(); 216 } 217 } 218 enterline(0, (linep-1)->addr + 1); 219 } 220 221 /* 222 * Read in symbols from object file. 223 */ 224 225 private readsyms (f) 226 Fileid f; 227 { 228 struct nlist *namelist; 229 register struct nlist *np, *ub; 230 register String name; 231 register Boolean afterlg; 232 integer index; 233 char *lastchar; 234 235 initsyms(); 236 namelist = newarr(struct nlist, nlhdr.nsyms); 237 read(f, namelist, nlhdr.nsyms * sizeof(struct nlist)); 238 afterlg = false; 239 ub = &namelist[nlhdr.nsyms]; 240 curnp = &namelist[0]; 241 np = curnp; 242 while (np < ub) { 243 index = np->n_un.n_strx; 244 if (index != 0) { 245 name = &stringtab[index - 4]; 246 /* 247 * If the program contains any .f files a trailing _ is stripped 248 * from the name on the assumption it was added by the compiler. 249 * This only affects names that follow the sdb N_SO entry with 250 * the .f name. 251 */ 252 if (strip_ and name[0] != '\0' ) { 253 lastchar = &name[strlen(name) - 1]; 254 if (*lastchar == '_') { 255 *lastchar = '\0'; 256 } 257 } 258 } else { 259 name = nil; 260 } 261 262 /* 263 * Assumptions: 264 * not an N_STAB ==> name != nil 265 * name[0] == '-' ==> name == "-lg" 266 * name[0] != '_' ==> filename or invisible 267 * 268 * The "-lg" signals the beginning of global loader symbols. 269 * 270 */ 271 if ((np->n_type&N_STAB) != 0) { 272 enter_nl(name, np); 273 } else if (name[0] == '-') { 274 afterlg = true; 275 foundglobals(); 276 } else if (afterlg) { 277 check_global(name, np); 278 } else if ((np->n_type&N_EXT) == N_EXT) { 279 afterlg = true; 280 foundglobals(); 281 check_global(name, np); 282 } else if (name[0] == '_') { 283 check_local(&name[1], np); 284 } else if ((np->n_type&N_TEXT) == N_TEXT) { 285 check_filename(name); 286 } 287 ++curnp; 288 np = curnp; 289 } 290 dispose(namelist); 291 } 292 293 /* 294 * Get a continuation entry from the name list. 295 * Return the beginning of the name. 296 */ 297 298 public String getcont () 299 { 300 register integer index; 301 register String name; 302 303 ++curnp; 304 index = curnp->n_un.n_strx; 305 if (index == 0) { 306 panic("continuation followed by empty stab"); 307 } 308 name = &stringtab[index - 4]; 309 return name; 310 } 311 312 /* 313 * Initialize symbol information. 314 */ 315 316 private initsyms () 317 { 318 curblock = nil; 319 curlevel = 0; 320 nesting = 0; 321 program = insert(identname("", true)); 322 program->class = PROG; 323 program->symvalue.funcv.beginaddr = 0; 324 program->symvalue.funcv.inline = false; 325 newfunc(program, codeloc(program)); 326 findbeginning(program); 327 enterblock(program); 328 curmodule = program; 329 } 330 331 /* 332 * Free all the object file information that's being stored. 333 */ 334 335 public objfree () 336 { 337 symbol_free(); 338 /* keywords_free(); */ 339 /* names_free(); */ 340 /* dispose(stringtab); */ 341 clrfunctab(); 342 } 343 344 /* 345 * Enter a namelist entry. 346 */ 347 348 private enter_nl (name, np) 349 String name; 350 register struct nlist *np; 351 { 352 register Symbol s; 353 register Name n; 354 355 s = nil; 356 switch (np->n_type) { 357 /* 358 * Build a symbol for the FORTRAN common area. All GSYMS that follow 359 * will be chained in a list with the head kept in common.offset, and 360 * the tail in common.chain. 361 */ 362 case N_BCOMM: 363 if (curcomm) { 364 curcomm->symvalue.common.chain = commchain; 365 } 366 n = identname(name, true); 367 curcomm = lookup(n); 368 if (curcomm == nil) { 369 curcomm = insert(n); 370 curcomm->class = COMMON; 371 curcomm->block = curblock; 372 curcomm->level = program->level; 373 curcomm->symvalue.common.chain = nil; 374 } 375 commchain = curcomm->symvalue.common.chain; 376 break; 377 378 case N_ECOMM: 379 if (curcomm) { 380 curcomm->symvalue.common.chain = commchain; 381 curcomm = nil; 382 } 383 break; 384 385 case N_LBRAC: 386 ++nesting; 387 addrstk[nesting] = (linep - 1)->addr; 388 break; 389 390 case N_RBRAC: 391 --nesting; 392 if (addrstk[nesting] == NOADDR) { 393 exitblock(); 394 newfunc(curblock, (linep - 1)->addr); 395 addrstk[nesting] = (linep - 1)->addr; 396 } 397 break; 398 399 case N_SLINE: 400 enterline((Lineno) np->n_desc, (Address) np->n_value); 401 break; 402 403 /* 404 * Source files. 405 */ 406 case N_SO: 407 n = identname(name, true); 408 enterSourceModule(n, (Address) np->n_value); 409 break; 410 411 /* 412 * Textually included files. 413 */ 414 case N_SOL: 415 enterfile(name, (Address) np->n_value); 416 break; 417 418 /* 419 * These symbols are assumed to have non-nil names. 420 */ 421 case N_GSYM: 422 case N_FUN: 423 case N_STSYM: 424 case N_LCSYM: 425 case N_RSYM: 426 case N_PSYM: 427 case N_LSYM: 428 case N_SSYM: 429 case N_LENG: 430 if (index(name, ':') == nil) { 431 if (not warned) { 432 warned = true; 433 warning("old style symbol information found in \"%s\"", 434 curfilename()); 435 } 436 } else { 437 entersym(name, np); 438 } 439 break; 440 441 case N_PC: 442 case N_MOD2: 443 break; 444 445 default: 446 printf("warning: stab entry unrecognized: "); 447 if (name != nil) { 448 printf("name %s,", name); 449 } 450 printf("ntype %2x, desc %x, value %x'\n", 451 np->n_type, np->n_desc, np->n_value); 452 break; 453 } 454 } 455 456 /* 457 * Try to find the symbol that is referred to by the given name. Since it's 458 * an external, we need to follow a level or two of indirection. 459 */ 460 461 private Symbol findsym (n, var_isextref) 462 Name n; 463 boolean *var_isextref; 464 { 465 register Symbol r, s; 466 467 *var_isextref = false; 468 find(s, n) where 469 ( 470 s->level == program->level and ( 471 s->class == EXTREF or s->class == VAR or 472 s->class == PROC or s->class == FUNC 473 ) 474 ) or ( 475 s->block == program and s->class == MODULE 476 ) 477 endfind(s); 478 if (s == nil) { 479 r = nil; 480 } else if (s->class == EXTREF) { 481 *var_isextref = true; 482 r = s->symvalue.extref; 483 delete(s); 484 485 /* 486 * Now check for another level of indirection that could come from 487 * a forward reference in procedure nesting information. In this case 488 * the symbol has already been deleted. 489 */ 490 if (r != nil and r->class == EXTREF) { 491 r = r->symvalue.extref; 492 } 493 /* 494 } else if (s->class == MODULE) { 495 s->class = FUNC; 496 s->level = program->level; 497 r = s; 498 */ 499 } else { 500 r = s; 501 } 502 return r; 503 } 504 505 /* 506 * Create a symbol for a text symbol with no source information. 507 * We treat it as an assembly language function. 508 */ 509 510 private Symbol deffunc (n) 511 Name n; 512 { 513 Symbol f; 514 515 f = insert(n); 516 f->language = findlanguage(".s"); 517 f->class = FUNC; 518 f->type = t_int; 519 f->block = curblock; 520 f->level = program->level; 521 f->symvalue.funcv.src = false; 522 f->symvalue.funcv.inline = false; 523 return f; 524 } 525 526 /* 527 * Create a symbol for a data or bss symbol with no source information. 528 * We treat it as an assembly language variable. 529 */ 530 531 private Symbol defvar (n) 532 Name n; 533 { 534 Symbol v; 535 536 v = insert(n); 537 v->language = findlanguage(".s"); 538 v->class = VAR; 539 v->type = t_int; 540 v->level = program->level; 541 v->block = curblock; 542 return v; 543 } 544 545 /* 546 * Update a symbol entry with a text address. 547 */ 548 549 private updateTextSym (s, name, addr) 550 Symbol s; 551 char *name; 552 Address addr; 553 { 554 if (s->class == VAR) { 555 s->symvalue.offset = addr; 556 } else { 557 s->symvalue.funcv.beginaddr = addr; 558 if (name[0] == '_') { 559 newfunc(s, codeloc(s)); 560 findbeginning(s); 561 } 562 } 563 } 564 565 /* 566 * Check to see if a global _name is already in the symbol table, 567 * if not then insert it. 568 */ 569 570 private check_global (name, np) 571 String name; 572 register struct nlist *np; 573 { 574 register Name n; 575 register Symbol t, u; 576 char buf[4096]; 577 boolean isextref; 578 integer count; 579 580 if (not streq(name, "_end")) { 581 if (name[0] == '_') { 582 n = identname(&name[1], true); 583 } else { 584 n = identname(name, true); 585 if (lookup(n) != nil) { 586 sprintf(buf, "$%s", name); 587 n = identname(buf, false); 588 } 589 } 590 if ((np->n_type&N_TYPE) == N_TEXT) { 591 count = 0; 592 t = findsym(n, &isextref); 593 while (isextref) { 594 ++count; 595 updateTextSym(t, name, np->n_value); 596 t = findsym(n, &isextref); 597 } 598 if (count == 0) { 599 if (t == nil) { 600 t = deffunc(n); 601 updateTextSym(t, name, np->n_value); 602 if (tracesyms) { 603 printdecl(t); 604 } 605 } else { 606 if (t->class == MODULE) { 607 u = t; 608 t = deffunc(n); 609 t->block = u; 610 if (tracesyms) { 611 printdecl(t); 612 } 613 } 614 updateTextSym(t, name, np->n_value); 615 } 616 } 617 } else if ((np->n_type&N_TYPE) == N_BSS) { 618 find(t, n) where 619 t->class == COMMON 620 endfind(t); 621 if (t != nil) { 622 u = (Symbol) t->symvalue.common.offset; 623 while (u != nil) { 624 u->symvalue.offset = u->symvalue.common.offset+np->n_value; 625 u = u->symvalue.common.chain; 626 } 627 } else { 628 check_var(np, n); 629 } 630 } else { 631 check_var(np, n); 632 } 633 } 634 } 635 636 /* 637 * Check to see if a namelist entry refers to a variable. 638 * If not, create a variable for the entry. In any case, 639 * set the offset of the variable according to the value field 640 * in the entry. 641 * 642 * If the external name has been referred to by several other symbols, 643 * we must update each of them. 644 */ 645 646 private check_var (np, n) 647 struct nlist *np; 648 register Name n; 649 { 650 register Symbol t, u, next; 651 Symbol conflict; 652 653 t = lookup(n); 654 if (t == nil) { 655 t = defvar(n); 656 t->symvalue.offset = np->n_value; 657 if (tracesyms) { 658 printdecl(t); 659 } 660 } else { 661 conflict = nil; 662 do { 663 next = t->next_sym; 664 if (t->name == n) { 665 if (t->class == MODULE and t->block == program) { 666 conflict = t; 667 } else if (t->class == EXTREF and t->level == program->level) { 668 u = t->symvalue.extref; 669 while (u != nil and u->class == EXTREF) { 670 u = u->symvalue.extref; 671 } 672 u->symvalue.offset = np->n_value; 673 delete(t); 674 } else if (t->level == program->level and 675 (t->class == VAR or t->class == PROC or t->class == FUNC) 676 ) { 677 conflict = nil; 678 t->symvalue.offset = np->n_value; 679 } 680 } 681 t = next; 682 } while (t != nil); 683 if (conflict != nil) { 684 u = defvar(n); 685 u->block = conflict; 686 u->symvalue.offset = np->n_value; 687 } 688 } 689 } 690 691 /* 692 * Check to see if a local _name is known in the current scope. 693 * If not then enter it. 694 */ 695 696 private check_local (name, np) 697 String name; 698 register struct nlist *np; 699 { 700 register Name n; 701 register Symbol t, cur; 702 703 n = identname(name, true); 704 cur = ((np->n_type&N_TYPE) == N_TEXT) ? curmodule : curblock; 705 find(t, n) where t->block == cur endfind(t); 706 if (t == nil) { 707 t = insert(n); 708 t->language = findlanguage(".s"); 709 t->type = t_int; 710 t->block = cur; 711 t->level = cur->level; 712 if ((np->n_type&N_TYPE) == N_TEXT) { 713 t->class = FUNC; 714 t->symvalue.funcv.src = false; 715 t->symvalue.funcv.inline = false; 716 t->symvalue.funcv.beginaddr = np->n_value; 717 newfunc(t, codeloc(t)); 718 findbeginning(t); 719 } else { 720 t->class = VAR; 721 t->symvalue.offset = np->n_value; 722 } 723 } 724 } 725 726 /* 727 * Check to see if a symbol corresponds to a object file name. 728 * For some reason these are listed as in the text segment. 729 */ 730 731 private check_filename (name) 732 String name; 733 { 734 register String mname; 735 register integer i; 736 Name n; 737 Symbol s; 738 739 mname = strdup(name); 740 i = strlen(mname) - 2; 741 if (i >= 0 and mname[i] == '.' and mname[i+1] == 'o') { 742 mname[i] = '\0'; 743 --i; 744 while (mname[i] != '/' and i >= 0) { 745 --i; 746 } 747 n = identname(&mname[i+1], true); 748 find(s, n) where s->block == program and s->class == MODULE endfind(s); 749 if (s == nil) { 750 s = insert(n); 751 s->language = findlanguage(".s"); 752 s->class = MODULE; 753 s->symvalue.funcv.beginaddr = 0; 754 findbeginning(s); 755 } 756 if (curblock->class != PROG) { 757 exitblock(); 758 if (curblock->class != PROG) { 759 exitblock(); 760 } 761 } 762 enterblock(s); 763 curmodule = s; 764 } 765 } 766 767 /* 768 * Check to see if a symbol is about to be defined within an unnamed block. 769 * If this happens, we create a procedure for the unnamed block, make it 770 * "inline" so that tracebacks don't associate an activation record with it, 771 * and enter it into the function table so that it will be detected 772 * by "whatblock". 773 */ 774 775 public chkUnnamedBlock () 776 { 777 register Symbol s; 778 static int bnum = 0; 779 char buf[100]; 780 Address startaddr; 781 782 if (nesting > 0 and addrstk[nesting] != NOADDR) { 783 startaddr = (linep - 1)->addr; 784 ++bnum; 785 sprintf(buf, "$b%d", bnum); 786 s = insert(identname(buf, false)); 787 s->language = curlang; 788 s->class = PROC; 789 s->symvalue.funcv.src = false; 790 s->symvalue.funcv.inline = true; 791 s->symvalue.funcv.beginaddr = startaddr; 792 enterblock(s); 793 newfunc(s, startaddr); 794 addrstk[nesting] = NOADDR; 795 } 796 } 797 798 /* 799 * Compilation unit. C associates scope with filenames 800 * so we treat them as "modules". The filename without 801 * the suffix is used for the module name. 802 * 803 * Because there is no explicit "end-of-block" mark in 804 * the object file, we must exit blocks for the current 805 * procedure and module. 806 */ 807 808 private enterSourceModule (n, addr) 809 Name n; 810 Address addr; 811 { 812 register Symbol s; 813 Name nn; 814 String mname, suffix; 815 816 mname = strdup(ident(n)); 817 if (rindex(mname, '/') != nil) { 818 mname = rindex(mname, '/') + 1; 819 } 820 suffix = rindex(mname, '.'); 821 curlang = findlanguage(suffix); 822 if (curlang == findlanguage(".f")) { 823 strip_ = true; 824 } 825 if (suffix != nil) { 826 *suffix = '\0'; 827 } 828 if (not (*language_op(curlang, L_HASMODULES))()) { 829 if (curblock->class != PROG) { 830 exitblock(); 831 if (curblock->class != PROG) { 832 exitblock(); 833 } 834 } 835 nn = identname(mname, true); 836 if (curmodule == nil or curmodule->name != nn) { 837 s = insert(nn); 838 s->class = MODULE; 839 s->symvalue.funcv.beginaddr = 0; 840 findbeginning(s); 841 } else { 842 s = curmodule; 843 } 844 s->language = curlang; 845 enterblock(s); 846 curmodule = s; 847 } 848 if (program->language == nil) { 849 program->language = curlang; 850 } 851 warned = false; 852 enterfile(ident(n), addr); 853 initTypeTable(); 854 } 855 856 /* 857 * Allocate file and line tables and initialize indices. 858 */ 859 860 private allocmaps (nf, nl) 861 integer nf, nl; 862 { 863 if (filetab != nil) { 864 dispose(filetab); 865 } 866 if (linetab != nil) { 867 dispose(linetab); 868 } 869 filetab = newarr(Filetab, nf); 870 linetab = newarr(Linetab, nl); 871 filep = filetab; 872 linep = linetab; 873 } 874 875 /* 876 * Add a file to the file table. 877 * 878 * If the new address is the same as the previous file address 879 * this routine used to not enter the file, but this caused some 880 * problems so it has been removed. It's not clear that this in 881 * turn may not also cause a problem. 882 */ 883 884 private enterfile (filename, addr) 885 String filename; 886 Address addr; 887 { 888 filep->addr = addr; 889 filep->filename = filename; 890 filep->lineindex = linep - linetab; 891 ++filep; 892 } 893 894 /* 895 * Since we only estimated the number of lines (and it was a poor 896 * estimation) and since we need to know the exact number of lines 897 * to do a binary search, we set it when we're done. 898 */ 899 900 private setnlines () 901 { 902 nlhdr.nlines = linep - linetab; 903 } 904 905 /* 906 * Similarly for nfiles ... 907 */ 908 909 private setnfiles () 910 { 911 nlhdr.nfiles = filep - filetab; 912 setsource(filetab[0].filename); 913 } 914