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