xref: /csrg-svn/old/dbx/object.c (revision 16631)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static	char sccsid[] = "@(#)object.c	1.15 (Berkeley) 06/23/84";
4 
5 /*
6  * Object code interface, mainly for extraction of symbolic information.
7  */
8 
9 #include "defs.h"
10 #include "object.h"
11 #include "stabstring.h"
12 #include "main.h"
13 #include "symbols.h"
14 #include "names.h"
15 #include "languages.h"
16 #include "mappings.h"
17 #include "lists.h"
18 #include <a.out.h>
19 #include <stab.h>
20 #include <ctype.h>
21 
22 #ifndef public
23 
24 struct {
25     unsigned int stringsize;	/* size of the dumped string table */
26     unsigned int nsyms;		/* number of symbols */
27     unsigned int nfiles;	/* number of files */
28     unsigned int nlines;	/* number of lines */
29 } nlhdr;
30 
31 #include "languages.h"
32 #include "symbols.h"
33 
34 #endif
35 
36 #ifndef N_MOD2
37 #    define N_MOD2 0x50
38 #endif
39 
40 public String objname = "a.out";
41 public integer objsize;
42 
43 public Language curlang;
44 public Symbol curmodule;
45 public Symbol curparam;
46 public Symbol curcomm;
47 public Symbol commchain;
48 
49 private char *stringtab;
50 private struct nlist *curnp;
51 private Boolean warned;
52 private Boolean strip_ = false;
53 
54 private Filetab *filep;
55 private Linetab *linep, *prevlinep;
56 
57 public String curfilename ()
58 {
59     return ((filep-1)->filename);
60 }
61 
62 /*
63  * Blocks are figured out on the fly while reading the symbol table.
64  */
65 
66 #define MAXBLKDEPTH 25
67 
68 public Symbol curblock;
69 
70 private Symbol blkstack[MAXBLKDEPTH];
71 private integer curlevel;
72 private integer bnum, nesting;
73 private Address addrstk[MAXBLKDEPTH];
74 
75 public pushBlock (b)
76 Symbol b;
77 {
78     if (curlevel >= MAXBLKDEPTH) {
79 	fatal("nesting depth too large (%d)", curlevel);
80     }
81     blkstack[curlevel] = curblock;
82     ++curlevel;
83     curblock = b;
84     if (traceblocks) {
85 	printf("entering block %s\n", symname(b));
86     }
87 }
88 
89 public enterblock (b)
90 Symbol b;
91 {
92     if (curblock == nil) {
93 	b->level = 1;
94     } else {
95 	b->level = curblock->level + 1;
96     }
97     b->block = curblock;
98     pushBlock(b);
99 }
100 
101 public exitblock ()
102 {
103     if (curblock->class == FUNC or curblock->class == PROC) {
104 	if (prevlinep != linep) {
105 	    curblock->symvalue.funcv.src = true;
106 	}
107     }
108     if (curlevel <= 0) {
109 	panic("nesting depth underflow (%d)", curlevel);
110     }
111     --curlevel;
112     if (traceblocks) {
113 	printf("exiting block %s\n", symname(curblock));
114     }
115     curblock = blkstack[curlevel];
116 }
117 
118 /*
119  * Enter a source line or file name reference into the appropriate table.
120  * Expanded inline to reduce procedure calls.
121  *
122  * private enterline (linenumber, address)
123  * Lineno linenumber;
124  * Address address;
125  *  ...
126  */
127 
128 #define enterline(linenumber, address) \
129 { \
130     register Linetab *lp; \
131  \
132     lp = linep - 1; \
133     if (linenumber != lp->line) { \
134 	if (address != lp->addr) { \
135 	    ++lp; \
136 	} \
137 	lp->line = linenumber; \
138 	lp->addr = address; \
139 	linep = lp + 1; \
140     } \
141 }
142 
143 /*
144  * Read in the namelist from the obj file.
145  *
146  * Reads and seeks are used instead of fread's and fseek's
147  * for efficiency sake; there's a lot of data being read here.
148  */
149 
150 public readobj (file)
151 String file;
152 {
153     Fileid f;
154     struct exec hdr;
155     struct nlist nlist;
156 
157     f = open(file, 0);
158     if (f < 0) {
159 	fatal("can't open %s", file);
160     }
161     read(f, &hdr, sizeof(hdr));
162     objsize = hdr.a_text;
163     nlhdr.nsyms = hdr.a_syms / sizeof(nlist);
164     nlhdr.nfiles = nlhdr.nsyms;
165     nlhdr.nlines = nlhdr.nsyms;
166     if (nlhdr.nsyms > 0) {
167 	lseek(f, (long) N_STROFF(hdr), 0);
168 	read(f, &(nlhdr.stringsize), sizeof(nlhdr.stringsize));
169 	nlhdr.stringsize -= 4;
170 	stringtab = newarr(char, nlhdr.stringsize);
171 	read(f, stringtab, nlhdr.stringsize);
172 	allocmaps(nlhdr.nfiles, nlhdr.nlines);
173 	lseek(f, (long) N_SYMOFF(hdr), 0);
174 	readsyms(f);
175 	ordfunctab();
176 	setnlines();
177 	setnfiles();
178     }
179     close(f);
180 }
181 
182 /*
183  * Read in symbols from object file.
184  */
185 
186 private readsyms (f)
187 Fileid f;
188 {
189     struct nlist *namelist;
190     register struct nlist *np, *ub;
191     register String name;
192     register Boolean afterlg;
193     integer index;
194     char *lastchar;
195 
196     initsyms();
197     namelist = newarr(struct nlist, nlhdr.nsyms);
198     read(f, namelist, nlhdr.nsyms * sizeof(struct nlist));
199     afterlg = false;
200     ub = &namelist[nlhdr.nsyms];
201     curnp = &namelist[0];
202     np = curnp;
203     while (np < ub) {
204 	index = np->n_un.n_strx;
205 	if (index != 0) {
206 	    name = &stringtab[index - 4];
207 	    /*
208              *  If the program contains any .f files a trailing _ is stripped
209        	     *  from the name on the assumption it was added by the compiler.
210 	     *  This only affects names that follow the sdb N_SO entry with
211              *  the .f name.
212              */
213             if (strip_ and name[0] != '\0' ) {
214 		lastchar = &name[strlen(name) - 1];
215 		if (*lastchar == '_') {
216 		    *lastchar = '\0';
217 		}
218             }
219 	} else {
220 	    name = nil;
221 	}
222 
223 	/*
224 	 * Assumptions:
225 	 *	not an N_STAB	==> name != nil
226 	 *	name[0] == '-'	==> name == "-lg"
227 	 *	name[0] != '_'	==> filename or invisible
228 	 *
229 	 * The "-lg" signals the beginning of global loader symbols.
230          *
231 	 */
232 	if ((np->n_type&N_STAB) != 0) {
233 	    enter_nl(name, np);
234 	} else if (name[0] == '-') {
235 	    afterlg = true;
236 	    if (curblock->class != PROG) {
237 		exitblock();
238 		if (curblock->class != PROG) {
239 		    exitblock();
240 		}
241 	    }
242 	    enterline(0, (linep-1)->addr + 1);
243 	} else if (afterlg) {
244 	    if (name[0] == '_') {
245 		check_global(&name[1], np);
246 	    }
247 	} else if (name[0] == '_') {
248 	    check_local(&name[1], np);
249 	} else if ((np->n_type&N_TEXT) == N_TEXT) {
250 	    check_filename(name);
251 	}
252 	++curnp;
253 	np = curnp;
254     }
255     if (not afterlg) {
256 	fatal("not linked for debugging, use \"cc -g ...\"");
257     }
258     dispose(namelist);
259 }
260 
261 /*
262  * Get a continuation entry from the name list.
263  * Return the beginning of the name.
264  */
265 
266 public String getcont ()
267 {
268     register integer index;
269     register String name;
270 
271     ++curnp;
272     index = curnp->n_un.n_strx;
273     if (index == 0) {
274 	panic("continuation followed by empty stab");
275     }
276     name = &stringtab[index - 4];
277     return name;
278 }
279 
280 /*
281  * Initialize symbol information.
282  */
283 
284 private initsyms ()
285 {
286     curblock = nil;
287     curlevel = 0;
288     nesting = 0;
289     program = insert(identname("", true));
290     program->class = PROG;
291     program->symvalue.funcv.beginaddr = 0;
292     program->symvalue.funcv.inline = false;
293     newfunc(program, codeloc(program));
294     findbeginning(program);
295     enterblock(program);
296     curmodule = program;
297     t_boolean = maketype("$boolean", 0L, 1L);
298     t_int = maketype("$integer", 0x80000000L, 0x7fffffffL);
299     t_char = maketype("$char", 0L, 255L);
300     t_real = maketype("$real", 8L, 0L);
301     t_nil = maketype("$nil", 0L, 0L);
302     t_open = maketype("integer", 0L, -1L);
303 }
304 
305 /*
306  * Free all the object file information that's being stored.
307  */
308 
309 public objfree ()
310 {
311     symbol_free();
312     keywords_free();
313     names_free();
314     dispose(stringtab);
315     clrfunctab();
316 }
317 
318 /*
319  * Enter a namelist entry.
320  */
321 
322 private enter_nl (name, np)
323 String name;
324 register struct nlist *np;
325 {
326     register Symbol s;
327     register Name n;
328 
329     s = nil;
330     switch (np->n_type) {
331 	/*
332 	 * Build a symbol for the FORTRAN common area.  All GSYMS that follow
333 	 * will be chained in a list with the head kept in common.offset, and
334 	 * the tail in common.chain.
335 	 */
336 	case N_BCOMM:
337  	    if (curcomm) {
338 		curcomm->symvalue.common.chain = commchain;
339 	    }
340 	    n = identname(name, true);
341 	    curcomm = lookup(n);
342 	    if (curcomm == nil) {
343 		curcomm = insert(n);
344 		curcomm->class = COMMON;
345 		curcomm->block = curblock;
346 		curcomm->level = program->level;
347 		curcomm->symvalue.common.chain = nil;
348 	    }
349 	    commchain = curcomm->symvalue.common.chain;
350 	    break;
351 
352 	case N_ECOMM:
353 	    if (curcomm) {
354 		curcomm->symvalue.common.chain = commchain;
355 		curcomm = nil;
356 	    }
357 	    break;
358 
359 	case N_LBRAC:
360 	    ++nesting;
361 	    addrstk[nesting] = (linep - 1)->addr;
362 	    break;
363 
364 	case N_RBRAC:
365 	    --nesting;
366 	    if (addrstk[nesting] == NOADDR) {
367 		exitblock();
368 		newfunc(curblock, (linep - 1)->addr);
369 		addrstk[nesting] = (linep - 1)->addr;
370 	    }
371 	    break;
372 
373 	case N_SLINE:
374 	    enterline((Lineno) np->n_desc, (Address) np->n_value);
375 	    break;
376 
377 	/*
378 	 * Source files.
379 	 */
380 	case N_SO:
381 	    n = identname(name, true);
382 	    enterSourceModule(n, (Address) np->n_value);
383 	    break;
384 
385 	/*
386 	 * Textually included files.
387 	 */
388 	case N_SOL:
389 	    enterfile(name, (Address) np->n_value);
390 	    break;
391 
392 	/*
393 	 * These symbols are assumed to have non-nil names.
394 	 */
395 	case N_GSYM:
396 	case N_FUN:
397 	case N_STSYM:
398 	case N_LCSYM:
399 	case N_RSYM:
400 	case N_PSYM:
401 	case N_LSYM:
402 	case N_SSYM:
403 	case N_LENG:
404 	    if (index(name, ':') == nil) {
405 		if (not warned) {
406 		    warned = true;
407 		    warning("old style symbol information found in \"%s\"",
408 			curfilename());
409 		}
410 	    } else {
411 		entersym(name, np);
412 	    }
413 	    break;
414 
415 	case N_PC:
416 	case N_MOD2:
417 	    break;
418 
419 	default:
420 	    printf("warning:  stab entry unrecognized: ");
421 	    if (name != nil) {
422 		printf("name %s,", name);
423 	    }
424 	    printf("ntype %2x, desc %x, value %x'\n",
425 		np->n_type, np->n_desc, np->n_value);
426 	    break;
427     }
428 }
429 
430 /*
431  * Try to find the symbol that is referred to by the given name.
432  * Since it's an external, we may want to follow a level of indirection.
433  */
434 
435 private Symbol findsym (n)
436 Name n;
437 {
438     register Symbol r, s;
439 
440     find(s, n) where
441 	s->level == program->level and
442 	    (s->class == EXTREF or s->class == VAR or
443 	     s->class == PROC or s->class == FUNC)
444     endfind(s);
445     if (s != nil and s->class == EXTREF) {
446 	r = s->symvalue.extref;
447 	delete(s);
448     } else {
449 	r = s;
450     }
451     return r;
452 }
453 
454 /*
455  * Check to see if a global _name is already in the symbol table,
456  * if not then insert it.
457  */
458 
459 private check_global (name, np)
460 String name;
461 register struct nlist *np;
462 {
463     register Name n;
464     register Symbol t, u;
465 
466     if (not streq(name, "end")) {
467 	n = identname(name, true);
468 	if ((np->n_type&N_TYPE) == N_TEXT) {
469 	    t = findsym(n);
470 	    if (t == nil) {
471 		t = insert(n);
472 		t->language = findlanguage(".s");
473 		t->class = FUNC;
474 		t->type = t_int;
475 		t->block = curblock;
476 		t->level = program->level;
477 		t->symvalue.funcv.src = false;
478 		t->symvalue.funcv.inline = false;
479 	    }
480 	    if (t->class == VAR) {
481 		t->symvalue.offset = np->n_value;
482 	    } else {
483 		t->symvalue.funcv.beginaddr = np->n_value;
484 		newfunc(t, codeloc(t));
485 		findbeginning(t);
486 	    }
487 	} else if ((np->n_type&N_TYPE) == N_BSS) {
488 	    find(t, n) where
489 		t->class == COMMON
490 	    endfind(t);
491 	    if (t != nil) {
492 		u = (Symbol) t->symvalue.common.offset;
493 		while (u != nil) {
494 		    u->symvalue.offset = u->symvalue.common.offset+np->n_value;
495 		    u = u->symvalue.common.chain;
496 		}
497             } else {
498 		check_var(np, n);
499 	    }
500         } else {
501 	    check_var(np, n);
502 	}
503     }
504 }
505 
506 /*
507  * Check to see if a namelist entry refers to a variable.
508  * If not, create a variable for the entry.  In any case,
509  * set the offset of the variable according to the value field
510  * in the entry.
511  */
512 
513 private check_var (np, n)
514 struct nlist *np;
515 register Name n;
516 {
517     register Symbol t;
518 
519     t = findsym(n);
520     if (t == nil) {
521 	t = insert(n);
522 	t->language = findlanguage(".s");
523 	t->class = VAR;
524 	t->type = t_int;
525 	t->level = program->level;
526 	t->block = curblock;
527     }
528     t->symvalue.offset = np->n_value;
529 }
530 
531 /*
532  * Check to see if a local _name is known in the current scope.
533  * If not then enter it.
534  */
535 
536 private check_local (name, np)
537 String name;
538 register struct nlist *np;
539 {
540     register Name n;
541     register Symbol t, cur;
542 
543     n = identname(name, true);
544     cur = ((np->n_type&N_TYPE) == N_TEXT) ? curmodule : curblock;
545     find(t, n) where t->block == cur endfind(t);
546     if (t == nil) {
547 	t = insert(n);
548 	t->language = findlanguage(".s");
549 	t->type = t_int;
550 	t->block = cur;
551 	t->level = cur->level;
552 	if ((np->n_type&N_TYPE) == N_TEXT) {
553 	    t->class = FUNC;
554 	    t->symvalue.funcv.src = false;
555 	    t->symvalue.funcv.inline = false;
556 	    t->symvalue.funcv.beginaddr = np->n_value;
557 	    newfunc(t, codeloc(t));
558 	    findbeginning(t);
559 	} else {
560 	    t->class = VAR;
561 	    t->symvalue.offset = np->n_value;
562 	}
563     }
564 }
565 
566 /*
567  * Check to see if a symbol corresponds to a object file name.
568  * For some reason these are listed as in the text segment.
569  */
570 
571 private check_filename (name)
572 String name;
573 {
574     register String mname;
575     register integer i;
576     register Symbol s;
577 
578     mname = strdup(name);
579     i = strlen(mname) - 2;
580     if (i >= 0 and mname[i] == '.' and mname[i+1] == 'o') {
581 	mname[i] = '\0';
582 	--i;
583 	while (mname[i] != '/' and i >= 0) {
584 	    --i;
585 	}
586 	s = insert(identname(&mname[i+1], true));
587 	s->language = findlanguage(".s");
588 	s->class = MODULE;
589 	s->symvalue.funcv.beginaddr = 0;
590 	findbeginning(s);
591 	if (curblock->class != PROG) {
592 	    exitblock();
593 	    if (curblock->class != PROG) {
594 		exitblock();
595 	    }
596 	}
597 	enterblock(s);
598 	curmodule = s;
599     }
600 }
601 
602 /*
603  * Check to see if a symbol is about to be defined within an unnamed block.
604  * If this happens, we create a procedure for the unnamed block, make it
605  * "inline" so that tracebacks don't associate an activation record with it,
606  * and enter it into the function table so that it will be detected
607  * by "whatblock".
608  */
609 
610 public chkUnnamedBlock ()
611 {
612     register Symbol s;
613     static int bnum = 0;
614     char buf[100];
615     Address startaddr;
616 
617     if (nesting > 0 and addrstk[nesting] != NOADDR) {
618 	startaddr = (linep - 1)->addr;
619 	++bnum;
620 	sprintf(buf, "$b%d", bnum);
621 	s = insert(identname(buf, false));
622 	s->language = curlang;
623 	s->class = PROC;
624 	s->symvalue.funcv.src = false;
625 	s->symvalue.funcv.inline = true;
626 	s->symvalue.funcv.beginaddr = startaddr;
627 	enterblock(s);
628 	newfunc(s, startaddr);
629 	addrstk[nesting] = NOADDR;
630     }
631 }
632 
633 /*
634  * Compilation unit.  C associates scope with filenames
635  * so we treat them as "modules".  The filename without
636  * the suffix is used for the module name.
637  *
638  * Because there is no explicit "end-of-block" mark in
639  * the object file, we must exit blocks for the current
640  * procedure and module.
641  */
642 
643 private enterSourceModule (n, addr)
644 Name n;
645 Address addr;
646 {
647     register Symbol s;
648     Name nn;
649     String mname, suffix;
650 
651     mname = strdup(ident(n));
652     if (rindex(mname, '/') != nil) {
653 	mname = rindex(mname, '/') + 1;
654     }
655     suffix = rindex(mname, '.');
656     curlang = findlanguage(suffix);
657     if (curlang == findlanguage(".f")) {
658 	strip_ = true;
659     }
660     if (suffix != nil) {
661 	*suffix = '\0';
662     }
663     if (not (*language_op(curlang, L_HASMODULES))()) {
664 	if (curblock->class != PROG) {
665 	    exitblock();
666 	    if (curblock->class != PROG) {
667 		exitblock();
668 	    }
669 	}
670 	nn = identname(mname, true);
671 	if (curmodule == nil or curmodule->name != nn) {
672 	    s = insert(nn);
673 	    s->class = MODULE;
674 	    s->symvalue.funcv.beginaddr = 0;
675 	    findbeginning(s);
676 	} else {
677 	    s = curmodule;
678 	}
679 	s->language = curlang;
680 	enterblock(s);
681 	curmodule = s;
682     }
683     if (program->language == nil) {
684 	program->language = curlang;
685     }
686     warned = false;
687     enterfile(ident(n), addr);
688     initTypeTable();
689 }
690 
691 /*
692  * Allocate file and line tables and initialize indices.
693  */
694 
695 private allocmaps (nf, nl)
696 integer nf, nl;
697 {
698     if (filetab != nil) {
699 	dispose(filetab);
700     }
701     if (linetab != nil) {
702 	dispose(linetab);
703     }
704     filetab = newarr(Filetab, nf);
705     linetab = newarr(Linetab, nl);
706     filep = filetab;
707     linep = linetab;
708 }
709 
710 /*
711  * Add a file to the file table.
712  *
713  * If the new address is the same as the previous file address
714  * this routine used to not enter the file, but this caused some
715  * problems so it has been removed.  It's not clear that this in
716  * turn may not also cause a problem.
717  */
718 
719 private enterfile (filename, addr)
720 String filename;
721 Address addr;
722 {
723     filep->addr = addr;
724     filep->filename = filename;
725     filep->lineindex = linep - linetab;
726     ++filep;
727 }
728 
729 /*
730  * Since we only estimated the number of lines (and it was a poor
731  * estimation) and since we need to know the exact number of lines
732  * to do a binary search, we set it when we're done.
733  */
734 
735 private setnlines ()
736 {
737     nlhdr.nlines = linep - linetab;
738 }
739 
740 /*
741  * Similarly for nfiles ...
742  */
743 
744 private setnfiles ()
745 {
746     nlhdr.nfiles = filep - filetab;
747     setsource(filetab[0].filename);
748 }
749