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