1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)object.c 1.6 03/30/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 program->symvalue.funcv.beginaddr = 0; 214 findbeginning(program); 215 newfunc(program); 216 enterblock(program); 217 curmodule = program; 218 t_boolean = maketype("$boolean", 0L, 1L); 219 t_int = maketype("$integer", 0x80000000L, 0x7fffffffL); 220 t_char = maketype("$char", 0L, 127L); 221 t_real = maketype("$real", 4L, 0L); 222 t_nil = maketype("$nil", 0L, 0L); 223 } 224 225 /* 226 * Free all the object file information that's being stored. 227 */ 228 229 public objfree() 230 { 231 symbol_free(); 232 keywords_free(); 233 names_free(); 234 dispose(stringtab); 235 clrfunctab(); 236 } 237 238 /* 239 * Enter a namelist entry. 240 */ 241 242 private enter_nl(name, np) 243 String name; 244 register struct nlist *np; 245 { 246 register Symbol s; 247 String mname, suffix; 248 register Name n; 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 s->symvalue.funcv.beginaddr = 0; 300 findbeginning(s); 301 enterblock(s); 302 curmodule = s; 303 if (program->language == nil) { 304 program->language = curlang; 305 } 306 warned = false; 307 enterfile(ident(n), (Address) np->n_value); 308 bzero(typetable, sizeof(typetable)); 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 s->symvalue.funcv.beginaddr = 0; 467 findbeginning(s); 468 if (curblock->class != PROG) { 469 exitblock(); 470 if (curblock->class != PROG) { 471 exitblock(); 472 } 473 } 474 enterblock(s); 475 curmodule = s; 476 } 477 } 478 479 /* 480 * Put an nlist into the symbol table. 481 * If it's already there just add the associated information. 482 * 483 * Type information is encoded in the name following a ":". 484 */ 485 486 private Symbol constype(); 487 private Char *curchar; 488 489 #define skipchar(ptr, ch) { \ 490 if (*ptr != ch) { \ 491 panic("expected char '%c', found char '%c'", ch, *ptr); \ 492 } \ 493 ++ptr; \ 494 } 495 496 private entersym(str, np) 497 String str; 498 struct nlist *np; 499 { 500 register Symbol s; 501 register char *p; 502 register int c; 503 register Name n; 504 register Integer i; 505 Boolean knowtype, isnew; 506 Symclass class; 507 Integer level; 508 509 p = index(str, ':'); 510 *p = '\0'; 511 c = *(p+1); 512 n = identname(str, true); 513 if (index("FfGV", c) != nil) { 514 if (c == 'F' or c == 'f') { 515 class = FUNC; 516 } else { 517 class = VAR; 518 } 519 level = (c == 'f' ? curmodule->level : program->level); 520 find(s, n) where s->level == level and s->class == class endfind(s); 521 if (s == nil) { 522 isnew = true; 523 s = insert(n); 524 } else { 525 isnew = false; 526 } 527 } else { 528 isnew = true; 529 s = insert(n); 530 } 531 532 /* 533 * Default attributes. 534 */ 535 s->language = curlang; 536 s->class = VAR; 537 s->block = curblock; 538 s->level = curlevel; 539 s->symvalue.offset = np->n_value; 540 curchar = p + 2; 541 knowtype = false; 542 switch (c) { 543 case 't': /* type name */ 544 s->class = TYPE; 545 i = getint(); 546 if (i == 0) { 547 panic("bad input on type \"%s\" at \"%s\"", symname(s), 548 curchar); 549 } else if (i >= NTYPES) { 550 panic("too many types in file \"%s\"", curfilename()); 551 } 552 /* 553 * A hack for C typedefs that don't create new types, 554 * e.g. typedef unsigned int Hashvalue; 555 * or typedef struct blah BLAH; 556 */ 557 if (*curchar == '\0') { 558 s->type = typetable[i]; 559 if (s->type == nil) { 560 s->type = symbol_alloc(); 561 typetable[i] = s->type; 562 } 563 knowtype = true; 564 } else { 565 typetable[i] = s; 566 skipchar(curchar, '='); 567 } 568 break; 569 570 case 'T': /* tag */ 571 s->class = TAG; 572 i = getint(); 573 if (i == 0) { 574 panic("bad input on tag \"%s\" at \"%s\"", symname(s), 575 curchar); 576 } else if (i >= NTYPES) { 577 panic("too many types in file \"%s\"", curfilename()); 578 } 579 if (typetable[i] != nil) { 580 typetable[i]->language = curlang; 581 typetable[i]->class = TYPE; 582 typetable[i]->type = s; 583 } else { 584 typetable[i] = s; 585 } 586 skipchar(curchar, '='); 587 break; 588 589 case 'F': /* public function */ 590 case 'f': /* private function */ 591 s->class = FUNC; 592 if (curblock->class == FUNC or curblock->class == PROC) { 593 exitblock(); 594 } 595 enterblock(s); 596 if (c == 'F') { 597 s->level = program->level; 598 isnew = false; 599 } 600 curparam = s; 601 if (isnew) { 602 s->symvalue.funcv.beginaddr = np->n_value; 603 newfunc(s); 604 findbeginning(s); 605 } 606 break; 607 608 case 'G': /* public variable */ 609 s->level = program->level; 610 break; 611 612 case 'S': /* private variable */ 613 s->level = curmodule->level; 614 s->block = curmodule; 615 break; 616 617 case 'V': /* own variable */ 618 s->level = 2; 619 break; 620 621 case 'r': /* register variable */ 622 s->level = -(s->level); 623 break; 624 625 case 'p': /* parameter variable */ 626 curparam->chain = s; 627 curparam = s; 628 break; 629 630 case 'v': /* varies parameter */ 631 s->class = REF; 632 s->symvalue.offset = np->n_value; 633 curparam->chain = s; 634 curparam = s; 635 break; 636 637 default: /* local variable */ 638 --curchar; 639 break; 640 } 641 if (not knowtype) { 642 s->type = constype(nil); 643 if (s->class == TAG) { 644 addtag(s); 645 } 646 } 647 if (tracesyms) { 648 printdecl(s); 649 fflush(stdout); 650 } 651 } 652 653 /* 654 * Construct a type out of a string encoding. 655 * 656 * The forms of the string are 657 * 658 * <number> 659 * <number>=<type> 660 * r<type>;<number>;<number> $ subrange 661 * a<type>;<type> $ array[index] of element 662 * s{<name>:<type>;<number>;<number>} $ record 663 * *<type> $ pointer 664 */ 665 666 private Symbol constype(type) 667 Symbol type; 668 { 669 register Symbol t, u; 670 register Char *p, *cur; 671 register Integer n; 672 Integer b; 673 Name name; 674 Char class; 675 676 b = curlevel; 677 if (isdigit(*curchar)) { 678 n = getint(); 679 if (n == 0) { 680 panic("bad type number at \"%s\"", curchar); 681 } else if (n >= NTYPES) { 682 panic("too many types in file \"%s\"", curfilename()); 683 } 684 if (*curchar == '=') { 685 if (typetable[n] != nil) { 686 t = typetable[n]; 687 } else { 688 t = symbol_alloc(); 689 typetable[n] = t; 690 } 691 ++curchar; 692 constype(t); 693 } else { 694 t = typetable[n]; 695 if (t == nil) { 696 t = symbol_alloc(); 697 typetable[n] = t; 698 } 699 } 700 } else { 701 if (type == nil) { 702 t = symbol_alloc(); 703 } else { 704 t = type; 705 } 706 t->language = curlang; 707 t->level = b; 708 class = *curchar++; 709 switch (class) { 710 case 'r': 711 t->class = RANGE; 712 t->type = constype(nil); 713 skipchar(curchar, ';'); 714 t->symvalue.rangev.lower = getint(); 715 skipchar(curchar, ';'); 716 t->symvalue.rangev.upper = getint(); 717 break; 718 719 case 'a': 720 t->class = ARRAY; 721 t->chain = constype(nil); 722 skipchar(curchar, ';'); 723 t->type = constype(nil); 724 break; 725 726 case 's': 727 case 'u': 728 t->class = (class == 's') ? RECORD : VARNT; 729 t->symvalue.offset = getint(); 730 u = t; 731 cur = curchar; 732 while (*cur != ';' and *cur != '\0') { 733 p = index(cur, ':'); 734 if (p == nil) { 735 panic("index(\"%s\", ':') failed", curchar); 736 } 737 *p = '\0'; 738 name = identname(cur, true); 739 u->chain = newSymbol(name, b, FIELD, nil, nil); 740 cur = p + 1; 741 u = u->chain; 742 u->language = curlang; 743 curchar = cur; 744 u->type = constype(nil); 745 skipchar(curchar, ','); 746 u->symvalue.field.offset = getint(); 747 skipchar(curchar, ','); 748 u->symvalue.field.length = getint(); 749 skipchar(curchar, ';'); 750 cur = curchar; 751 } 752 if (*cur == ';') { 753 ++cur; 754 } 755 curchar = cur; 756 break; 757 758 case 'e': 759 t->class = SCAL; 760 u = t; 761 while (*curchar != ';' and *curchar != '\0') { 762 p = index(curchar, ':'); 763 assert(p != nil); 764 *p = '\0'; 765 u->chain = insert(identname(curchar, true)); 766 curchar = p + 1; 767 u = u->chain; 768 u->language = curlang; 769 u->class = CONST; 770 u->level = b; 771 u->block = curblock; 772 u->type = t; 773 u->symvalue.iconval = getint(); 774 skipchar(curchar, ','); 775 } 776 break; 777 778 case '*': 779 t->class = PTR; 780 t->type = constype(nil); 781 break; 782 783 case 'f': 784 t->class = FUNC; 785 t->type = constype(nil); 786 break; 787 788 default: 789 badcaseval(class); 790 } 791 } 792 return t; 793 } 794 795 /* 796 * Read an integer from the current position in the type string. 797 */ 798 799 private Integer getint() 800 { 801 register Integer n; 802 register char *p; 803 register Boolean isneg; 804 805 n = 0; 806 p = curchar; 807 if (*p == '-') { 808 isneg = true; 809 ++p; 810 } else { 811 isneg = false; 812 } 813 while (isdigit(*p)) { 814 n = 10*n + (*p - '0'); 815 ++p; 816 } 817 curchar = p; 818 return isneg ? (-n) : n; 819 } 820 821 /* 822 * Add a tag name. This is a kludge to be able to refer 823 * to tags that have the same name as some other symbol 824 * in the same block. 825 */ 826 827 private addtag(s) 828 register Symbol s; 829 { 830 register Symbol t; 831 char buf[100]; 832 833 sprintf(buf, "$$%.90s", ident(s->name)); 834 t = insert(identname(buf, false)); 835 t->language = s->language; 836 t->class = TAG; 837 t->type = s->type; 838 t->block = s->block; 839 } 840 841 /* 842 * Allocate file and line tables and initialize indices. 843 */ 844 845 private allocmaps(nf, nl) 846 Integer nf, nl; 847 { 848 if (filetab != nil) { 849 dispose(filetab); 850 } 851 if (linetab != nil) { 852 dispose(linetab); 853 } 854 filetab = newarr(Filetab, nf); 855 linetab = newarr(Linetab, nl); 856 filep = filetab; 857 linep = linetab; 858 } 859 860 /* 861 * Add a file to the file table. 862 */ 863 864 private enterfile(filename, addr) 865 String filename; 866 Address addr; 867 { 868 if (addr != curfaddr) { 869 filep->addr = addr; 870 filep->filename = filename; 871 filep->lineindex = linep - linetab; 872 ++filep; 873 curfaddr = addr; 874 } 875 } 876 877 /* 878 * Since we only estimated the number of lines (and it was a poor 879 * estimation) and since we need to know the exact number of lines 880 * to do a binary search, we set it when we're done. 881 */ 882 883 private setnlines() 884 { 885 nlhdr.nlines = linep - linetab; 886 } 887 888 /* 889 * Similarly for nfiles ... 890 */ 891 892 private setnfiles() 893 { 894 nlhdr.nfiles = filep - filetab; 895 setsource(filetab[0].filename); 896 } 897