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) 5/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: }