xref: /inferno-os/utils/8l/obj.c (revision dea4b9f1bb02529d02b0389c1f6b0e514ed7deff)
1 #define	EXTERN
2 #include	"l.h"
3 #include	<ar.h>
4 
5 #ifndef	DEFAULT
6 #define	DEFAULT	'9'
7 #endif
8 
9 char	*noname		= "<none>";
10 char	symname[]	= SYMDEF;
11 char	thechar		= '8';
12 char	*thestring 	= "386";
13 
14 char**	libdir;
15 int	nlibdir	= 0;
16 static	int	maxlibdir = 0;
17 
18 /*
19  *	-H0 -T0x40004C -D0x10000000	is garbage unix
20  *	-H1 -T0xd0 -R4			is unix coff
21  *	-H2 -T4128 -R4096		is plan9 format
22  *	-H3 -Tx -Rx			is MS-DOS .COM
23  *	-H4 -Tx -Rx			is fake MS-DOS .EXE
24  *	-H5 -T0x80100020 -R4096		is ELF
25  */
26 
27 void
28 usage(void)
29 {
30 	diag("usage: %s [-options] objects", argv0);
31 	errorexit();
32 }
33 
34 static int
35 isobjfile(char *f)
36 {
37 	int n, v;
38 	Biobuf *b;
39 	char buf1[5], buf2[SARMAG];
40 
41 	b = Bopen(f, OREAD);
42 	if(b == nil)
43 		return 0;
44 	n = Bread(b, buf1, 5);
45 	if(n == 5 && (buf1[2] == 1 && buf1[3] == '<' || buf1[3] == 1 && buf1[4] == '<'))
46 		v = 1;	/* good enough for our purposes */
47 	else{
48 		Bseek(b, 0, 0);
49 		n = Bread(b, buf2, SARMAG);
50 		v = n == SARMAG && strncmp(buf2, ARMAG, SARMAG) == 0;
51 	}
52 	Bterm(b);
53 	return v;
54 }
55 
56 void
57 main(int argc, char *argv[])
58 {
59 	int i, c;
60 	char *a;
61 	char name[LIBNAMELEN];
62 
63 	Binit(&bso, 1, OWRITE);
64 	cout = -1;
65 	listinit();
66 	memset(debug, 0, sizeof(debug));
67 	nerrors = 0;
68 	outfile = "8.out";
69 	HEADTYPE = -1;
70 	INITTEXT = -1;
71 	INITTEXTP = -1;
72 	INITDAT = -1;
73 	INITRND = -1;
74 	INITENTRY = 0;
75 	ARGBEGIN {
76 	default:
77 		c = ARGC();
78 		if(c >= 0 && c < sizeof(debug))
79 			debug[c]++;
80 		break;
81 	case 'o': /* output to (next arg) */
82 		outfile = ARGF();
83 		break;
84 	case 'E':
85 		a = ARGF();
86 		if(a)
87 			INITENTRY = a;
88 		break;
89 	case 'H':
90 		a = ARGF();
91 		if(a)
92 			HEADTYPE = atolwhex(a);
93 		break;
94 	case 'L':
95 		addlibpath(EARGF(usage()));
96 		break;
97 	case 'T':
98 		a = ARGF();
99 		if(a)
100 			INITTEXT = atolwhex(a);
101 		break;
102 	case 'P':
103 		a = ARGF();
104 		if(a)
105 			INITTEXTP = atolwhex(a);
106 		break;
107 	case 'D':
108 		a = ARGF();
109 		if(a)
110 			INITDAT = atolwhex(a);
111 		break;
112 	case 'R':
113 		a = ARGF();
114 		if(a)
115 			INITRND = atolwhex(a);
116 		break;
117 	case 'x':	/* produce export table */
118 		doexp = 1;
119 		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1])){
120 			a = ARGF();
121 			if(strcmp(a, "*") == 0)
122 				allexport = 1;
123 			else
124 				readundefs(a, SEXPORT);
125 		}
126 		break;
127 	case 'u':	/* produce dynamically loadable module */
128 		dlm = 1;
129 		debug['l']++;
130 		if(argv[1] != nil && argv[1][0] != '-' && !isobjfile(argv[1]))
131 			readundefs(ARGF(), SIMPORT);
132 		break;
133 	} ARGEND
134 	USED(argc);
135 	if(*argv == 0)
136 		usage();
137 	if(!debug['9'] && !debug['U'] && !debug['B'])
138 		debug[DEFAULT] = 1;
139 	a = getenv("ccroot");
140 	if(a != nil && *a != '\0') {
141 		if(!fileexists(a)) {
142 			diag("nonexistent $ccroot: %s", a);
143 			errorexit();
144 		}
145 	}else
146 		a = "";
147 	snprint(name, sizeof(name), "%s/%s/lib", a, thestring);
148 	addlibpath(name);
149 	if(HEADTYPE == -1) {
150 		if(debug['U'])
151 			HEADTYPE = 1;
152 		if(debug['B'])
153 			HEADTYPE = 2;
154 		if(debug['9'])
155 			HEADTYPE = 2;
156 	}
157 	switch(HEADTYPE) {
158 	default:
159 		diag("unknown -H option");
160 		errorexit();
161 
162 	case 0:	/* this is garbage */
163 		HEADR = 20L+56L;
164 		if(INITTEXT == -1)
165 			INITTEXT = 0x40004CL;
166 		if(INITDAT == -1)
167 			INITDAT = 0x10000000L;
168 		if(INITRND == -1)
169 			INITRND = 0;
170 		break;
171 	case 1:	/* is unix coff */
172 		HEADR = 0xd0L;
173 		if(INITTEXT == -1)
174 			INITTEXT = 0xd0;
175 		if(INITDAT == -1)
176 			INITDAT = 0x400000;
177 		if(INITRND == -1)
178 			INITRND = 0;
179 		break;
180 	case 2:	/* plan 9 */
181 		HEADR = 32L;
182 		if(INITTEXT == -1)
183 			INITTEXT = 4096+32;
184 		if(INITDAT == -1)
185 			INITDAT = 0;
186 		if(INITRND == -1)
187 			INITRND = 4096;
188 		break;
189 	case 3:	/* MS-DOS .COM */
190 		HEADR = 0;
191 		if(INITTEXT == -1)
192 			INITTEXT = 0x0100;
193 		if(INITDAT == -1)
194 			INITDAT = 0;
195 		if(INITRND == -1)
196 			INITRND = 4;
197 		break;
198 	case 4:	/* fake MS-DOS .EXE */
199 		HEADR = 0x200;
200 		if(INITTEXT == -1)
201 			INITTEXT = 0x0100;
202 		if(INITDAT == -1)
203 			INITDAT = 0;
204 		if(INITRND == -1)
205 			INITRND = 4;
206 		HEADR += (INITTEXT & 0xFFFF);
207 		if(debug['v'])
208 			Bprint(&bso, "HEADR = 0x%ld\n", HEADR);
209 		break;
210 	case 5:	/* elf executable */
211 		HEADR = rnd(Ehdr32sz+3*Phdr32sz, 16);
212 		if(INITTEXT == -1)
213 			INITTEXT = 0x80100020L;
214 		if(INITDAT == -1)
215 			INITDAT = 0;
216 		if(INITRND == -1)
217 			INITRND = 4096;
218 		break;
219 	}
220 	if (INITTEXTP == -1)
221 		INITTEXTP = INITTEXT;
222 	if(INITDAT != 0 && INITRND != 0)
223 		print("warning: -D0x%lux is ignored because of -R0x%lux\n",
224 			INITDAT, INITRND);
225 	if(debug['v'])
226 		Bprint(&bso, "HEADER = -H0x%ld -T0x%lux -D0x%lux -R0x%lux\n",
227 			HEADTYPE, INITTEXT, INITDAT, INITRND);
228 	Bflush(&bso);
229 	for(i=1; optab[i].as; i++)
230 		if(i != optab[i].as) {
231 			diag("phase error in optab: %d", i);
232 			errorexit();
233 		}
234 
235 	for(i=0; i<Ymax; i++)
236 		ycover[i*Ymax + i] = 1;
237 
238 	ycover[Yi0*Ymax + Yi8] = 1;
239 	ycover[Yi1*Ymax + Yi8] = 1;
240 
241 	ycover[Yi0*Ymax + Yi32] = 1;
242 	ycover[Yi1*Ymax + Yi32] = 1;
243 	ycover[Yi8*Ymax + Yi32] = 1;
244 
245 	ycover[Yal*Ymax + Yrb] = 1;
246 	ycover[Ycl*Ymax + Yrb] = 1;
247 	ycover[Yax*Ymax + Yrb] = 1;
248 	ycover[Ycx*Ymax + Yrb] = 1;
249 	ycover[Yrx*Ymax + Yrb] = 1;
250 
251 	ycover[Yax*Ymax + Yrx] = 1;
252 	ycover[Ycx*Ymax + Yrx] = 1;
253 
254 	ycover[Yax*Ymax + Yrl] = 1;
255 	ycover[Ycx*Ymax + Yrl] = 1;
256 	ycover[Yrx*Ymax + Yrl] = 1;
257 
258 	ycover[Yf0*Ymax + Yrf] = 1;
259 
260 	ycover[Yal*Ymax + Ymb] = 1;
261 	ycover[Ycl*Ymax + Ymb] = 1;
262 	ycover[Yax*Ymax + Ymb] = 1;
263 	ycover[Ycx*Ymax + Ymb] = 1;
264 	ycover[Yrx*Ymax + Ymb] = 1;
265 	ycover[Yrb*Ymax + Ymb] = 1;
266 	ycover[Ym*Ymax + Ymb] = 1;
267 
268 	ycover[Yax*Ymax + Yml] = 1;
269 	ycover[Ycx*Ymax + Yml] = 1;
270 	ycover[Yrx*Ymax + Yml] = 1;
271 	ycover[Yrl*Ymax + Yml] = 1;
272 	ycover[Ym*Ymax + Yml] = 1;
273 
274 	for(i=0; i<D_NONE; i++) {
275 		reg[i] = -1;
276 		if(i >= D_AL && i <= D_BH)
277 			reg[i] = (i-D_AL) & 7;
278 		if(i >= D_AX && i <= D_DI)
279 			reg[i] = (i-D_AX) & 7;
280 		if(i >= D_F0 && i <= D_F0+7)
281 			reg[i] = (i-D_F0) & 7;
282 	}
283 
284 	zprg.link = P;
285 	zprg.pcond = P;
286 	zprg.back = 2;
287 	zprg.as = AGOK;
288 	zprg.from.type = D_NONE;
289 	zprg.from.index = D_NONE;
290 	zprg.from.scale = 1;
291 	zprg.to = zprg.from;
292 
293 	pcstr = "%.6lux ";
294 	nuxiinit();
295 	histgen = 0;
296 	textp = P;
297 	datap = P;
298 	edatap = P;
299 	pc = 0;
300 	dtype = 4;
301 	cout = create(outfile, 1, 0775);
302 	if(cout < 0) {
303 		diag("cannot create %s: %r", outfile);
304 		errorexit();
305 	}
306 	version = 0;
307 	cbp = buf.cbuf;
308 	cbc = sizeof(buf.cbuf);
309 	firstp = prg();
310 	lastp = firstp;
311 
312 	if(INITENTRY == 0) {
313 		INITENTRY = "_main";
314 		if(debug['p'])
315 			INITENTRY = "_mainp";
316 		if(!debug['l'])
317 			lookup(INITENTRY, 0)->type = SXREF;
318 	} else if(!(*INITENTRY >= '0' && *INITENTRY <= '9'))
319 		lookup(INITENTRY, 0)->type = SXREF;
320 
321 	while(*argv)
322 		objfile(*argv++);
323 	if(!debug['l'])
324 		loadlib();
325 	firstp = firstp->link;
326 	if(firstp == P)
327 		errorexit();
328 	if(doexp || dlm){
329 		EXPTAB = "_exporttab";
330 		zerosig(EXPTAB);
331 		zerosig("etext");
332 		zerosig("edata");
333 		zerosig("end");
334 		if(dlm){
335 			import();
336 			HEADTYPE = 2;
337 			INITTEXT = INITDAT = 0;
338 			INITRND = 8;
339 			INITENTRY = EXPTAB;
340 		}
341 		export();
342 	}
343 	patch();
344 	follow();
345 	dodata();
346 	dostkoff();
347 	if(debug['p'])
348 		if(debug['1'])
349 			doprof1();
350 		else
351 			doprof2();
352 	span();
353 	doinit();
354 	asmb();
355 	undef();
356 	if(debug['v']) {
357 		Bprint(&bso, "%5.2f cpu time\n", cputime());
358 		Bprint(&bso, "%ld symbols\n", nsymbol);
359 		Bprint(&bso, "%ld memory used\n", thunk);
360 		Bprint(&bso, "%d sizeof adr\n", sizeof(Adr));
361 		Bprint(&bso, "%d sizeof prog\n", sizeof(Prog));
362 	}
363 	Bflush(&bso);
364 
365 	errorexit();
366 }
367 
368 void
369 addlibpath(char *arg)
370 {
371 	char **p;
372 
373 	if(nlibdir >= maxlibdir) {
374 		if(maxlibdir == 0)
375 			maxlibdir = 8;
376 		else
377 			maxlibdir *= 2;
378 		p = malloc(maxlibdir*sizeof(*p));
379 		if(p == nil) {
380 			diag("out of memory");
381 			errorexit();
382 		}
383 		memmove(p, libdir, nlibdir*sizeof(*p));
384 		free(libdir);
385 		libdir = p;
386 	}
387 	libdir[nlibdir++] = strdup(arg);
388 }
389 
390 char*
391 findlib(char *file)
392 {
393 	int i;
394 	char name[LIBNAMELEN];
395 
396 	for(i = 0; i < nlibdir; i++) {
397 		snprint(name, sizeof(name), "%s/%s", libdir[i], file);
398 		if(fileexists(name))
399 			return libdir[i];
400 	}
401 	return nil;
402 }
403 
404 void
405 loadlib(void)
406 {
407 	int i;
408 	long h;
409 	Sym *s;
410 
411 loop:
412 	xrefresolv = 0;
413 	for(i=0; i<libraryp; i++) {
414 		if(debug['v'])
415 			Bprint(&bso, "%5.2f autolib: %s (from %s)\n", cputime(), library[i], libraryobj[i]);
416 		objfile(library[i]);
417 	}
418 	if(xrefresolv)
419 	for(h=0; h<nelem(hash); h++)
420 	for(s = hash[h]; s != S; s = s->link)
421 		if(s->type == SXREF)
422 			goto loop;
423 }
424 
425 void
426 errorexit(void)
427 {
428 
429 	if(nerrors) {
430 		if(cout >= 0)
431 			remove(outfile);
432 		exits("error");
433 	}
434 	exits(0);
435 }
436 
437 void
438 objfile(char *file)
439 {
440 	long off, esym, cnt, l;
441 	int f, work;
442 	Sym *s;
443 	char magbuf[SARMAG];
444 	char name[LIBNAMELEN], pname[LIBNAMELEN];
445 	struct ar_hdr arhdr;
446 	char *e, *start, *stop;
447 
448 	if(debug['v'])
449 		Bprint(&bso, "%5.2f ldobj: %s\n", cputime(), file);
450 	Bflush(&bso);
451 	if(file[0] == '-' && file[1] == 'l') {
452 		snprint(pname, sizeof(pname), "lib%s.a", file+2);
453 		e = findlib(pname);
454 		if(e == nil) {
455 			diag("cannot find library: %s", file);
456 			errorexit();
457 		}
458 		snprint(name, sizeof(name), "%s/%s", e, pname);
459 		file = name;
460 	}
461 	f = open(file, 0);
462 	if(f < 0) {
463 		diag("cannot open %s: %r", file);
464 		errorexit();
465 	}
466 	l = read(f, magbuf, SARMAG);
467 	if(l != SARMAG || strncmp(magbuf, ARMAG, SARMAG)){
468 		/* load it as a regular file */
469 		l = seek(f, 0L, 2);
470 		seek(f, 0L, 0);
471 		ldobj(f, l, file);
472 		close(f);
473 		return;
474 	}
475 
476 	l = read(f, &arhdr, SAR_HDR);
477 	if(l != SAR_HDR) {
478 		diag("%s: short read on archive file symbol header", file);
479 		goto out;
480 	}
481 	if(strncmp(arhdr.name, symname, strlen(symname))) {
482 		diag("%s: first entry not symbol header", file);
483 		goto out;
484 	}
485 
486 	esym = SARMAG + SAR_HDR + atolwhex(arhdr.size);
487 	off = SARMAG + SAR_HDR;
488 
489 	/*
490 	 * just bang the whole symbol file into memory
491 	 */
492 	seek(f, off, 0);
493 	cnt = esym - off;
494 	start = malloc(cnt + 10);
495 	cnt = read(f, start, cnt);
496 	if(cnt <= 0){
497 		close(f);
498 		return;
499 	}
500 	stop = &start[cnt];
501 	memset(stop, 0, 10);
502 
503 	work = 1;
504 	while(work) {
505 		if(debug['v'])
506 			Bprint(&bso, "%5.2f library pass: %s\n", cputime(), file);
507 		Bflush(&bso);
508 		work = 0;
509 		for(e = start; e < stop; e = strchr(e+5, 0) + 1) {
510 			s = lookup(e+5, 0);
511 			if(s->type != SXREF)
512 				continue;
513 			sprint(pname, "%s(%s)", file, s->name);
514 			if(debug['v'])
515 				Bprint(&bso, "%5.2f library: %s\n", cputime(), pname);
516 			Bflush(&bso);
517 			l = e[1] & 0xff;
518 			l |= (e[2] & 0xff) << 8;
519 			l |= (e[3] & 0xff) << 16;
520 			l |= (e[4] & 0xff) << 24;
521 			seek(f, l, 0);
522 			/* need readn to read the dumps (at least) */
523 			l = readn(f, &arhdr, SAR_HDR);
524 			if(l != SAR_HDR)
525 				goto bad;
526 			if(strncmp(arhdr.fmag, ARFMAG, sizeof(arhdr.fmag)))
527 				goto bad;
528 			l = atolwhex(arhdr.size);
529 			ldobj(f, l, pname);
530 			if(s->type == SXREF) {
531 				diag("%s: failed to load: %s", file, s->name);
532 				errorexit();
533 			}
534 			work = 1;
535 			xrefresolv = 1;
536 		}
537 	}
538 	return;
539 
540 bad:
541 	diag("%s: bad or out of date archive", file);
542 out:
543 	close(f);
544 }
545 
546 int
547 zaddr(uchar *p, Adr *a, Sym *h[])
548 {
549 	int c, t, i;
550 	int l;
551 	Sym *s;
552 	Auto *u;
553 
554 	t = p[0];
555 
556 	c = 1;
557 	if(t & T_INDEX) {
558 		a->index = p[c];
559 		a->scale = p[c+1];
560 		c += 2;
561 	} else {
562 		a->index = D_NONE;
563 		a->scale = 0;
564 	}
565 	a->offset = 0;
566 	if(t & T_OFFSET) {
567 		a->offset = p[c] | (p[c+1]<<8) | (p[c+2]<<16) | (p[c+3]<<24);
568 		c += 4;
569 	}
570 	a->sym = S;
571 	if(t & T_SYM) {
572 		a->sym = h[p[c]];
573 		c++;
574 	}
575 	a->type = D_NONE;
576 	if(t & T_FCONST) {
577 		a->ieee.l = p[c] | (p[c+1]<<8) | (p[c+2]<<16) | (p[c+3]<<24);
578 		a->ieee.h = p[c+4] | (p[c+5]<<8) | (p[c+6]<<16) | (p[c+7]<<24);
579 		c += 8;
580 		a->type = D_FCONST;
581 	} else
582 	if(t & T_SCONST) {
583 		for(i=0; i<NSNAME; i++)
584 			a->scon[i] = p[c+i];
585 		c += NSNAME;
586 		a->type = D_SCONST;
587 	}
588 	if(t & T_TYPE) {
589 		a->type = p[c];
590 		c++;
591 	}
592 	s = a->sym;
593 	if(s == S)
594 		return c;
595 
596 	t = a->type;
597 	if(t != D_AUTO && t != D_PARAM)
598 		return c;
599 	l = a->offset;
600 	for(u=curauto; u; u=u->link) {
601 		if(u->asym == s)
602 		if(u->type == t) {
603 			if(u->aoffset > l)
604 				u->aoffset = l;
605 			return c;
606 		}
607 	}
608 
609 	while(nhunk < sizeof(Auto))
610 		gethunk();
611 	u = (Auto*)hunk;
612 	nhunk -= sizeof(Auto);
613 	hunk += sizeof(Auto);
614 
615 	u->link = curauto;
616 	curauto = u;
617 	u->asym = s;
618 	u->aoffset = l;
619 	u->type = t;
620 	return c;
621 }
622 
623 void
624 addlib(char *obj)
625 {
626 	char fn1[LIBNAMELEN], fn2[LIBNAMELEN], comp[LIBNAMELEN], *p, *name;
627 	int i, search;
628 
629 	if(histfrogp <= 0)
630 		return;
631 
632 	name = fn1;
633 	search = 0;
634 	if(histfrog[0]->name[1] == '/') {
635 		sprint(name, "");
636 		i = 1;
637 	} else if(histfrog[0]->name[1] == '.') {
638 		sprint(name, ".");
639 		i = 0;
640 	} else {
641 		sprint(name, "");
642 		i = 0;
643 		search = 1;
644 	}
645 
646 	for(; i<histfrogp; i++) {
647 		snprint(comp, sizeof comp, histfrog[i]->name+1);
648 		for(;;) {
649 			p = strstr(comp, "$O");
650 			if(p == 0)
651 				break;
652 			memmove(p+1, p+2, strlen(p+2)+1);
653 			p[0] = thechar;
654 		}
655 		for(;;) {
656 			p = strstr(comp, "$M");
657 			if(p == 0)
658 				break;
659 			if(strlen(comp)+strlen(thestring)-2+1 >= sizeof comp) {
660 				diag("library component too long");
661 				return;
662 			}
663 			memmove(p+strlen(thestring), p+2, strlen(p+2)+1);
664 			memmove(p, thestring, strlen(thestring));
665 		}
666 		if(strlen(fn1) + strlen(comp) + 3 >= sizeof(fn1)) {
667 			diag("library component too long");
668 			return;
669 		}
670 		if(i > 0 || !search)
671 			strcat(fn1, "/");
672 		strcat(fn1, comp);
673 	}
674 
675 	cleanname(name);
676 
677 	if(search){
678 		p = findlib(name);
679 		if(p != nil){
680 			snprint(fn2, sizeof(fn2), "%s/%s", p, name);
681 			name = fn2;
682 		}
683 	}
684 
685 	for(i=0; i<libraryp; i++)
686 		if(strcmp(name, library[i]) == 0)
687 			return;
688 	if(libraryp == nelem(library)){
689 		diag("too many autolibs; skipping %s", name);
690 		return;
691 	}
692 
693 	p = malloc(strlen(name) + 1);
694 	strcpy(p, name);
695 	library[libraryp] = p;
696 	p = malloc(strlen(obj) + 1);
697 	strcpy(p, obj);
698 	libraryobj[libraryp] = p;
699 	libraryp++;
700 }
701 
702 void
703 addhist(long line, int type)
704 {
705 	Auto *u;
706 	Sym *s;
707 	int i, j, k;
708 
709 	u = malloc(sizeof(Auto));
710 	s = malloc(sizeof(Sym));
711 	s->name = malloc(2*(histfrogp+1) + 1);
712 
713 	u->asym = s;
714 	u->type = type;
715 	u->aoffset = line;
716 	u->link = curhist;
717 	curhist = u;
718 
719 	j = 1;
720 	for(i=0; i<histfrogp; i++) {
721 		k = histfrog[i]->value;
722 		s->name[j+0] = k>>8;
723 		s->name[j+1] = k;
724 		j += 2;
725 	}
726 }
727 
728 void
729 histtoauto(void)
730 {
731 	Auto *l;
732 
733 	while(l = curhist) {
734 		curhist = l->link;
735 		l->link = curauto;
736 		curauto = l;
737 	}
738 }
739 
740 void
741 collapsefrog(Sym *s)
742 {
743 	int i;
744 
745 	/*
746 	 * bad encoding of path components only allows
747 	 * MAXHIST components. if there is an overflow,
748 	 * first try to collapse xxx/..
749 	 */
750 	for(i=1; i<histfrogp; i++)
751 		if(strcmp(histfrog[i]->name+1, "..") == 0) {
752 			memmove(histfrog+i-1, histfrog+i+1,
753 				(histfrogp-i-1)*sizeof(histfrog[0]));
754 			histfrogp--;
755 			goto out;
756 		}
757 
758 	/*
759 	 * next try to collapse .
760 	 */
761 	for(i=0; i<histfrogp; i++)
762 		if(strcmp(histfrog[i]->name+1, ".") == 0) {
763 			memmove(histfrog+i, histfrog+i+1,
764 				(histfrogp-i-1)*sizeof(histfrog[0]));
765 			goto out;
766 		}
767 
768 	/*
769 	 * last chance, just truncate from front
770 	 */
771 	memmove(histfrog+0, histfrog+1,
772 		(histfrogp-1)*sizeof(histfrog[0]));
773 
774 out:
775 	histfrog[histfrogp-1] = s;
776 }
777 
778 void
779 nopout(Prog *p)
780 {
781 	p->as = ANOP;
782 	p->from.type = D_NONE;
783 	p->to.type = D_NONE;
784 }
785 
786 uchar*
787 readsome(int f, uchar *buf, uchar *good, uchar *stop, int max)
788 {
789 	int n;
790 
791 	n = stop - good;
792 	memmove(buf, good, stop - good);
793 	stop = buf + n;
794 	n = MAXIO - n;
795 	if(n > max)
796 		n = max;
797 	n = read(f, stop, n);
798 	if(n <= 0)
799 		return 0;
800 	return stop + n;
801 }
802 
803 void
804 ldobj(int f, long c, char *pn)
805 {
806 	long ipc;
807 	Prog *p, *t;
808 	uchar *bloc, *bsize, *stop;
809 	int v, o, r, skip;
810 	Sym *h[NSYM], *s, *di;
811 	ulong sig;
812 	static int files;
813 	static char **filen;
814 	char **nfilen;
815 
816 	if((files&15) == 0){
817 		nfilen = malloc((files+16)*sizeof(char*));
818 		memmove(nfilen, filen, files*sizeof(char*));
819 		free(filen);
820 		filen = nfilen;
821 	}
822 	filen[files++] = strdup(pn);
823 
824 	bsize = buf.xbuf;
825 	bloc = buf.xbuf;
826 	di = S;
827 
828 newloop:
829 	memset(h, 0, sizeof(h));
830 	version++;
831 	histfrogp = 0;
832 	ipc = pc;
833 	skip = 0;
834 
835 loop:
836 	if(c <= 0)
837 		goto eof;
838 	r = bsize - bloc;
839 	if(r < 100 && r < c) {		/* enough for largest prog */
840 		bsize = readsome(f, buf.xbuf, bloc, bsize, c);
841 		if(bsize == 0)
842 			goto eof;
843 		bloc = buf.xbuf;
844 		goto loop;
845 	}
846 	o = bloc[0] | (bloc[1] << 8);
847 	if(o <= AXXX || o >= ALAST) {
848 		if(o < 0)
849 			goto eof;
850 		diag("%s: opcode out of range %d", pn, o);
851 		print("	probably not a .8 file\n");
852 		errorexit();
853 	}
854 
855 	if(o == ANAME || o == ASIGNAME) {
856 		sig = 0;
857 		if(o == ASIGNAME) {
858 			sig = bloc[2] | (bloc[3]<<8) | (bloc[4]<<16) | (bloc[5]<<24);
859 			bloc += 4;
860 			c -= 4;
861 		}
862 		stop = memchr(&bloc[4], 0, bsize-&bloc[4]);
863 		if(stop == 0){
864 			bsize = readsome(f, buf.xbuf, bloc, bsize, c);
865 			if(bsize == 0)
866 				goto eof;
867 			bloc = buf.xbuf;
868 			stop = memchr(&bloc[4], 0, bsize-&bloc[4]);
869 			if(stop == 0){
870 				fprint(2, "%s: name too long\n", pn);
871 				errorexit();
872 			}
873 		}
874 		v = bloc[2];	/* type */
875 		o = bloc[3];	/* sym */
876 		bloc += 4;
877 		c -= 4;
878 
879 		r = 0;
880 		if(v == D_STATIC)
881 			r = version;
882 		s = lookup((char*)bloc, r);
883 		c -= &stop[1] - bloc;
884 		bloc = stop + 1;
885 
886 		if(debug['S'] && r == 0)
887 			sig = 1729;
888 		if(sig != 0){
889 			if(s->sig != 0 && s->sig != sig)
890 				diag("incompatible type signatures %lux(%s) and %lux(%s) for %s", s->sig, filen[s->file], sig, pn, s->name);
891 			s->sig = sig;
892 			s->file = files-1;
893 		}
894 
895 		if(debug['W'])
896 			print("	ANAME	%s\n", s->name);
897 		h[o] = s;
898 		if((v == D_EXTERN || v == D_STATIC) && s->type == 0)
899 			s->type = SXREF;
900 		if(v == D_FILE) {
901 			if(s->type != SFILE) {
902 				histgen++;
903 				s->type = SFILE;
904 				s->value = histgen;
905 			}
906 			if(histfrogp < MAXHIST) {
907 				histfrog[histfrogp] = s;
908 				histfrogp++;
909 			} else
910 				collapsefrog(s);
911 		}
912 		goto loop;
913 	}
914 
915 	while(nhunk < sizeof(Prog))
916 		gethunk();
917 	p = (Prog*)hunk;
918 	nhunk -= sizeof(Prog);
919 	hunk += sizeof(Prog);
920 
921 	p->as = o;
922 	p->line = bloc[2] | (bloc[3] << 8) | (bloc[4] << 16) | (bloc[5] << 24);
923 	p->back = 2;
924 	r = zaddr(bloc+6, &p->from, h) + 6;
925 	r += zaddr(bloc+r, &p->to, h);
926 	bloc += r;
927 	c -= r;
928 
929 	if(debug['W'])
930 		print("%P\n", p);
931 
932 	switch(p->as) {
933 	case AHISTORY:
934 		if(p->to.offset == -1) {
935 			addlib(pn);
936 			histfrogp = 0;
937 			goto loop;
938 		}
939 		addhist(p->line, D_FILE);		/* 'z' */
940 		if(p->to.offset)
941 			addhist(p->to.offset, D_FILE1);	/* 'Z' */
942 		histfrogp = 0;
943 		goto loop;
944 
945 	case AEND:
946 		histtoauto();
947 		if(curtext != P)
948 			curtext->to.autom = curauto;
949 		curauto = 0;
950 		curtext = P;
951 		if(c)
952 			goto newloop;
953 		return;
954 
955 	case AGLOBL:
956 		s = p->from.sym;
957 		if(s->type == 0 || s->type == SXREF) {
958 			s->type = SBSS;
959 			s->value = 0;
960 		}
961 		if(s->type != SBSS) {
962 			diag("%s: redefinition: %s in %s",
963 				pn, s->name, TNAME);
964 			s->type = SBSS;
965 			s->value = 0;
966 		}
967 		if(p->to.offset > s->value)
968 			s->value = p->to.offset;
969 		goto loop;
970 
971 	case ADYNT:
972 		if(p->to.sym == S) {
973 			diag("DYNT without a sym\n%P", p);
974 			break;
975 		}
976 		di = p->to.sym;
977 		p->from.scale = 4;
978 		if(di->type == SXREF) {
979 			if(debug['z'])
980 				Bprint(&bso, "%P set to %d\n", p, dtype);
981 			di->type = SCONST;
982 			di->value = dtype;
983 			dtype += 4;
984 		}
985 		if(p->from.sym == S)
986 			break;
987 
988 		p->from.offset = di->value;
989 		p->from.sym->type = SDATA;
990 		if(curtext == P) {
991 			diag("DYNT not in text: %P", p);
992 			break;
993 		}
994 		p->to.sym = curtext->from.sym;
995 		p->to.type = D_ADDR;
996 		p->to.index = D_EXTERN;
997 		goto data;
998 
999 	case AINIT:
1000 		if(p->from.sym == S) {
1001 			diag("INIT without a sym\n%P", p);
1002 			break;
1003 		}
1004 		if(di == S) {
1005 			diag("INIT without previous DYNT\n%P", p);
1006 			break;
1007 		}
1008 		p->from.offset = di->value;
1009 		p->from.sym->type = SDATA;
1010 		goto data;
1011 
1012 	case ADATA:
1013 	data:
1014 		if(edatap == P)
1015 			datap = p;
1016 		else
1017 			edatap->link = p;
1018 		edatap = p;
1019 		p->link = P;
1020 		goto loop;
1021 
1022 	case AGOK:
1023 		diag("%s: GOK opcode in %s", pn, TNAME);
1024 		pc++;
1025 		goto loop;
1026 
1027 	case ATEXT:
1028 		if(curtext != P) {
1029 			histtoauto();
1030 			curtext->to.autom = curauto;
1031 			curauto = 0;
1032 		}
1033 		skip = 0;
1034 		curtext = p;
1035 		s = p->from.sym;
1036 		if(s == S) {
1037 			diag("%s: no TEXT symbol: %P", pn, p);
1038 			errorexit();
1039 		}
1040 		if(s->type != 0 && s->type != SXREF) {
1041 			if(p->from.scale & DUPOK) {
1042 				skip = 1;
1043 				goto casdef;
1044 			}
1045 			diag("%s: redefinition: %s\n%P", pn, s->name, p);
1046 		}
1047 		s->type = STEXT;
1048 		s->value = pc;
1049 		lastp->link = p;
1050 		lastp = p;
1051 		p->pc = pc;
1052 		pc++;
1053 		if(textp == P) {
1054 			textp = p;
1055 			etextp = p;
1056 			goto loop;
1057 		}
1058 		etextp->pcond = p;
1059 		etextp = p;
1060 		goto loop;
1061 
1062 	case AFMOVF:
1063 	case AFADDF:
1064 	case AFSUBF:
1065 	case AFSUBRF:
1066 	case AFMULF:
1067 	case AFDIVF:
1068 	case AFDIVRF:
1069 	case AFCOMF:
1070 	case AFCOMFP:
1071 		if(skip)
1072 			goto casdef;
1073 		if(p->from.type == D_FCONST) {
1074 			/* size sb 9 max */
1075 			sprint(literal, "$%lux", ieeedtof(&p->from.ieee));
1076 			s = lookup(literal, 0);
1077 			if(s->type == 0) {
1078 				s->type = SBSS;
1079 				s->value = 4;
1080 				t = prg();
1081 				t->as = ADATA;
1082 				t->line = p->line;
1083 				t->from.type = D_EXTERN;
1084 				t->from.sym = s;
1085 				t->from.scale = 4;
1086 				t->to = p->from;
1087 				if(edatap == P)
1088 					datap = t;
1089 				else
1090 					edatap->link = t;
1091 				edatap = t;
1092 				t->link = P;
1093 			}
1094 			p->from.type = D_EXTERN;
1095 			p->from.sym = s;
1096 			p->from.offset = 0;
1097 		}
1098 		goto casdef;
1099 
1100 	case AFMOVD:
1101 	case AFADDD:
1102 	case AFSUBD:
1103 	case AFSUBRD:
1104 	case AFMULD:
1105 	case AFDIVD:
1106 	case AFDIVRD:
1107 	case AFCOMD:
1108 	case AFCOMDP:
1109 		if(skip)
1110 			goto casdef;
1111 		if(p->from.type == D_FCONST) {
1112 			/* size sb 18 max */
1113 			sprint(literal, "$%lux.%lux",
1114 				p->from.ieee.l, p->from.ieee.h);
1115 			s = lookup(literal, 0);
1116 			if(s->type == 0) {
1117 				s->type = SBSS;
1118 				s->value = 8;
1119 				t = prg();
1120 				t->as = ADATA;
1121 				t->line = p->line;
1122 				t->from.type = D_EXTERN;
1123 				t->from.sym = s;
1124 				t->from.scale = 8;
1125 				t->to = p->from;
1126 				if(edatap == P)
1127 					datap = t;
1128 				else
1129 					edatap->link = t;
1130 				edatap = t;
1131 				t->link = P;
1132 			}
1133 			p->from.type = D_EXTERN;
1134 			p->from.sym = s;
1135 			p->from.offset = 0;
1136 		}
1137 		goto casdef;
1138 
1139 	casdef:
1140 	default:
1141 		if(skip)
1142 			nopout(p);
1143 
1144 		if(p->to.type == D_BRANCH)
1145 			p->to.offset += ipc;
1146 		lastp->link = p;
1147 		lastp = p;
1148 		p->pc = pc;
1149 		pc++;
1150 		goto loop;
1151 	}
1152 	goto loop;
1153 
1154 eof:
1155 	diag("truncated object file: %s", pn);
1156 }
1157 
1158 Sym*
1159 lookup(char *symb, int v)
1160 {
1161 	Sym *s;
1162 	char *p;
1163 	long h;
1164 	int l, c;
1165 
1166 	h = v;
1167 	for(p=symb; c = *p; p++)
1168 		h = h+h+h + c;
1169 	l = (p - symb) + 1;
1170 	h &= 0xffffff;
1171 	h %= NHASH;
1172 	for(s = hash[h]; s != S; s = s->link)
1173 		if(s->version == v)
1174 		if(memcmp(s->name, symb, l) == 0)
1175 			return s;
1176 
1177 	while(nhunk < sizeof(Sym))
1178 		gethunk();
1179 	s = (Sym*)hunk;
1180 	nhunk -= sizeof(Sym);
1181 	hunk += sizeof(Sym);
1182 
1183 	s->name = malloc(l + 1);
1184 	memmove(s->name, symb, l);
1185 
1186 	s->link = hash[h];
1187 	s->type = 0;
1188 	s->version = v;
1189 	s->value = 0;
1190 	s->sig = 0;
1191 	hash[h] = s;
1192 	nsymbol++;
1193 	return s;
1194 }
1195 
1196 Prog*
1197 prg(void)
1198 {
1199 	Prog *p;
1200 
1201 	while(nhunk < sizeof(Prog))
1202 		gethunk();
1203 	p = (Prog*)hunk;
1204 	nhunk -= sizeof(Prog);
1205 	hunk += sizeof(Prog);
1206 
1207 	*p = zprg;
1208 	return p;
1209 }
1210 
1211 Prog*
1212 copyp(Prog *q)
1213 {
1214 	Prog *p;
1215 
1216 	p = prg();
1217 	*p = *q;
1218 	return p;
1219 }
1220 
1221 Prog*
1222 appendp(Prog *q)
1223 {
1224 	Prog *p;
1225 
1226 	p = prg();
1227 	p->link = q->link;
1228 	q->link = p;
1229 	p->line = q->line;
1230 	return p;
1231 }
1232 
1233 void
1234 gethunk(void)
1235 {
1236 	char *h;
1237 	long nh;
1238 
1239 	nh = NHUNK;
1240 	if(thunk >= 5L*NHUNK) {
1241 		nh = 5L*NHUNK;
1242 		if(thunk >= 25L*NHUNK)
1243 			nh = 25L*NHUNK;
1244 	}
1245 	h = mysbrk(nh);
1246 	if(h == (char*)-1) {
1247 		diag("out of memory");
1248 		errorexit();
1249 	}
1250 	hunk = h;
1251 	nhunk = nh;
1252 	thunk += nh;
1253 }
1254 
1255 void
1256 doprof1(void)
1257 {
1258 	Sym *s;
1259 	long n;
1260 	Prog *p, *q;
1261 
1262 	if(debug['v'])
1263 		Bprint(&bso, "%5.2f profile 1\n", cputime());
1264 	Bflush(&bso);
1265 	s = lookup("__mcount", 0);
1266 	n = 1;
1267 	for(p = firstp->link; p != P; p = p->link) {
1268 		if(p->as == ATEXT) {
1269 			q = prg();
1270 			q->line = p->line;
1271 			q->link = datap;
1272 			datap = q;
1273 			q->as = ADATA;
1274 			q->from.type = D_EXTERN;
1275 			q->from.offset = n*4;
1276 			q->from.sym = s;
1277 			q->from.scale = 4;
1278 			q->to = p->from;
1279 			q->to.type = D_CONST;
1280 
1281 			q = prg();
1282 			q->line = p->line;
1283 			q->pc = p->pc;
1284 			q->link = p->link;
1285 			p->link = q;
1286 			p = q;
1287 			p->as = AADDL;
1288 			p->from.type = D_CONST;
1289 			p->from.offset = 1;
1290 			p->to.type = D_EXTERN;
1291 			p->to.sym = s;
1292 			p->to.offset = n*4 + 4;
1293 
1294 			n += 2;
1295 			continue;
1296 		}
1297 	}
1298 	q = prg();
1299 	q->line = 0;
1300 	q->link = datap;
1301 	datap = q;
1302 
1303 	q->as = ADATA;
1304 	q->from.type = D_EXTERN;
1305 	q->from.sym = s;
1306 	q->from.scale = 4;
1307 	q->to.type = D_CONST;
1308 	q->to.offset = n;
1309 
1310 	s->type = SBSS;
1311 	s->value = n*4;
1312 }
1313 
1314 void
1315 doprof2(void)
1316 {
1317 	Sym *s2, *s4;
1318 	Prog *p, *q, *q2, *ps2, *ps4;
1319 
1320 	if(debug['v'])
1321 		Bprint(&bso, "%5.2f profile 2\n", cputime());
1322 	Bflush(&bso);
1323 
1324 	if(debug['e']){
1325 		s2 = lookup("_tracein", 0);
1326 		s4 = lookup("_traceout", 0);
1327 	}else{
1328 		s2 = lookup("_profin", 0);
1329 		s4 = lookup("_profout", 0);
1330 	}
1331 	if(s2->type != STEXT || s4->type != STEXT) {
1332 		if(debug['e'])
1333 			diag("_tracein/_traceout not defined %d %d", s2->type, s4->type);
1334 		else
1335 			diag("_profin/_profout not defined");
1336 		return;
1337 	}
1338 
1339 	ps2 = P;
1340 	ps4 = P;
1341 	for(p = firstp; p != P; p = p->link) {
1342 		if(p->as == ATEXT) {
1343 			if(p->from.sym == s2) {
1344 				p->from.scale = 1;
1345 				ps2 = p;
1346 			}
1347 			if(p->from.sym == s4) {
1348 				p->from.scale = 1;
1349 				ps4 = p;
1350 			}
1351 		}
1352 	}
1353 	for(p = firstp; p != P; p = p->link) {
1354 		if(p->as == ATEXT) {
1355 			curtext = p;
1356 
1357 			if(p->from.scale & NOPROF) {	/* dont profile */
1358 				for(;;) {
1359 					q = p->link;
1360 					if(q == P)
1361 						break;
1362 					if(q->as == ATEXT)
1363 						break;
1364 					p = q;
1365 				}
1366 				continue;
1367 			}
1368 
1369 			/*
1370 			 * JMPL	profin
1371 			 */
1372 			q = prg();
1373 			q->line = p->line;
1374 			q->pc = p->pc;
1375 			q->link = p->link;
1376 			if(debug['e']){		/* embedded tracing */
1377 				q2 = prg();
1378 				p->link = q2;
1379 				q2->link = q;
1380 
1381 				q2->line = p->line;
1382 				q2->pc = p->pc;
1383 
1384 				q2->as = AJMP;
1385 				q2->to.type = D_BRANCH;
1386 				q2->to.sym = p->to.sym;
1387 				q2->pcond = q->link;
1388 			}else
1389 				p->link = q;
1390 			p = q;
1391 			p->as = ACALL;
1392 			p->to.type = D_BRANCH;
1393 			p->pcond = ps2;
1394 			p->to.sym = s2;
1395 
1396 			continue;
1397 		}
1398 		if(p->as == ARET) {
1399 			/*
1400 			 * RET (default)
1401 			 */
1402 			if(debug['e']){		/* embedded tracing */
1403 				q = prg();
1404 				q->line = p->line;
1405 				q->pc = p->pc;
1406 				q->link = p->link;
1407 				p->link = q;
1408 				p = q;
1409 			}
1410 			/*
1411 			 * RET
1412 			 */
1413 			q = prg();
1414 			q->as = ARET;
1415 			q->from = p->from;
1416 			q->to = p->to;
1417 			q->link = p->link;
1418 			p->link = q;
1419 
1420 			/*
1421 			 * JAL	profout
1422 			 */
1423 			p->as = ACALL;
1424 			p->from = zprg.from;
1425 			p->to = zprg.to;
1426 			p->to.type = D_BRANCH;
1427 			p->pcond = ps4;
1428 			p->to.sym = s4;
1429 
1430 			p = q;
1431 
1432 			continue;
1433 		}
1434 	}
1435 }
1436 
1437 void
1438 nuxiinit(void)
1439 {
1440 	int i, c;
1441 
1442 	for(i=0; i<4; i++) {
1443 		c = find1(0x04030201L, i+1);
1444 		if(i < 2)
1445 			inuxi2[i] = c;
1446 		if(i < 1)
1447 			inuxi1[i] = c;
1448 		inuxi4[i] = c;
1449 		fnuxi4[i] = c;
1450 		fnuxi8[i] = c;
1451 		fnuxi8[i+4] = c+4;
1452 	}
1453 	if(debug['v']) {
1454 		Bprint(&bso, "inuxi = ");
1455 		for(i=0; i<1; i++)
1456 			Bprint(&bso, "%d", inuxi1[i]);
1457 		Bprint(&bso, " ");
1458 		for(i=0; i<2; i++)
1459 			Bprint(&bso, "%d", inuxi2[i]);
1460 		Bprint(&bso, " ");
1461 		for(i=0; i<4; i++)
1462 			Bprint(&bso, "%d", inuxi4[i]);
1463 		Bprint(&bso, "\nfnuxi = ");
1464 		for(i=0; i<4; i++)
1465 			Bprint(&bso, "%d", fnuxi4[i]);
1466 		Bprint(&bso, " ");
1467 		for(i=0; i<8; i++)
1468 			Bprint(&bso, "%d", fnuxi8[i]);
1469 		Bprint(&bso, "\n");
1470 	}
1471 	Bflush(&bso);
1472 }
1473 
1474 int
1475 find1(long l, int c)
1476 {
1477 	char *p;
1478 	int i;
1479 
1480 	p = (char*)&l;
1481 	for(i=0; i<4; i++)
1482 		if(*p++ == c)
1483 			return i;
1484 	return 0;
1485 }
1486 
1487 int
1488 find2(long l, int c)
1489 {
1490 	short *p;
1491 	int i;
1492 
1493 	p = (short*)&l;
1494 	for(i=0; i<4; i+=2) {
1495 		if(((*p >> 8) & 0xff) == c)
1496 			return i;
1497 		if((*p++ & 0xff) == c)
1498 			return i+1;
1499 	}
1500 	return 0;
1501 }
1502 
1503 long
1504 ieeedtof(Ieee *e)
1505 {
1506 	int exp;
1507 	long v;
1508 
1509 	if(e->h == 0)
1510 		return 0;
1511 	exp = (e->h>>20) & ((1L<<11)-1L);
1512 	exp -= (1L<<10) - 2L;
1513 	v = (e->h & 0xfffffL) << 3;
1514 	v |= (e->l >> 29) & 0x7L;
1515 	if((e->l >> 28) & 1) {
1516 		v++;
1517 		if(v & 0x800000L) {
1518 			v = (v & 0x7fffffL) >> 1;
1519 			exp++;
1520 		}
1521 	}
1522 	if(exp <= -126 || exp >= 130)
1523 		diag("double fp to single fp overflow");
1524 	v |= ((exp + 126) & 0xffL) << 23;
1525 	v |= e->h & 0x80000000L;
1526 	return v;
1527 }
1528 
1529 double
1530 ieeedtod(Ieee *ieeep)
1531 {
1532 	Ieee e;
1533 	double fr;
1534 	int exp;
1535 
1536 	if(ieeep->h & (1L<<31)) {
1537 		e.h = ieeep->h & ~(1L<<31);
1538 		e.l = ieeep->l;
1539 		return -ieeedtod(&e);
1540 	}
1541 	if(ieeep->l == 0 && ieeep->h == 0)
1542 		return 0;
1543 	fr = ieeep->l & ((1L<<16)-1L);
1544 	fr /= 1L<<16;
1545 	fr += (ieeep->l>>16) & ((1L<<16)-1L);
1546 	fr /= 1L<<16;
1547 	fr += (ieeep->h & (1L<<20)-1L) | (1L<<20);
1548 	fr /= 1L<<21;
1549 	exp = (ieeep->h>>20) & ((1L<<11)-1L);
1550 	exp -= (1L<<10) - 2L;
1551 	return ldexp(fr, exp);
1552 }
1553 
1554 void
1555 undefsym(Sym *s)
1556 {
1557 	int n;
1558 
1559 	n = imports;
1560 	if(s->value != 0)
1561 		diag("value != 0 on SXREF");
1562 	if(n >= 1<<Rindex)
1563 		diag("import index %d out of range", n);
1564 	s->value = n<<Roffset;
1565 	s->type = SUNDEF;
1566 	imports++;
1567 }
1568 
1569 void
1570 zerosig(char *sp)
1571 {
1572 	Sym *s;
1573 
1574 	s = lookup(sp, 0);
1575 	s->sig = 0;
1576 }
1577 
1578 void
1579 readundefs(char *f, int t)
1580 {
1581 	int i, n;
1582 	Sym *s;
1583 	Biobuf *b;
1584 	char *l, buf[256], *fields[64];
1585 
1586 	if(f == nil)
1587 		return;
1588 	b = Bopen(f, OREAD);
1589 	if(b == nil){
1590 		diag("could not open %s: %r", f);
1591 		errorexit();
1592 	}
1593 	while((l = Brdline(b, '\n')) != nil){
1594 		n = Blinelen(b);
1595 		if(n >= sizeof(buf)){
1596 			diag("%s: line too long", f);
1597 			errorexit();
1598 		}
1599 		memmove(buf, l, n);
1600 		buf[n-1] = '\0';
1601 		n = getfields(buf, fields, nelem(fields), 1, " \t\r\n");
1602 		if(n == nelem(fields)){
1603 			diag("%s: bad format", f);
1604 			errorexit();
1605 		}
1606 		for(i = 0; i < n; i++){
1607 			s = lookup(fields[i], 0);
1608 			s->type = SXREF;
1609 			s->subtype = t;
1610 			if(t == SIMPORT)
1611 				nimports++;
1612 			else
1613 				nexports++;
1614 		}
1615 	}
1616 	Bterm(b);
1617 }
1618