1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)object.c 1.5 03/13/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 default: 350 /* 351 * Should complain out this, obviously the wrong symbol format. 352 * 353 if (name != nil) { 354 printf("%s, ", name); 355 } 356 printf("ntype %2x, desc %x, value %x\n", 357 np->n_type, np->n_desc, np->n_value); 358 * 359 */ 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 * or typedef struct blah BLAH; 554 */ 555 if (*curchar == '\0') { 556 s->type = typetable[i]; 557 if (s->type == nil) { 558 s->type = symbol_alloc(); 559 typetable[i] = s->type; 560 } 561 knowtype = true; 562 } else { 563 typetable[i] = s; 564 skipchar(curchar, '='); 565 } 566 break; 567 568 case 'T': /* tag */ 569 s->class = TAG; 570 i = getint(); 571 if (i == 0) { 572 panic("bad input on tag \"%s\" at \"%s\"", symname(s), 573 curchar); 574 } else if (i >= NTYPES) { 575 panic("too many types in file \"%s\"", curfilename()); 576 } 577 if (typetable[i] != nil) { 578 typetable[i]->language = curlang; 579 typetable[i]->class = TYPE; 580 typetable[i]->type = s; 581 } else { 582 typetable[i] = s; 583 } 584 skipchar(curchar, '='); 585 break; 586 587 case 'F': /* public function */ 588 case 'f': /* private function */ 589 s->class = FUNC; 590 if (curblock->class == FUNC or curblock->class == PROC) { 591 exitblock(); 592 } 593 enterblock(s); 594 if (c == 'F') { 595 s->level = program->level; 596 isnew = false; 597 } 598 curparam = s; 599 if (isnew) { 600 s->symvalue.funcv.beginaddr = np->n_value; 601 newfunc(s); 602 findbeginning(s); 603 } 604 break; 605 606 case 'G': /* public variable */ 607 s->level = program->level; 608 break; 609 610 case 'S': /* private variable */ 611 s->level = curmodule->level; 612 s->block = curmodule; 613 break; 614 615 case 'V': /* own variable */ 616 s->level = 2; 617 break; 618 619 case 'r': /* register variable */ 620 s->level = -(s->level); 621 break; 622 623 case 'p': /* parameter variable */ 624 curparam->chain = s; 625 curparam = s; 626 break; 627 628 case 'v': /* varies parameter */ 629 s->class = REF; 630 s->symvalue.offset = np->n_value; 631 curparam->chain = s; 632 curparam = s; 633 break; 634 635 default: /* local variable */ 636 --curchar; 637 break; 638 } 639 if (not knowtype) { 640 s->type = constype(nil); 641 if (s->class == TAG) { 642 addtag(s); 643 } 644 } 645 if (tracesyms) { 646 printdecl(s); 647 fflush(stdout); 648 } 649 } 650 651 /* 652 * Construct a type out of a string encoding. 653 * 654 * The forms of the string are 655 * 656 * <number> 657 * <number>=<type> 658 * r<type>;<number>;<number> $ subrange 659 * a<type>;<type> $ array[index] of element 660 * s{<name>:<type>;<number>;<number>} $ record 661 * *<type> $ pointer 662 */ 663 664 private Symbol constype(type) 665 Symbol type; 666 { 667 register Symbol t, u; 668 register Char *p, *cur; 669 register Integer n; 670 Integer b; 671 Name name; 672 Char class; 673 674 b = curlevel; 675 if (isdigit(*curchar)) { 676 n = getint(); 677 if (n == 0) { 678 panic("bad type number at \"%s\"", curchar); 679 } else if (n >= NTYPES) { 680 panic("too many types in file \"%s\"", curfilename()); 681 } 682 if (*curchar == '=') { 683 if (typetable[n] != nil) { 684 t = typetable[n]; 685 } else { 686 t = symbol_alloc(); 687 typetable[n] = t; 688 } 689 ++curchar; 690 constype(t); 691 } else { 692 t = typetable[n]; 693 if (t == nil) { 694 t = symbol_alloc(); 695 typetable[n] = t; 696 } 697 } 698 } else { 699 if (type == nil) { 700 t = symbol_alloc(); 701 } else { 702 t = type; 703 } 704 t->language = curlang; 705 t->level = b; 706 class = *curchar++; 707 switch (class) { 708 case 'r': 709 t->class = RANGE; 710 t->type = constype(nil); 711 skipchar(curchar, ';'); 712 t->symvalue.rangev.lower = getint(); 713 skipchar(curchar, ';'); 714 t->symvalue.rangev.upper = getint(); 715 break; 716 717 case 'a': 718 t->class = ARRAY; 719 t->chain = constype(nil); 720 skipchar(curchar, ';'); 721 t->type = constype(nil); 722 break; 723 724 case 's': 725 case 'u': 726 t->class = (class == 's') ? RECORD : VARNT; 727 t->symvalue.offset = getint(); 728 u = t; 729 cur = curchar; 730 while (*cur != ';' and *cur != '\0') { 731 p = index(cur, ':'); 732 if (p == nil) { 733 panic("index(\"%s\", ':') failed", curchar); 734 } 735 *p = '\0'; 736 name = identname(cur, true); 737 u->chain = newSymbol(name, b, FIELD, nil, nil); 738 cur = p + 1; 739 u = u->chain; 740 u->language = curlang; 741 curchar = cur; 742 u->type = constype(nil); 743 skipchar(curchar, ','); 744 u->symvalue.field.offset = getint(); 745 skipchar(curchar, ','); 746 u->symvalue.field.length = getint(); 747 skipchar(curchar, ';'); 748 cur = curchar; 749 } 750 if (*cur == ';') { 751 ++cur; 752 } 753 curchar = cur; 754 break; 755 756 case 'e': 757 t->class = SCAL; 758 u = t; 759 while (*curchar != ';' and *curchar != '\0') { 760 p = index(curchar, ':'); 761 assert(p != nil); 762 *p = '\0'; 763 u->chain = insert(identname(curchar, true)); 764 curchar = p + 1; 765 u = u->chain; 766 u->language = curlang; 767 u->class = CONST; 768 u->level = b; 769 u->block = curblock; 770 u->type = t; 771 u->symvalue.iconval = getint(); 772 skipchar(curchar, ','); 773 } 774 break; 775 776 case '*': 777 t->class = PTR; 778 t->type = constype(nil); 779 break; 780 781 case 'f': 782 t->class = FUNC; 783 t->type = constype(nil); 784 break; 785 786 default: 787 badcaseval(class); 788 } 789 } 790 return t; 791 } 792 793 /* 794 * Read an integer from the current position in the type string. 795 */ 796 797 private Integer getint() 798 { 799 register Integer n; 800 register char *p; 801 register Boolean isneg; 802 803 n = 0; 804 p = curchar; 805 if (*p == '-') { 806 isneg = true; 807 ++p; 808 } else { 809 isneg = false; 810 } 811 while (isdigit(*p)) { 812 n = 10*n + (*p - '0'); 813 ++p; 814 } 815 curchar = p; 816 return isneg ? (-n) : n; 817 } 818 819 /* 820 * Add a tag name. This is a kludge to be able to refer 821 * to tags that have the same name as some other symbol 822 * in the same block. 823 */ 824 825 private addtag(s) 826 register Symbol s; 827 { 828 register Symbol t; 829 char buf[100]; 830 831 sprintf(buf, "$$%.90s", ident(s->name)); 832 t = insert(identname(buf, false)); 833 t->language = s->language; 834 t->class = TAG; 835 t->type = s->type; 836 t->block = s->block; 837 } 838 839 /* 840 * Allocate file and line tables and initialize indices. 841 */ 842 843 private allocmaps(nf, nl) 844 Integer nf, nl; 845 { 846 if (filetab != nil) { 847 dispose(filetab); 848 } 849 if (linetab != nil) { 850 dispose(linetab); 851 } 852 filetab = newarr(Filetab, nf); 853 linetab = newarr(Linetab, nl); 854 filep = filetab; 855 linep = linetab; 856 } 857 858 /* 859 * Add a file to the file table. 860 */ 861 862 private enterfile(filename, addr) 863 String filename; 864 Address addr; 865 { 866 if (addr != curfaddr) { 867 filep->addr = addr; 868 filep->filename = filename; 869 filep->lineindex = linep - linetab; 870 ++filep; 871 curfaddr = addr; 872 } 873 } 874 875 /* 876 * Since we only estimated the number of lines (and it was a poor 877 * estimation) and since we need to know the exact number of lines 878 * to do a binary search, we set it when we're done. 879 */ 880 881 private setnlines() 882 { 883 nlhdr.nlines = linep - linetab; 884 } 885 886 /* 887 * Similarly for nfiles ... 888 */ 889 890 private setnfiles() 891 { 892 nlhdr.nfiles = filep - filetab; 893 setsource(filetab[0].filename); 894 } 895