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