1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)object.c 1.4 02/16/83"; 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 (afterlg) { 182 if (name[0] == '_') { 183 check_global(&name[1], np); 184 } 185 } else if (name[0] == '_') { 186 check_local(&name[1], np); 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 case N_LENG: 349 /* 350 * Should complain out this, obviously the wrong symbol format. 351 */ 352 break; 353 354 default: 355 if (name != nil) { 356 printf("%s, ", name); 357 } 358 printf("ntype %2x, desc %x, value %x\n", 359 np->n_type, np->n_desc, np->n_value); 360 break; 361 } 362 } 363 364 /* 365 * Check to see if a global _name is already in the symbol table, 366 * if not then insert it. 367 */ 368 369 private check_global(name, np) 370 String name; 371 register struct nlist *np; 372 { 373 register Name n; 374 register Symbol t; 375 376 if (not streq(name, "end")) { 377 n = identname(name, true); 378 if ((np->n_type&N_TYPE) == N_TEXT) { 379 find(t, n) where 380 t->level == program->level and isblock(t) 381 endfind(t); 382 if (t == nil) { 383 t = insert(n); 384 t->language = findlanguage(".s"); 385 t->class = FUNC; 386 t->type = t_int; 387 t->block = curblock; 388 t->level = program->level; 389 } 390 t->symvalue.funcv.beginaddr = np->n_value; 391 newfunc(t); 392 findbeginning(t); 393 } else { 394 find(t, n) where 395 t->class == VAR and t->level == program->level 396 endfind(t); 397 if (t == nil) { 398 t = insert(n); 399 t->language = findlanguage(".s"); 400 t->class = VAR; 401 t->type = t_int; 402 t->block = curblock; 403 t->level = program->level; 404 } 405 t->symvalue.offset = np->n_value; 406 } 407 } 408 } 409 410 /* 411 * Check to see if a local _name is known in the current scope. 412 * If not then enter it. 413 */ 414 415 private check_local(name, np) 416 String name; 417 register struct nlist *np; 418 { 419 register Name n; 420 register Symbol t, cur; 421 422 n = identname(name, true); 423 cur = ((np->n_type&N_TYPE) == N_TEXT) ? curmodule : curblock; 424 find(t, n) where t->block == cur endfind(t); 425 if (t == nil) { 426 t = insert(n); 427 t->language = findlanguage(".s"); 428 t->type = t_int; 429 t->block = cur; 430 t->level = cur->level; 431 if ((np->n_type&N_TYPE) == N_TEXT) { 432 t->class = FUNC; 433 t->symvalue.funcv.beginaddr = np->n_value; 434 newfunc(t); 435 findbeginning(t); 436 } else { 437 t->class = VAR; 438 t->symvalue.offset = np->n_value; 439 } 440 } 441 } 442 443 /* 444 * Check to see if a symbol corresponds to a object file name. 445 * For some reason these are listed as in the text segment. 446 */ 447 448 private check_filename(name) 449 String name; 450 { 451 register String mname; 452 register Integer i; 453 register Symbol s; 454 455 mname = strdup(name); 456 i = strlen(mname) - 2; 457 if (i >= 0 and mname[i] == '.' and mname[i+1] == 'o') { 458 mname[i] = '\0'; 459 --i; 460 while (mname[i] != '/' and i >= 0) { 461 --i; 462 } 463 s = insert(identname(&mname[i+1], true)); 464 s->language = findlanguage(".s"); 465 s->class = MODULE; 466 if (curblock->class != PROG) { 467 exitblock(); 468 if (curblock->class != PROG) { 469 exitblock(); 470 } 471 } 472 enterblock(s); 473 curmodule = s; 474 } 475 } 476 477 /* 478 * Put an nlist into the symbol table. 479 * If it's already there just add the associated information. 480 * 481 * Type information is encoded in the name following a ":". 482 */ 483 484 private Symbol constype(); 485 private Char *curchar; 486 487 #define skipchar(ptr, ch) { \ 488 if (*ptr != ch) { \ 489 panic("expected char '%c', found char '%c'", ch, *ptr); \ 490 } \ 491 ++ptr; \ 492 } 493 494 private entersym(str, np) 495 String str; 496 struct nlist *np; 497 { 498 register Symbol s; 499 register char *p; 500 register int c; 501 register Name n; 502 register Integer i; 503 Boolean knowtype, isnew; 504 Symclass class; 505 Integer level; 506 507 p = index(str, ':'); 508 *p = '\0'; 509 c = *(p+1); 510 n = identname(str, true); 511 if (index("FfGV", c) != nil) { 512 if (c == 'F' or c == 'f') { 513 class = FUNC; 514 } else { 515 class = VAR; 516 } 517 level = (c == 'f' ? curmodule->level : program->level); 518 find(s, n) where s->level == level and s->class == class endfind(s); 519 if (s == nil) { 520 isnew = true; 521 s = insert(n); 522 } else { 523 isnew = false; 524 } 525 } else { 526 isnew = true; 527 s = insert(n); 528 } 529 530 /* 531 * Default attributes. 532 */ 533 s->language = curlang; 534 s->class = VAR; 535 s->block = curblock; 536 s->level = curlevel; 537 s->symvalue.offset = np->n_value; 538 curchar = p + 2; 539 knowtype = false; 540 switch (c) { 541 case 't': /* type name */ 542 s->class = TYPE; 543 i = getint(); 544 if (i == 0) { 545 panic("bad input on type \"%s\" at \"%s\"", symname(s), 546 curchar); 547 } else if (i >= NTYPES) { 548 panic("too many types in file \"%s\"", curfilename()); 549 } 550 /* 551 * A hack for C typedefs that don't create new types, 552 * e.g. typedef unsigned int Hashvalue; 553 */ 554 if (*curchar == '\0') { 555 s->type = typetable[i]; 556 if (s->type == nil) { 557 panic("nil type for %d", i); 558 } 559 knowtype = true; 560 } else { 561 typetable[i] = s; 562 skipchar(curchar, '='); 563 } 564 break; 565 566 case 'T': /* tag */ 567 s->class = TAG; 568 i = getint(); 569 if (i == 0) { 570 panic("bad input on tag \"%s\" at \"%s\"", symname(s), 571 curchar); 572 } else if (i >= NTYPES) { 573 panic("too many types in file \"%s\"", curfilename()); 574 } 575 if (typetable[i] != nil) { 576 typetable[i]->language = curlang; 577 typetable[i]->class = TYPE; 578 typetable[i]->type = s; 579 } else { 580 typetable[i] = s; 581 } 582 skipchar(curchar, '='); 583 break; 584 585 case 'F': /* public function */ 586 case 'f': /* private function */ 587 s->class = FUNC; 588 if (curblock->class == FUNC or curblock->class == PROC) { 589 exitblock(); 590 } 591 enterblock(s); 592 if (c == 'F') { 593 s->level = program->level; 594 isnew = false; 595 } 596 curparam = s; 597 if (isnew) { 598 s->symvalue.funcv.beginaddr = np->n_value; 599 newfunc(s); 600 findbeginning(s); 601 } 602 break; 603 604 case 'G': /* public variable */ 605 s->level = program->level; 606 break; 607 608 case 'S': /* private variable */ 609 s->level = curmodule->level; 610 s->block = curmodule; 611 break; 612 613 case 'V': /* own variable */ 614 s->level = 2; 615 break; 616 617 case 'r': /* register variable */ 618 s->level = -(s->level); 619 break; 620 621 case 'p': /* parameter variable */ 622 curparam->chain = s; 623 curparam = s; 624 break; 625 626 case 'v': /* varies parameter */ 627 s->class = REF; 628 s->symvalue.offset = np->n_value; 629 curparam->chain = s; 630 curparam = s; 631 break; 632 633 default: /* local variable */ 634 --curchar; 635 break; 636 } 637 if (not knowtype) { 638 s->type = constype(nil); 639 if (s->class == TAG) { 640 addtag(s); 641 } 642 } 643 if (tracesyms) { 644 printdecl(s); 645 fflush(stdout); 646 } 647 } 648 649 /* 650 * Construct a type out of a string encoding. 651 * 652 * The forms of the string are 653 * 654 * <number> 655 * <number>=<type> 656 * r<type>;<number>;<number> $ subrange 657 * a<type>;<type> $ array[index] of element 658 * s{<name>:<type>;<number>;<number>} $ record 659 * *<type> $ pointer 660 */ 661 662 private Symbol constype(type) 663 Symbol type; 664 { 665 register Symbol t, u; 666 register Char *p, *cur; 667 register Integer n; 668 Integer b; 669 Name name; 670 Char class; 671 672 b = curlevel; 673 if (isdigit(*curchar)) { 674 n = getint(); 675 if (n == 0) { 676 panic("bad type number at \"%s\"", curchar); 677 } else if (n >= NTYPES) { 678 panic("too many types in file \"%s\"", curfilename()); 679 } 680 if (*curchar == '=') { 681 if (typetable[n] != nil) { 682 t = typetable[n]; 683 } else { 684 t = symbol_alloc(); 685 typetable[n] = t; 686 } 687 ++curchar; 688 constype(t); 689 } else { 690 t = typetable[n]; 691 if (t == nil) { 692 t = symbol_alloc(); 693 typetable[n] = t; 694 } 695 } 696 } else { 697 if (type == nil) { 698 t = symbol_alloc(); 699 } else { 700 t = type; 701 } 702 t->language = curlang; 703 t->level = b; 704 class = *curchar++; 705 switch (class) { 706 case 'r': 707 t->class = RANGE; 708 t->type = constype(nil); 709 skipchar(curchar, ';'); 710 t->symvalue.rangev.lower = getint(); 711 skipchar(curchar, ';'); 712 t->symvalue.rangev.upper = getint(); 713 break; 714 715 case 'a': 716 t->class = ARRAY; 717 t->chain = constype(nil); 718 skipchar(curchar, ';'); 719 t->type = constype(nil); 720 break; 721 722 case 's': 723 case 'u': 724 t->class = (class == 's') ? RECORD : VARNT; 725 t->symvalue.offset = getint(); 726 u = t; 727 cur = curchar; 728 while (*cur != ';' and *cur != '\0') { 729 p = index(cur, ':'); 730 if (p == nil) { 731 panic("index(\"%s\", ':') failed", curchar); 732 } 733 *p = '\0'; 734 name = identname(cur, true); 735 u->chain = newSymbol(name, b, FIELD, nil, nil); 736 cur = p + 1; 737 u = u->chain; 738 u->language = curlang; 739 curchar = cur; 740 u->type = constype(nil); 741 skipchar(curchar, ','); 742 u->symvalue.field.offset = getint(); 743 skipchar(curchar, ','); 744 u->symvalue.field.length = getint(); 745 skipchar(curchar, ';'); 746 cur = curchar; 747 } 748 if (*cur == ';') { 749 ++cur; 750 } 751 curchar = cur; 752 break; 753 754 case 'e': 755 t->class = SCAL; 756 u = t; 757 while (*curchar != ';' and *curchar != '\0') { 758 p = index(curchar, ':'); 759 assert(p != nil); 760 *p = '\0'; 761 u->chain = insert(identname(curchar, true)); 762 curchar = p + 1; 763 u = u->chain; 764 u->language = curlang; 765 u->class = CONST; 766 u->level = b; 767 u->block = curblock; 768 u->type = t; 769 u->symvalue.iconval = getint(); 770 skipchar(curchar, ','); 771 } 772 break; 773 774 case '*': 775 t->class = PTR; 776 t->type = constype(nil); 777 break; 778 779 case 'f': 780 t->class = FUNC; 781 t->type = constype(nil); 782 break; 783 784 default: 785 badcaseval(class); 786 } 787 } 788 return t; 789 } 790 791 /* 792 * Read an integer from the current position in the type string. 793 */ 794 795 private Integer getint() 796 { 797 register Integer n; 798 register char *p; 799 register Boolean isneg; 800 801 n = 0; 802 p = curchar; 803 if (*p == '-') { 804 isneg = true; 805 ++p; 806 } else { 807 isneg = false; 808 } 809 while (isdigit(*p)) { 810 n = 10*n + (*p - '0'); 811 ++p; 812 } 813 curchar = p; 814 return isneg ? (-n) : n; 815 } 816 817 /* 818 * Add a tag name. This is a kludge to be able to refer 819 * to tags that have the same name as some other symbol 820 * in the same block. 821 */ 822 823 private addtag(s) 824 register Symbol s; 825 { 826 register Symbol t; 827 char buf[100]; 828 829 sprintf(buf, "$$%.90s", ident(s->name)); 830 t = insert(identname(buf, false)); 831 t->language = s->language; 832 t->class = TAG; 833 t->type = s->type; 834 t->block = s->block; 835 } 836 837 /* 838 * Allocate file and line tables and initialize indices. 839 */ 840 841 private allocmaps(nf, nl) 842 Integer nf, nl; 843 { 844 if (filetab != nil) { 845 dispose(filetab); 846 } 847 if (linetab != nil) { 848 dispose(linetab); 849 } 850 filetab = newarr(Filetab, nf); 851 linetab = newarr(Linetab, nl); 852 filep = filetab; 853 linep = linetab; 854 } 855 856 /* 857 * Add a file to the file table. 858 */ 859 860 private enterfile(filename, addr) 861 String filename; 862 Address addr; 863 { 864 if (addr != curfaddr) { 865 filep->addr = addr; 866 filep->filename = filename; 867 filep->lineindex = linep - linetab; 868 ++filep; 869 curfaddr = addr; 870 } 871 } 872 873 /* 874 * Since we only estimated the number of lines (and it was a poor 875 * estimation) and since we need to know the exact number of lines 876 * to do a binary search, we set it when we're done. 877 */ 878 879 private setnlines() 880 { 881 nlhdr.nlines = linep - linetab; 882 } 883 884 /* 885 * Similarly for nfiles ... 886 */ 887 888 private setnfiles() 889 { 890 nlhdr.nfiles = filep - filetab; 891 setsource(filetab[0].filename); 892 } 893