xref: /original-bsd/old/dbx/object.c (revision 037861c2)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)object.c 1.4 02/16/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 	    /*
350 	     * Should complain out this, obviously the wrong symbol format.
351 	     */
352 	    break;
353 
354 	default:
355 	    if (name != nil) {
356 		printf("%s, ", name);
357 	    }
358 	    printf("ntype %2x, desc %x, value %x\n",
359 		np->n_type, np->n_desc, np->n_value);
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 	     */
554 	    if (*curchar == '\0') {
555 		s->type = typetable[i];
556 		if (s->type == nil) {
557 		    panic("nil type for %d", i);
558 		}
559 		knowtype = true;
560 	    } else {
561 		typetable[i] = s;
562 		skipchar(curchar, '=');
563 	    }
564 	    break;
565 
566 	case 'T':	/* tag */
567 	    s->class = TAG;
568 	    i = getint();
569 	    if (i == 0) {
570 		panic("bad input on tag \"%s\" at \"%s\"", symname(s),
571 		    curchar);
572 	    } else if (i >= NTYPES) {
573 		panic("too many types in file \"%s\"", curfilename());
574 	    }
575 	    if (typetable[i] != nil) {
576 		typetable[i]->language = curlang;
577 		typetable[i]->class = TYPE;
578 		typetable[i]->type = s;
579 	    } else {
580 		typetable[i] = s;
581 	    }
582 	    skipchar(curchar, '=');
583 	    break;
584 
585 	case 'F':	/* public function */
586 	case 'f':	/* private function */
587 	    s->class = FUNC;
588 	    if (curblock->class == FUNC or curblock->class == PROC) {
589 		exitblock();
590 	    }
591 	    enterblock(s);
592 	    if (c == 'F') {
593 		s->level = program->level;
594 		isnew = false;
595 	    }
596 	    curparam = s;
597 	    if (isnew) {
598 		s->symvalue.funcv.beginaddr = np->n_value;
599 		newfunc(s);
600 		findbeginning(s);
601 	    }
602 	    break;
603 
604 	case 'G':	/* public variable */
605 	    s->level = program->level;
606 	    break;
607 
608 	case 'S':	/* private variable */
609 	    s->level = curmodule->level;
610 	    s->block = curmodule;
611 	    break;
612 
613 	case 'V':	/* own variable */
614 	    s->level = 2;
615 	    break;
616 
617 	case 'r':	/* register variable */
618 	    s->level = -(s->level);
619 	    break;
620 
621 	case 'p':	/* parameter variable */
622 	    curparam->chain = s;
623 	    curparam = s;
624 	    break;
625 
626 	case 'v':	/* varies parameter */
627 	    s->class = REF;
628 	    s->symvalue.offset = np->n_value;
629 	    curparam->chain = s;
630 	    curparam = s;
631 	    break;
632 
633 	default:	/* local variable */
634 	    --curchar;
635 	    break;
636     }
637     if (not knowtype) {
638 	s->type = constype(nil);
639 	if (s->class == TAG) {
640 	    addtag(s);
641 	}
642     }
643     if (tracesyms) {
644 	printdecl(s);
645 	fflush(stdout);
646     }
647 }
648 
649 /*
650  * Construct a type out of a string encoding.
651  *
652  * The forms of the string are
653  *
654  *	<number>
655  *	<number>=<type>
656  *	r<type>;<number>;<number>		$ subrange
657  *	a<type>;<type>				$ array[index] of element
658  *	s{<name>:<type>;<number>;<number>}	$ record
659  *	*<type>					$ pointer
660  */
661 
662 private Symbol constype(type)
663 Symbol type;
664 {
665     register Symbol t, u;
666     register Char *p, *cur;
667     register Integer n;
668     Integer b;
669     Name name;
670     Char class;
671 
672     b = curlevel;
673     if (isdigit(*curchar)) {
674 	n = getint();
675 	if (n == 0) {
676 	    panic("bad type number at \"%s\"", curchar);
677 	} else if (n >= NTYPES) {
678 	    panic("too many types in file \"%s\"", curfilename());
679 	}
680 	if (*curchar == '=') {
681 	    if (typetable[n] != nil) {
682 		t = typetable[n];
683 	    } else {
684 		t = symbol_alloc();
685 		typetable[n] = t;
686 	    }
687 	    ++curchar;
688 	    constype(t);
689 	} else {
690 	    t = typetable[n];
691 	    if (t == nil) {
692 		t = symbol_alloc();
693 		typetable[n] = t;
694 	    }
695 	}
696     } else {
697 	if (type == nil) {
698 	    t = symbol_alloc();
699 	} else {
700 	    t = type;
701 	}
702 	t->language = curlang;
703 	t->level = b;
704 	class = *curchar++;
705 	switch (class) {
706 	    case 'r':
707 		t->class = RANGE;
708 		t->type = constype(nil);
709 		skipchar(curchar, ';');
710 		t->symvalue.rangev.lower = getint();
711 		skipchar(curchar, ';');
712 		t->symvalue.rangev.upper = getint();
713 		break;
714 
715 	    case 'a':
716 		t->class = ARRAY;
717 		t->chain = constype(nil);
718 		skipchar(curchar, ';');
719 		t->type = constype(nil);
720 		break;
721 
722 	    case 's':
723 	    case 'u':
724 		t->class = (class == 's') ? RECORD : VARNT;
725 		t->symvalue.offset = getint();
726 		u = t;
727 		cur = curchar;
728 		while (*cur != ';' and *cur != '\0') {
729 		    p = index(cur, ':');
730 		    if (p == nil) {
731 			panic("index(\"%s\", ':') failed", curchar);
732 		    }
733 		    *p = '\0';
734 		    name = identname(cur, true);
735 		    u->chain = newSymbol(name, b, FIELD, nil, nil);
736 		    cur = p + 1;
737 		    u = u->chain;
738 		    u->language = curlang;
739 		    curchar = cur;
740 		    u->type = constype(nil);
741 		    skipchar(curchar, ',');
742 		    u->symvalue.field.offset = getint();
743 		    skipchar(curchar, ',');
744 		    u->symvalue.field.length = getint();
745 		    skipchar(curchar, ';');
746 		    cur = curchar;
747 		}
748 		if (*cur == ';') {
749 		    ++cur;
750 		}
751 		curchar = cur;
752 		break;
753 
754 	    case 'e':
755 		t->class = SCAL;
756 		u = t;
757 		while (*curchar != ';' and *curchar != '\0') {
758 		    p = index(curchar, ':');
759 		    assert(p != nil);
760 		    *p = '\0';
761 		    u->chain = insert(identname(curchar, true));
762 		    curchar = p + 1;
763 		    u = u->chain;
764 		    u->language = curlang;
765 		    u->class = CONST;
766 		    u->level = b;
767 		    u->block = curblock;
768 		    u->type = t;
769 		    u->symvalue.iconval = getint();
770 		    skipchar(curchar, ',');
771 		}
772 		break;
773 
774 	    case '*':
775 		t->class = PTR;
776 		t->type = constype(nil);
777 		break;
778 
779 	    case 'f':
780 		t->class = FUNC;
781 		t->type = constype(nil);
782 		break;
783 
784 	    default:
785 		badcaseval(class);
786 	}
787     }
788     return t;
789 }
790 
791 /*
792  * Read an integer from the current position in the type string.
793  */
794 
795 private Integer getint()
796 {
797     register Integer n;
798     register char *p;
799     register Boolean isneg;
800 
801     n = 0;
802     p = curchar;
803     if (*p == '-') {
804 	isneg = true;
805 	++p;
806     } else {
807 	isneg = false;
808     }
809     while (isdigit(*p)) {
810 	n = 10*n + (*p - '0');
811 	++p;
812     }
813     curchar = p;
814     return isneg ? (-n) : n;
815 }
816 
817 /*
818  * Add a tag name.  This is a kludge to be able to refer
819  * to tags that have the same name as some other symbol
820  * in the same block.
821  */
822 
823 private addtag(s)
824 register Symbol s;
825 {
826     register Symbol t;
827     char buf[100];
828 
829     sprintf(buf, "$$%.90s", ident(s->name));
830     t = insert(identname(buf, false));
831     t->language = s->language;
832     t->class = TAG;
833     t->type = s->type;
834     t->block = s->block;
835 }
836 
837 /*
838  * Allocate file and line tables and initialize indices.
839  */
840 
841 private allocmaps(nf, nl)
842 Integer nf, nl;
843 {
844     if (filetab != nil) {
845 	dispose(filetab);
846     }
847     if (linetab != nil) {
848 	dispose(linetab);
849     }
850     filetab = newarr(Filetab, nf);
851     linetab = newarr(Linetab, nl);
852     filep = filetab;
853     linep = linetab;
854 }
855 
856 /*
857  * Add a file to the file table.
858  */
859 
860 private enterfile(filename, addr)
861 String filename;
862 Address addr;
863 {
864     if (addr != curfaddr) {
865 	filep->addr = addr;
866 	filep->filename = filename;
867 	filep->lineindex = linep - linetab;
868 	++filep;
869 	curfaddr = addr;
870     }
871 }
872 
873 /*
874  * Since we only estimated the number of lines (and it was a poor
875  * estimation) and since we need to know the exact number of lines
876  * to do a binary search, we set it when we're done.
877  */
878 
879 private setnlines()
880 {
881     nlhdr.nlines = linep - linetab;
882 }
883 
884 /*
885  * Similarly for nfiles ...
886  */
887 
888 private setnfiles()
889 {
890     nlhdr.nfiles = filep - filetab;
891     setsource(filetab[0].filename);
892 }
893