/* Copyright (c) 1982 Regents of the University of California */ static char sccsid[] = "@(#)object.c 1.4 02/16/83"; /* * Object code interface, mainly for extraction of symbolic information. */ #include "defs.h" #include "object.h" #include "main.h" #include "symbols.h" #include "names.h" #include "languages.h" #include "mappings.h" #include "lists.h" #include #include #include #ifndef public struct { unsigned int stringsize; /* size of the dumped string table */ unsigned int nsyms; /* number of symbols */ unsigned int nfiles; /* number of files */ unsigned int nlines; /* number of lines */ } nlhdr; #endif public String objname = "a.out"; public Integer objsize; public char *stringtab; private String progname = nil; private Language curlang; private Symbol curmodule; private Symbol curparam; private Boolean warned; private Filetab *filep; private Linetab *linep; private Address curfaddr; #define curfilename() (filep-1)->filename /* * Blocks are figured out on the fly while reading the symbol table. */ #define MAXBLKDEPTH 25 private Symbol curblock; private Symbol blkstack[MAXBLKDEPTH]; private Integer curlevel; #define enterblock(b) { \ blkstack[curlevel] = curblock; \ ++curlevel; \ b->level = curlevel; \ b->block = curblock; \ curblock = b; \ } #define exitblock() { \ --curlevel; \ curblock = blkstack[curlevel]; \ } /* * Enter a source line or file name reference into the appropriate table. * Expanded inline to reduce procedure calls. * * private enterline(linenumber, address) * Lineno linenumber; * Address address; * ... */ #define enterline(linenumber, address) \ { \ register Linetab *lp; \ \ lp = linep - 1; \ if (linenumber != lp->line) { \ if (address != lp->addr) { \ ++lp; \ } \ lp->line = linenumber; \ lp->addr = address; \ linep = lp + 1; \ } \ } #define NTYPES 1000 private Symbol typetable[NTYPES]; /* * Read in the namelist from the obj file. * * Reads and seeks are used instead of fread's and fseek's * for efficiency sake; there's a lot of data being read here. */ public readobj(file) String file; { Fileid f; struct exec hdr; struct nlist nlist; f = open(file, 0); if (f < 0) { fatal("can't open %s", file); } read(f, &hdr, sizeof(hdr)); objsize = hdr.a_text; nlhdr.nsyms = hdr.a_syms / sizeof(nlist); nlhdr.nfiles = nlhdr.nsyms; nlhdr.nlines = nlhdr.nsyms; lseek(f, (long) N_STROFF(hdr), 0); read(f, &(nlhdr.stringsize), sizeof(nlhdr.stringsize)); nlhdr.stringsize -= 4; stringtab = newarr(char, nlhdr.stringsize); read(f, stringtab, nlhdr.stringsize); allocmaps(nlhdr.nfiles, nlhdr.nlines); lseek(f, (long) N_SYMOFF(hdr), 0); readsyms(f); ordfunctab(); setnlines(); setnfiles(); close(f); } /* * Read in symbols from object file. */ private readsyms(f) Fileid f; { struct nlist *namelist; register struct nlist *np, *ub; register int index; register String name; register Boolean afterlg; initsyms(); namelist = newarr(struct nlist, nlhdr.nsyms); read(f, namelist, nlhdr.nsyms * sizeof(struct nlist)); afterlg = false; ub = &namelist[nlhdr.nsyms]; for (np = &namelist[0]; np < ub; np++) { index = np->n_un.n_strx; if (index != 0) { name = &stringtab[index - 4]; } else { name = nil; } /* * assumptions: * not an N_STAB ==> name != nil * name[0] == '-' ==> name == "-lg" * name[0] != '_' ==> filename or invisible * * The "-lg" signals the beginning of global loader symbols. */ if ((np->n_type&N_STAB) != 0) { enter_nl(name, np); } else if (name[0] == '-') { afterlg = true; if (curblock->class != PROG) { exitblock(); if (curblock->class != PROG) { exitblock(); } } enterline(0, (linep-1)->addr + 1); } else if (afterlg) { if (name[0] == '_') { check_global(&name[1], np); } } else if (name[0] == '_') { check_local(&name[1], np); } else if ((np->n_type&N_TEXT) == N_TEXT) { check_filename(name); } } dispose(namelist); } /* * Initialize symbol information. */ private initsyms() { curblock = nil; curlevel = 0; if (progname == nil) { progname = strdup(objname); if (rindex(progname, '/') != nil) { progname = rindex(progname, '/') + 1; } if (index(progname, '.') != nil) { *(index(progname, '.')) = '\0'; } } program = insert(identname(progname, true)); program->class = PROG; newfunc(program); findbeginning(program); enterblock(program); curmodule = program; t_boolean = maketype("$boolean", 0L, 1L); t_int = maketype("$integer", 0x80000000L, 0x7fffffffL); t_char = maketype("$char", 0L, 127L); t_real = maketype("$real", 4L, 0L); t_nil = maketype("$nil", 0L, 0L); } /* * Free all the object file information that's being stored. */ public objfree() { symbol_free(); keywords_free(); names_free(); dispose(stringtab); clrfunctab(); } /* * Enter a namelist entry. */ private enter_nl(name, np) String name; register struct nlist *np; { register Symbol s; String mname, suffix; register Name n; register Symbol *tt; s = nil; if (name == nil) { n = nil; } else { n = identname(name, true); } switch (np->n_type) { case N_LBRAC: s = symbol_alloc(); s->class = PROC; enterblock(s); break; case N_RBRAC: exitblock(); break; case N_SLINE: enterline((Lineno) np->n_desc, (Address) np->n_value); break; /* * Compilation unit. C associates scope with filenames * so we treat them as "modules". The filename without * the suffix is used for the module name. * * Because there is no explicit "end-of-block" mark in * the object file, we must exit blocks for the current * procedure and module. */ case N_SO: mname = strdup(ident(n)); if (rindex(mname, '/') != nil) { mname = rindex(mname, '/') + 1; } suffix = rindex(mname, '.'); curlang = findlanguage(suffix); if (suffix != nil) { *suffix = '\0'; } if (curblock->class != PROG) { exitblock(); if (curblock->class != PROG) { exitblock(); } } s = insert(identname(mname, true)); s->language = curlang; s->class = MODULE; enterblock(s); curmodule = s; if (program->language == nil) { program->language = curlang; } warned = false; enterfile(ident(n), (Address) np->n_value); for (tt = &typetable[0]; tt < &typetable[NTYPES]; tt++) { *tt = nil; } break; /* * Textually included files. */ case N_SOL: enterfile(name, (Address) np->n_value); break; /* * These symbols are assumed to have non-nil names. */ case N_GSYM: case N_FUN: case N_STSYM: case N_LCSYM: case N_RSYM: case N_PSYM: case N_LSYM: case N_SSYM: if (index(name, ':') == nil) { if (not warned) { warned = true; /* * Shouldn't do this if user might be typing. * warning("old style symbol information found in \"%s\"", curfilename()); * */ } } else { entersym(name, np); } break; case N_PC: break; case N_LENG: /* * Should complain out this, obviously the wrong symbol format. */ break; default: if (name != nil) { printf("%s, ", name); } printf("ntype %2x, desc %x, value %x\n", np->n_type, np->n_desc, np->n_value); break; } } /* * Check to see if a global _name is already in the symbol table, * if not then insert it. */ private check_global(name, np) String name; register struct nlist *np; { register Name n; register Symbol t; if (not streq(name, "end")) { n = identname(name, true); if ((np->n_type&N_TYPE) == N_TEXT) { find(t, n) where t->level == program->level and isblock(t) endfind(t); if (t == nil) { t = insert(n); t->language = findlanguage(".s"); t->class = FUNC; t->type = t_int; t->block = curblock; t->level = program->level; } t->symvalue.funcv.beginaddr = np->n_value; newfunc(t); findbeginning(t); } else { find(t, n) where t->class == VAR and t->level == program->level endfind(t); if (t == nil) { t = insert(n); t->language = findlanguage(".s"); t->class = VAR; t->type = t_int; t->block = curblock; t->level = program->level; } t->symvalue.offset = np->n_value; } } } /* * Check to see if a local _name is known in the current scope. * If not then enter it. */ private check_local(name, np) String name; register struct nlist *np; { register Name n; register Symbol t, cur; n = identname(name, true); cur = ((np->n_type&N_TYPE) == N_TEXT) ? curmodule : curblock; find(t, n) where t->block == cur endfind(t); if (t == nil) { t = insert(n); t->language = findlanguage(".s"); t->type = t_int; t->block = cur; t->level = cur->level; if ((np->n_type&N_TYPE) == N_TEXT) { t->class = FUNC; t->symvalue.funcv.beginaddr = np->n_value; newfunc(t); findbeginning(t); } else { t->class = VAR; t->symvalue.offset = np->n_value; } } } /* * Check to see if a symbol corresponds to a object file name. * For some reason these are listed as in the text segment. */ private check_filename(name) String name; { register String mname; register Integer i; register Symbol s; mname = strdup(name); i = strlen(mname) - 2; if (i >= 0 and mname[i] == '.' and mname[i+1] == 'o') { mname[i] = '\0'; --i; while (mname[i] != '/' and i >= 0) { --i; } s = insert(identname(&mname[i+1], true)); s->language = findlanguage(".s"); s->class = MODULE; if (curblock->class != PROG) { exitblock(); if (curblock->class != PROG) { exitblock(); } } enterblock(s); curmodule = s; } } /* * Put an nlist into the symbol table. * If it's already there just add the associated information. * * Type information is encoded in the name following a ":". */ private Symbol constype(); private Char *curchar; #define skipchar(ptr, ch) { \ if (*ptr != ch) { \ panic("expected char '%c', found char '%c'", ch, *ptr); \ } \ ++ptr; \ } private entersym(str, np) String str; struct nlist *np; { register Symbol s; register char *p; register int c; register Name n; register Integer i; Boolean knowtype, isnew; Symclass class; Integer level; p = index(str, ':'); *p = '\0'; c = *(p+1); n = identname(str, true); if (index("FfGV", c) != nil) { if (c == 'F' or c == 'f') { class = FUNC; } else { class = VAR; } level = (c == 'f' ? curmodule->level : program->level); find(s, n) where s->level == level and s->class == class endfind(s); if (s == nil) { isnew = true; s = insert(n); } else { isnew = false; } } else { isnew = true; s = insert(n); } /* * Default attributes. */ s->language = curlang; s->class = VAR; s->block = curblock; s->level = curlevel; s->symvalue.offset = np->n_value; curchar = p + 2; knowtype = false; switch (c) { case 't': /* type name */ s->class = TYPE; i = getint(); if (i == 0) { panic("bad input on type \"%s\" at \"%s\"", symname(s), curchar); } else if (i >= NTYPES) { panic("too many types in file \"%s\"", curfilename()); } /* * A hack for C typedefs that don't create new types, * e.g. typedef unsigned int Hashvalue; */ if (*curchar == '\0') { s->type = typetable[i]; if (s->type == nil) { panic("nil type for %d", i); } knowtype = true; } else { typetable[i] = s; skipchar(curchar, '='); } break; case 'T': /* tag */ s->class = TAG; i = getint(); if (i == 0) { panic("bad input on tag \"%s\" at \"%s\"", symname(s), curchar); } else if (i >= NTYPES) { panic("too many types in file \"%s\"", curfilename()); } if (typetable[i] != nil) { typetable[i]->language = curlang; typetable[i]->class = TYPE; typetable[i]->type = s; } else { typetable[i] = s; } skipchar(curchar, '='); break; case 'F': /* public function */ case 'f': /* private function */ s->class = FUNC; if (curblock->class == FUNC or curblock->class == PROC) { exitblock(); } enterblock(s); if (c == 'F') { s->level = program->level; isnew = false; } curparam = s; if (isnew) { s->symvalue.funcv.beginaddr = np->n_value; newfunc(s); findbeginning(s); } break; case 'G': /* public variable */ s->level = program->level; break; case 'S': /* private variable */ s->level = curmodule->level; s->block = curmodule; break; case 'V': /* own variable */ s->level = 2; break; case 'r': /* register variable */ s->level = -(s->level); break; case 'p': /* parameter variable */ curparam->chain = s; curparam = s; break; case 'v': /* varies parameter */ s->class = REF; s->symvalue.offset = np->n_value; curparam->chain = s; curparam = s; break; default: /* local variable */ --curchar; break; } if (not knowtype) { s->type = constype(nil); if (s->class == TAG) { addtag(s); } } if (tracesyms) { printdecl(s); fflush(stdout); } } /* * Construct a type out of a string encoding. * * The forms of the string are * * * = * r;; $ subrange * a; $ array[index] of element * s{:;;} $ record * * $ pointer */ private Symbol constype(type) Symbol type; { register Symbol t, u; register Char *p, *cur; register Integer n; Integer b; Name name; Char class; b = curlevel; if (isdigit(*curchar)) { n = getint(); if (n == 0) { panic("bad type number at \"%s\"", curchar); } else if (n >= NTYPES) { panic("too many types in file \"%s\"", curfilename()); } if (*curchar == '=') { if (typetable[n] != nil) { t = typetable[n]; } else { t = symbol_alloc(); typetable[n] = t; } ++curchar; constype(t); } else { t = typetable[n]; if (t == nil) { t = symbol_alloc(); typetable[n] = t; } } } else { if (type == nil) { t = symbol_alloc(); } else { t = type; } t->language = curlang; t->level = b; class = *curchar++; switch (class) { case 'r': t->class = RANGE; t->type = constype(nil); skipchar(curchar, ';'); t->symvalue.rangev.lower = getint(); skipchar(curchar, ';'); t->symvalue.rangev.upper = getint(); break; case 'a': t->class = ARRAY; t->chain = constype(nil); skipchar(curchar, ';'); t->type = constype(nil); break; case 's': case 'u': t->class = (class == 's') ? RECORD : VARNT; t->symvalue.offset = getint(); u = t; cur = curchar; while (*cur != ';' and *cur != '\0') { p = index(cur, ':'); if (p == nil) { panic("index(\"%s\", ':') failed", curchar); } *p = '\0'; name = identname(cur, true); u->chain = newSymbol(name, b, FIELD, nil, nil); cur = p + 1; u = u->chain; u->language = curlang; curchar = cur; u->type = constype(nil); skipchar(curchar, ','); u->symvalue.field.offset = getint(); skipchar(curchar, ','); u->symvalue.field.length = getint(); skipchar(curchar, ';'); cur = curchar; } if (*cur == ';') { ++cur; } curchar = cur; break; case 'e': t->class = SCAL; u = t; while (*curchar != ';' and *curchar != '\0') { p = index(curchar, ':'); assert(p != nil); *p = '\0'; u->chain = insert(identname(curchar, true)); curchar = p + 1; u = u->chain; u->language = curlang; u->class = CONST; u->level = b; u->block = curblock; u->type = t; u->symvalue.iconval = getint(); skipchar(curchar, ','); } break; case '*': t->class = PTR; t->type = constype(nil); break; case 'f': t->class = FUNC; t->type = constype(nil); break; default: badcaseval(class); } } return t; } /* * Read an integer from the current position in the type string. */ private Integer getint() { register Integer n; register char *p; register Boolean isneg; n = 0; p = curchar; if (*p == '-') { isneg = true; ++p; } else { isneg = false; } while (isdigit(*p)) { n = 10*n + (*p - '0'); ++p; } curchar = p; return isneg ? (-n) : n; } /* * Add a tag name. This is a kludge to be able to refer * to tags that have the same name as some other symbol * in the same block. */ private addtag(s) register Symbol s; { register Symbol t; char buf[100]; sprintf(buf, "$$%.90s", ident(s->name)); t = insert(identname(buf, false)); t->language = s->language; t->class = TAG; t->type = s->type; t->block = s->block; } /* * Allocate file and line tables and initialize indices. */ private allocmaps(nf, nl) Integer nf, nl; { if (filetab != nil) { dispose(filetab); } if (linetab != nil) { dispose(linetab); } filetab = newarr(Filetab, nf); linetab = newarr(Linetab, nl); filep = filetab; linep = linetab; } /* * Add a file to the file table. */ private enterfile(filename, addr) String filename; Address addr; { if (addr != curfaddr) { filep->addr = addr; filep->filename = filename; filep->lineindex = linep - linetab; ++filep; curfaddr = addr; } } /* * Since we only estimated the number of lines (and it was a poor * estimation) and since we need to know the exact number of lines * to do a binary search, we set it when we're done. */ private setnlines() { nlhdr.nlines = linep - linetab; } /* * Similarly for nfiles ... */ private setnfiles() { nlhdr.nfiles = filep - filetab; setsource(filetab[0].filename); }