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