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