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