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