xref: /csrg-svn/old/dbx/object.c (revision 11769)
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