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