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