xref: /inferno-os/utils/libmach/sym.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
1 #include <lib9.h>
2 #include <bio.h>
3 #include "mach.h"
4 
5 #define	HUGEINT	0x7fffffff
6 #define	NNAME	20		/* a relic of the past */
7 
8 typedef	struct txtsym Txtsym;
9 typedef	struct file File;
10 typedef	struct hist Hist;
11 typedef struct pcl Pcl;
12 
13 struct txtsym {				/* Text Symbol table */
14 	int 	n;			/* number of local vars */
15 	Sym	**locals;		/* array of ptrs to autos */
16 	Sym	*sym;			/* function symbol entry */
17 };
18 
19 struct hist {		/* Stack of include files & #line directives */
20 	char	*name;			/* Assumes names Null terminated in file */
21 	long	line;			/* line # where it was included */
22 	long	offset;			/* line # of #line directive */
23 };
24 
25 struct pcl {
26 	uchar		*pcline;	/* start of pcline data for this file */
27 	long		pc;		/* starting pc for pcline crunch */
28 	long		line;		/* starting line for pcline crunch */
29 };
30 
31 struct file {				/* Per input file header to history stack */
32 	long		addr;		/* address of first text sym */
33 	union {
34 		Txtsym		*ftxt;	/* first text symbol */
35 		Sym		*fsym;	/* only during initilization */
36 	} u0;
37 	int		n;		/* size of history stack */
38 	Hist		*hist;		/* history stack */
39 	Pcl		pcl;		/* pcline startup data */
40 };
41 
42 static	int	debug = 0;
43 
44 static	Sym	**autos;		/* Base of auto variables */
45 static	File	*files;			/* Base of file arena */
46 static	int	fpmax;			/* largest file path index */
47 static	Sym	**fnames;		/* file names path component table */
48 static	Sym	**globals;		/* globals by addr table */
49 static	Hist	*hist;			/* base of history stack */
50 static	int	isbuilt;		/* internal table init flag */
51 static	long	nauto;			/* number of automatics */
52 static	long	nfiles;			/* number of files */
53 static	long	nglob;			/* number of globals */
54 static	long	nhist;			/* number of history stack entries */
55 static	long	nsym;			/* number of symbols */
56 static	long	ntxt;			/* number of text symbols */
57 static	uchar	*pcline;		/* start of pc-line state table */
58 static	uchar 	*pclineend;		/* end of pc-line table */
59 static	int	npcl;			/* number of files pcl-decoded so far */
60 static	uchar	*spoff;			/* start of pc-sp state table */
61 static	uchar	*spoffend;		/* end of pc-sp offset table */
62 static	Sym	*symbols;		/* symbol table */
63 static	Txtsym	*txt;			/* Base of text symbol table */
64 static	long	txtstart;		/* start of text segment */
65 static	long	txtend;			/* end of text segment */
66 
67 static void	cleansyms(void);
68 static int	decodename(Biobuf*, Sym*);
69 static short	*encfname(char*);
70 static int 	fline(char*, int, long, Hist*, Hist**);
71 static void	fillsym(Sym*, Symbol*);
72 static int	findglobal(char*, Symbol*);
73 static int	findlocvar(Symbol*, char *, Symbol*);
74 static int	findtext(char*, Symbol*);
75 static int	hcomp(Hist*, short*);
76 static int	hline(File*, short*, ulong*);
77 static void	printhist(char*, Hist*, int);
78 static long	pcl2line(ulong, Pcl *, Pcl *);
79 static long	pc2fline(ulong, File **);
80 static int	pc2filex(ulong);
81 static int	buildtbls(void);
82 static int	symcomp(void*, void*);
83 static int	symerrmsg(int, char*);
84 static int	txtcomp(void*, void*);
85 static int	filecomp(void*, void*);
86 
87 /*
88  *	initialize the symbol tables
89  */
90 int
91 syminit(int fd, Fhdr *fp)
92 {
93 	Sym *p;
94 	int i, size;
95 	Biobuf b;
96 	extern void thumbpctab(Biobuf*, Fhdr*);
97 
98 	if(fp->symsz == 0)
99 		return 0;
100 	if(fp->type == FNONE)
101 		return 0;
102 
103 	cleansyms();
104 	textseg(fp->txtaddr, fp);
105 		/* minimum symbol record size = 4+1+2 bytes */
106 	symbols = malloc((fp->symsz/(4+1+2)+1)*sizeof(Sym));
107 	if(symbols == 0) {
108 		werrstr("can't malloc %ld bytes", fp->symsz);
109 		return -1;
110 	}
111 
112 	Binit(&b, fd, OREAD);
113 	Bseek(&b, fp->symoff, 0);
114 	nsym = 0;
115 	size = 0;
116 	for(p = symbols; size < fp->symsz; p++, nsym++) {
117 		if(Bread(&b, &p->value, sizeof(p->value)) != sizeof(p->value))
118 			return symerrmsg(sizeof(p->value), "symbol");
119 		p->value = beswal(p->value);
120 		if(Bread(&b, &p->type, sizeof(p->type)) != sizeof(p->type))
121 			return symerrmsg(sizeof(p->value), "symbol");
122 
123 		i = decodename(&b, p);
124 		if(i < 0)
125 			return -1;
126 		size += i+sizeof(p->value)+sizeof(p->type);
127 
128 		/* count global & auto vars, text symbols, and file names */
129 		switch (p->type) {
130 		case 'l':
131 		case 'L':
132 		case 't':
133 		case 'T':
134 			ntxt++;
135 			break;
136 		case 'd':
137 		case 'D':
138 		case 'b':
139 		case 'B':
140 			nglob++;
141 			break;
142 		case 'f':
143 			if(strcmp(p->name, ".frame") == 0) {
144 				p->type = 'm';
145 				nauto++;
146 			}
147 			else if(p->value > fpmax)
148 				fpmax = p->value;	/* highest path index */
149 			break;
150 		case 'a':
151 		case 'p':
152 		case 'm':
153 			nauto++;
154 			break;
155 		case 'z':
156 			if(p->value == 1) {		/* one extra per file */
157 				nhist++;
158 				nfiles++;
159 			}
160 			nhist++;
161 			break;
162 		default:
163 			break;
164 		}
165 	}
166 	if (debug)
167 		fprint(2,"NG: %ld NT: %ld NF: %d\n", nglob, ntxt, fpmax);
168 	if (fp->sppcsz) {			/* pc-sp offset table */
169 		spoff = (uchar *)malloc(fp->sppcsz);
170 		if(spoff == 0) {
171 			werrstr("can't malloc %ld bytes", fp->sppcsz);
172 			return -1;
173 		}
174 		Bseek(&b, fp->sppcoff, 0);
175 		i = Bread(&b, spoff, fp->sppcsz);
176 		if(i != fp->sppcsz){
177 			spoff = 0;
178 			return symerrmsg(fp->sppcsz, "sp-pc");
179 		}
180 		spoffend = spoff+fp->sppcsz;
181 	}
182 	if (fp->lnpcsz) {			/* pc-line number table */
183 		pcline = (uchar *)malloc(fp->lnpcsz);
184 		if(pcline == 0) {
185 			werrstr("can't malloc %ld bytes", fp->lnpcsz);
186 			return -1;
187 		}
188 		Bseek(&b, fp->lnpcoff, 0);
189 		i = Bread(&b, pcline, fp->lnpcsz);
190 		if(i != fp->lnpcsz){
191 			pcline = 0;
192 			return symerrmsg(fp->lnpcsz, "pc-line");
193 		}
194 		pclineend = pcline+fp->lnpcsz;
195 	}
196 	if(fp->type == FARM)	/* thumb pc table */
197 		thumbpctab(&b, fp);
198 	return nsym;
199 }
200 
201 static int
202 symerrmsg(int n, char *table)
203 {
204 	werrstr("can't read %d bytes of %s table", n, table);
205 	return -1;
206 }
207 
208 static int
209 decodename(Biobuf *bp, Sym *p)
210 {
211 	char *cp;
212 	int c1, c2;
213 	int n;
214 
215 	if((p->type & 0x80) == 0) {		/* old-style, fixed length names */
216 		p->name = malloc(NNAME);
217 		if(p->name == 0) {
218 			werrstr("can't malloc %d bytes", NNAME);
219 			return -1;
220 		}
221 		if(Bread(bp, p->name, NNAME) != NNAME)
222 			return symerrmsg(NNAME, "symbol");
223 		Bseek(bp, 3, 1);
224 		return NNAME+3;
225 	}
226 
227 	p->type &= ~0x80;
228 	if(p->type == 'z' || p->type == 'Z') {
229 		n = Bseek(bp, 0, 1);
230 		if(Bgetc(bp) < 0) {
231 			werrstr("can't read symbol name");
232 			return -1;
233 		}
234 		for(;;) {
235 			c1 = Bgetc(bp);
236 			c2 = Bgetc(bp);
237 			if(c1 < 0 || c2 < 0) {
238 				werrstr("can't read symbol name");
239 				return -1;
240 			}
241 			if(c1 == 0 && c2 == 0)
242 				break;
243 		}
244 		n = Bseek(bp, 0, 1)-n;
245 		p->name = malloc(n);
246 		if(p->name == 0) {
247 			werrstr("can't malloc %d bytes", n);
248 			return -1;
249 		}
250 		Bseek(bp, -n, 1);
251 		if(Bread(bp, p->name, n) != n) {
252 			werrstr("can't read %d bytes of symbol name", n);
253 			return -1;
254 		}
255 	} else {
256 		cp = Brdline(bp, '\0');
257 		if(cp == 0) {
258 			werrstr("can't read symbol name");
259 			return -1;
260 		}
261 		n = Blinelen(bp);
262 		p->name = malloc(n);
263 		if(p->name == 0) {
264 			werrstr("can't malloc %d bytes", n);
265 			return -1;
266 		}
267 		strcpy(p->name, cp);
268 	}
269 	return n;
270 }
271 /*
272  *	free any previously loaded symbol tables
273  */
274 static void
275 cleansyms(void)
276 {
277 	if(globals)
278 		free(globals);
279 	globals = 0;
280 	nglob = 0;
281 	if(txt)
282 		free(txt);
283 	txt = 0;
284 	ntxt = 0;
285 	if(fnames)
286 		free(fnames);
287 	fnames = 0;
288 	fpmax = 0;
289 
290 	if(files)
291 		free(files);
292 	files = 0;
293 	nfiles = 0;
294 	if(hist)
295 		free(hist);
296 	hist = 0;
297 	nhist = 0;
298 	if(autos)
299 		free(autos);
300 	autos = 0;
301 	nauto = 0;
302 	isbuilt = 0;
303 	if(symbols)
304 		free(symbols);
305 	symbols = 0;
306 	nsym = 0;
307 	if(spoff)
308 		free(spoff);
309 	spoff = 0;
310 	if(pcline)
311 		free(pcline);
312 	pcline = 0;
313 	npcl = 0;
314 }
315 /*
316  *	delimit the text segment
317  */
318 void
319 textseg(ulong base, Fhdr *fp)
320 {
321 	txtstart = base;
322 	txtend = base+fp->txtsz;
323 	npcl = 0;	/* all pcls must be recomputed */
324 }
325 /*
326  *	symbase: return base and size of raw symbol table
327  *		(special hack for high access rate operations)
328  */
329 Sym *
330 symbase(long *n)
331 {
332 	*n = nsym;
333 	return symbols;
334 }
335 /*
336  *	Get the ith symbol table entry
337  */
338 Sym *
339 getsym(int index)
340 {
341 	if(index < nsym)
342 		return &symbols[index];
343 	return 0;
344 }
345 
346 /*
347  *	initialize internal symbol tables
348  */
349 static int
350 buildtbls(void)
351 {
352 	int i, j, nh, ng, nt;
353 	File *f;
354 	Txtsym *tp;
355 	Hist *hp;
356 	Sym *p, **ap;
357 
358 	if(isbuilt)
359 		return 1;
360 	isbuilt = 1;
361 			/* allocate the tables */
362 	if(nglob) {
363 		globals = malloc(nglob*sizeof(*globals));
364 		if(!globals) {
365 			werrstr("can't malloc global symbol table");
366 			return 0;
367 		}
368 	}
369 	if(ntxt) {
370 		txt = malloc(ntxt*sizeof(*txt));
371 		if (!txt) {
372 			werrstr("can't malloc text symbol table");
373 			return 0;
374 		}
375 	}
376 	fpmax++;
377 	fnames = malloc(fpmax*sizeof(*fnames));
378 	if (!fnames) {
379 		werrstr("can't malloc file name table");
380 		return 0;
381 	}
382 	memset(fnames, 0, fpmax*sizeof(*fnames));
383 	files = malloc(nfiles*sizeof(*files));
384 	if(!files) {
385 		werrstr("can't malloc file table");
386 		return 0;
387 	}
388 	hist = malloc(nhist*sizeof(Hist));
389 	if(hist == 0) {
390 		werrstr("can't malloc history stack");
391 		return 0;
392 	}
393 	autos = malloc(nauto*sizeof(Sym*));
394 	if(autos == 0) {
395 		werrstr("can't malloc auto symbol table");
396 		return 0;
397 	}
398 		/* load the tables */
399 	ng = nt = nh = 0;
400 	f = 0;
401 	tp = 0;
402 	i = nsym;
403 	hp = hist;
404 	ap = autos;
405 	for(p = symbols; i-- > 0; p++) {
406 		switch(p->type) {
407 		case 'D':
408 		case 'd':
409 		case 'B':
410 		case 'b':
411 			if(debug)
412 				fprint(2,"Global: %s %lux\n", p->name, p->value);
413 			globals[ng++] = p;
414 			break;
415 		case 'z':
416 			if(p->value == 1) {		/* New file */
417 				if(f) {
418 					f->n = nh;
419 					f->hist[nh].name = 0;	/* one extra */
420 					hp += nh+1;
421 					f++;
422 				}
423 				else f = files;
424 				f->hist = hp;
425 				f->u0.fsym = 0;
426 				f->addr = 0;
427 				nh = 0;
428 			}
429 				/* alloc one slot extra as terminator */
430 			f->hist[nh].name = p->name;
431 			f->hist[nh].line = p->value;
432 			f->hist[nh].offset = 0;
433 			if(debug)
434 				printhist("-> ", &f->hist[nh], 1);
435 			nh++;
436 			break;
437 		case 'Z':
438 			if(f && nh > 0)
439 				f->hist[nh-1].offset = p->value;
440 			break;
441 		case 'T':
442 		case 't':	/* Text: terminate history if first in file */
443 		case 'L':
444 		case 'l':
445 			tp = &txt[nt++];
446 			tp->n = 0;
447 			tp->sym = p;
448 			tp->locals = ap;
449 			if(debug)
450 				fprint(2,"TEXT: %s at %lux\n", p->name, p->value);
451 			if(f && !f->u0.fsym) {			/* first  */
452 				f->u0.fsym = p;
453 				f->addr = p->value;
454 			}
455 			break;
456 		case 'a':
457 		case 'p':
458 		case 'm':		/* Local Vars */
459 			if(!tp)
460 				print("Warning: Free floating local var");
461 			else {
462 				if(debug)
463 					fprint(2,"Local: %s %lux\n", p->name, p->value);
464 				tp->locals[tp->n] = p;
465 				tp->n++;
466 				ap++;
467 			}
468 			break;
469 		case 'f':		/* File names */
470 			if(debug)
471 				fprint(2,"Fname: %s\n", p->name);
472 			fnames[p->value] = p;
473 			break;
474 		default:
475 			break;
476 		}
477 	}
478 		/* sort global and text tables into ascending address order */
479 	qsort(globals, nglob, sizeof(Sym*), symcomp);
480 	qsort(txt, ntxt, sizeof(Txtsym), txtcomp);
481 	qsort(files, nfiles, sizeof(File), filecomp);
482 	tp = txt;
483 	for(i = 0, f = files; i < nfiles; i++, f++) {
484 		for(j = 0; j < ntxt; j++) {
485 			if(f->u0.fsym == tp->sym) {
486 				if(debug) {
487 					fprint(2,"LINK: %s to at %lux", f->u0.fsym->name, f->addr);
488 					printhist("... ", f->hist, 1);
489 				}
490 				f->u0.ftxt = tp++;
491 				break;
492 			}
493 			if(++tp >= txt+ntxt)	/* wrap around */
494 				tp = txt;
495 		}
496 	}
497 	return 1;
498 }
499 
500 /*
501  * find symbol function.var by name.
502  *	fn != 0 && var != 0	=> look for fn in text, var in data
503  *	fn != 0 && var == 0	=> look for fn in text
504  *	fn == 0 && var != 0	=> look for var first in text then in data space.
505  */
506 int
507 lookup(char *fn, char *var, Symbol *s)
508 {
509 	int found;
510 
511 	if(buildtbls() == 0)
512 		return 0;
513 	if(fn) {
514 		found = findtext(fn, s);
515 		if(var == 0)		/* case 2: fn not in text */
516 			return found;
517 		else if(!found)		/* case 1: fn not found */
518 			return 0;
519 	} else if(var) {
520 		found = findtext(var, s);
521 		if(found)
522 			return 1;	/* case 3: var found in text */
523 	} else return 0;		/* case 4: fn & var == zero */
524 
525 	if(found)
526 		return findlocal(s, var, s);	/* case 1: fn found */
527 	return findglobal(var, s);		/* case 3: var not found */
528 
529 }
530 /*
531  * find a function by name
532  */
533 static int
534 findtext(char *name, Symbol *s)
535 {
536 	int i;
537 
538 	for(i = 0; i < ntxt; i++) {
539 		if(strcmp(txt[i].sym->name, name) == 0) {
540 			fillsym(txt[i].sym, s);
541 			s->handle = (void *) &txt[i];
542 			return 1;
543 		}
544 	}
545 	return 0;
546 }
547 /*
548  * find global variable by name
549  */
550 static int
551 findglobal(char *name, Symbol *s)
552 {
553 	int i;
554 
555 	for(i = 0; i < nglob; i++) {
556 		if(strcmp(globals[i]->name, name) == 0) {
557 			fillsym(globals[i], s);
558 			return 1;
559 		}
560 	}
561 	return 0;
562 }
563 /*
564  *	find the local variable by name within a given function
565  */
566 int
567 findlocal(Symbol *s1, char *name, Symbol *s2)
568 {
569 	if(s1 == 0)
570 		return 0;
571 	if(buildtbls() == 0)
572 		return 0;
573 	return findlocvar(s1, name, s2);
574 }
575 /*
576  *	find the local variable by name within a given function
577  *		(internal function - does no parameter validation)
578  */
579 static int
580 findlocvar(Symbol *s1, char *name, Symbol *s2)
581 {
582 	Txtsym *tp;
583 	int i;
584 
585 	tp = (Txtsym *)s1->handle;
586 	if(tp && tp->locals) {
587 		for(i = 0; i < tp->n; i++)
588 			if (strcmp(tp->locals[i]->name, name) == 0) {
589 				fillsym(tp->locals[i], s2);
590 				s2->handle = (void *)tp;
591 				return 1;
592 			}
593 	}
594 	return 0;
595 }
596 /*
597  *	Get ith text symbol
598  */
599 int
600 textsym(Symbol *s, int index)
601 {
602 
603 	if(buildtbls() == 0)
604 		return 0;
605 	if(index >= ntxt)
606 		return 0;
607 	fillsym(txt[index].sym, s);
608 	s->handle = (void *)&txt[index];
609 	return 1;
610 }
611 /*
612  *	Get ith file name
613  */
614 int
615 filesym(int index, char *buf, int n)
616 {
617 	Hist *hp;
618 
619 	if(buildtbls() == 0)
620 		return 0;
621 	if(index >= nfiles)
622 		return 0;
623 	hp = files[index].hist;
624 	if(!hp || !hp->name)
625 		return 0;
626 	return fileelem(fnames, (uchar*)hp->name, buf, n);
627 }
628 /*
629  *	Lookup name of local variable located at an offset into the frame.
630  *	The type selects either a parameter or automatic.
631  */
632 int
633 getauto(Symbol *s1, int off, int type, Symbol *s2)
634 {
635 	Txtsym *tp;
636 	Sym *p;
637 	int i, t;
638 
639 	if(s1 == 0)
640 		return 0;
641 	if(type == CPARAM)
642 		t = 'p';
643 	else if(type == CAUTO)
644 		t = 'a';
645 	else
646 		return 0;
647 	if(buildtbls() == 0)
648 		return 0;
649 	tp = (Txtsym *)s1->handle;
650 	if(tp == 0)
651 		return 0;
652 	for(i = 0; i < tp->n; i++) {
653 		p = tp->locals[i];
654 		if(p->type == t && p->value == off) {
655 			fillsym(p, s2);
656 			s2->handle = s1->handle;
657 			return 1;
658 		}
659 	}
660 	return 0;
661 }
662 
663 /*
664  * Find text symbol containing addr; binary search assumes text array is sorted by addr
665  */
666 static int
667 srchtext(long addr)
668 {
669 	ulong val;
670 	int top, bot, mid;
671 	Sym *sp;
672 
673 	val = addr;
674 	bot = 0;
675 	top = ntxt;
676 	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
677 		sp = txt[mid].sym;
678 		if(val < (ulong)sp->value)
679 			top = mid;
680 		else if(mid != ntxt-1 && val >= (ulong)txt[mid+1].sym->value)
681 			bot = mid;
682 		else
683 			return mid;
684 	}
685 	return -1;
686 }
687 
688 /*
689  * Find data symbol containing addr; binary search assumes data array is sorted by addr
690  */
691 static
692 int srchdata(long addr)
693 {
694 	ulong val;
695 	int top, bot, mid;
696 	Sym *sp;
697 
698 	bot = 0;
699 	top = nglob;
700 	val = addr;
701 	for(mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
702 		sp = globals[mid];
703 		if(val < (ulong)sp->value)
704 			top = mid;
705 		else if(mid < nglob-1 && val >= (ulong)globals[mid+1]->value)
706 			bot = mid;
707 		else
708 			return mid;
709 	}
710 	return -1;
711 }
712 /*
713  * Find symbol containing val in specified search space
714  * There is a special case when a value falls beyond the end
715  * of the text segment; if the search space is CTEXT, that value
716  * (usually etext) is returned.  If the search space is CANY, symbols in the
717  * data space are searched for a match.
718  */
719 int
720 findsym(long w, int type, Symbol *s)
721 {
722 	int i;
723 
724 	if(buildtbls() == 0)
725 		return 0;
726 
727 	if(type == CTEXT || type == CANY) {
728 		i = srchtext(w);
729 		if(i >= 0) {
730 			if(type == CTEXT || i != ntxt-1) {
731 				fillsym(txt[i].sym, s);
732 				s->handle = (void *) &txt[i];
733 				return 1;
734 			}
735 		}
736 	}
737 	if(type == CDATA || type == CANY) {
738 		i = srchdata(w);
739 		if(i >= 0) {
740 			fillsym(globals[i], s);
741 			return 1;
742 		}
743 	}
744 	return 0;
745 }
746 
747 /*
748  *	Find the start and end address of the function containing addr
749  */
750 int
751 fnbound(long addr, ulong *bounds)
752 {
753 	int i;
754 
755 	if(buildtbls() == 0)
756 		return 0;
757 
758 	i = srchtext(addr);
759 	if(0 <= i && i < ntxt-1) {
760 		bounds[0] = txt[i].sym->value;
761 		bounds[1] = txt[i+1].sym->value;
762 		return 1;
763 	}
764 	return 0;
765 }
766 
767 /*
768  * get the ith local symbol for a function
769  * the input symbol table is reverse ordered, so we reverse
770  * accesses here to maintain approx. parameter ordering in a stack trace.
771  */
772 int
773 localsym(Symbol *s, int index)
774 {
775 	Txtsym *tp;
776 
777 	if(s == 0)
778 		return 0;
779 	if(buildtbls() == 0)
780 		return 0;
781 
782 	tp = (Txtsym *)s->handle;
783 	if(tp && tp->locals && index < tp->n) {
784 		fillsym(tp->locals[tp->n-index-1], s);	/* reverse */
785 		s->handle = (void *)tp;
786 		return 1;
787 	}
788 	return 0;
789 }
790 /*
791  * get the ith global symbol
792  */
793 int
794 globalsym(Symbol *s, int index)
795 {
796 	if(s == 0)
797 		return 0;
798 	if(buildtbls() == 0)
799 		return 0;
800 
801 	if(index < nglob) {
802 		fillsym(globals[index], s);
803 		return 1;
804 	}
805 	return 0;
806 }
807 /*
808  *	find the pc given a file name and line offset into it.
809  */
810 long
811 file2pc(char *file, ulong line)
812 {
813 	File *fp;
814 	int i;
815 	long pc;
816 	ulong start, end;
817 	short *name;
818 
819 	if(buildtbls() == 0 || files == 0)
820 		return -1;
821 	name = encfname(file);
822 	if(name == 0) {			/* encode the file name */
823 		werrstr("file %s not found", file);
824 		return -1;
825 	}
826 		/* find this history stack */
827 	for(i = 0, fp = files; i < nfiles; i++, fp++)
828 		if (hline(fp, name, &line))
829 			break;
830 	free(name);
831 	if(i >= nfiles) {
832 		werrstr("line %ld in file %s not found", line, file);
833 		return -1;
834 	}
835 	start = fp->addr;		/* first text addr this file */
836 	if(i < nfiles-1)
837 		end = (fp+1)->addr;	/* first text addr next file */
838 	else
839 		end = 0;		/* last file in load module */
840 	/*
841 	 * At this point, line contains the offset into the file.
842 	 * run the state machine to locate the pc closest to that value.
843 	 */
844 	if(debug)
845 		fprint(2,"find pc for %ld - between: %lux and %lux\n", line, start, end);
846 	pc = line2addr(line, start, end);
847 	if(pc == -1) {
848 		werrstr("line %ld not in file %s", line, file);
849 		return -1;
850 	}
851 	return pc;
852 }
853 /*
854  *	search for a path component index
855  */
856 static int
857 pathcomp(char *s, int n)
858 {
859 	int i;
860 
861 	for(i = 0; i <= fpmax; i++)
862 		if(fnames[i] && strncmp(s, fnames[i]->name, n) == 0 && fnames[i]->name[n] == 0)
863 			return i;
864 	return -1;
865 }
866 /*
867  *	Encode a char file name as a sequence of short indices
868  *	into the file name dictionary.
869  */
870 static short*
871 encfname(char *file)
872 {
873 	int i, j;
874 	char *cp, *cp2;
875 	short *dest;
876 
877 	if(*file == '/')	/* always check first '/' */
878 		cp2 = file+1;
879 	else {
880 		cp2 = strchr(file, '/');
881 		if(!cp2)
882 			cp2 = strchr(file, 0);
883 	}
884 	cp = file;
885 	dest = 0;
886 	for(i = 0; *cp; i++) {
887 		j = pathcomp(cp, cp2-cp);
888 		if(j < 0)
889 			return 0;	/* not found */
890 		dest = realloc(dest, (i+1)*sizeof(short));
891 		dest[i] = j;
892 		cp = cp2;
893 		while(*cp == '/')	/* skip embedded '/'s */
894 			cp++;
895 		cp2 = strchr(cp, '/');
896 		if(!cp2)
897 			cp2 = strchr(cp, 0);
898 	}
899 	dest = realloc(dest, (i+1)*sizeof(short));
900 	dest[i] = 0;
901 	return dest;
902 }
903 /*
904  *	Search a history stack for a matching file name accumulating
905  *	the size of intervening files in the stack.
906  */
907 static int
908 hline(File *fp, short *name, ulong *line)
909 {
910 	Hist *hp;
911 	int offset, depth;
912 	long ln;
913 
914 	for(hp = fp->hist; hp->name; hp++)		/* find name in stack */
915 		if(hp->name[1] || hp->name[2]) {
916 			if(hcomp(hp, name))
917 				break;
918 		}
919 	if(!hp->name)		/* match not found */
920 		return 0;
921 	if(debug)
922 		printhist("hline found ... ", hp, 1);
923 	/*
924 	 * unwind the stack until empty or we hit an entry beyond our line
925 	 */
926 	ln = *line;
927 	offset = hp->line-1;
928 	depth = 1;
929 	for(hp++; depth && hp->name; hp++) {
930 		if(debug)
931 			printhist("hline inspect ... ", hp, 1);
932 		if(hp->name[1] || hp->name[2]) {
933 			if(hp->offset){			/* Z record */
934 				offset = 0;
935 				if(hcomp(hp, name)) {
936 					if(*line <= hp->offset)
937 						break;
938 					ln = *line+hp->line-hp->offset;
939 					depth = 1;	/* implicit pop */
940 				} else
941 					depth = 2;	/* implicit push */
942 			} else if(depth == 1 && ln < hp->line-offset)
943 					break;		/* Beyond our line */
944 			else if(depth++ == 1)		/* push	*/
945 				offset -= hp->line;
946 		} else if(--depth == 1)		/* pop */
947 			offset += hp->line;
948 	}
949 	*line = ln+offset;
950 	return 1;
951 }
952 /*
953  *	compare two encoded file names
954  */
955 static int
956 hcomp(Hist *hp, short *sp)
957 {
958 	uchar *cp;
959 	int i, j;
960 	short *s;
961 
962 	cp = (uchar *)hp->name;
963 	s = sp;
964 	if (*s == 0)
965 		return 0;
966 	for (i = 1; j = (cp[i]<<8)|cp[i+1]; i += 2) {
967 		if(j == 0)
968 			break;
969 		if(*s == j)
970 			s++;
971 		else
972 			s = sp;
973 	}
974 	return *s == 0;
975 }
976 /*
977  *	Convert a pc to a "file:line {file:line}" string.
978  */
979 int
980 fileline(char *str, int n, ulong dot)
981 {
982 	long line;
983 	File *f;
984 	int i;
985 
986 	*str = 0;
987 	if(buildtbls() == 0)
988 		return 0;
989 
990 	i = pc2filex(dot);
991 	if (i >= 0)
992 	{
993 		f = &files[i];
994 		line = pc2line(dot);
995 		if(line >= 0 && fline(str, n, line, f->hist, 0) >= 0)
996 			return 1;
997 	}
998 	return 0;
999 }
1000 /*
1001  *	Convert a pc to an index to the file table
1002  */
1003 static int
1004 pc2filex(ulong dot)
1005 {
1006 	int top, bot, mid;
1007 	File *f;
1008 
1009 	if(buildtbls() == 0)
1010 		return -1;
1011 
1012 		/* binary search assumes file list is sorted by addr */
1013 	bot = 0;
1014 	top = nfiles;
1015 	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
1016 		f = &files[mid];
1017 		if(dot < f->addr)
1018 			top = mid;
1019 		else if(mid < nfiles-1 && dot >= (f+1)->addr)
1020 			bot = mid;
1021 		else
1022 			return mid;
1023 	}
1024 	return -1;
1025 }
1026 
1027 /*
1028  *	Convert a line number within a composite file to relative line
1029  *	number in a source file.  A composite file is the source
1030  *	file with included files inserted in line.
1031  */
1032 static int
1033 fline(char *str, int n, long line, Hist *base, Hist **ret)
1034 {
1035 	Hist *start;			/* start of current level */
1036 	Hist *h;			/* current entry */
1037 	int delta;			/* sum of size of files this level */
1038 	int k;
1039 
1040 	start = base;
1041 	h = base;
1042 	delta = h->line;
1043 	while(h && h->name && line > h->line) {
1044 		if(h->name[1] || h->name[2]) {
1045 			if(h->offset != 0) {	/* #line Directive */
1046 				delta = h->line-h->offset+1;
1047 				start = h;
1048 				base = h++;
1049 			} else {		/* beginning of File */
1050 				if(start == base)
1051 					start = h++;
1052 				else {
1053 					k = fline(str, n, line, start, &h);
1054 					if(k <= 0)
1055 						return k;
1056 				}
1057 			}
1058 		} else {
1059 			if(start == base) {	/* end of recursion level */
1060 				if(ret)
1061 					*ret = h;
1062 				return 1;
1063 			} else {			/* end of included file */
1064 				delta += h->line-start->line;
1065 				h++;
1066 				start = base;
1067 			}
1068 		}
1069 	}
1070 	if(!h)
1071 		return -1;
1072 	if(start != base)
1073 		line = line-start->line+1;
1074 	else
1075 		line = line-delta+1;
1076 	if(!h->name)
1077 		strncpy(str, "<eof>", n);
1078 	else {
1079 		k = fileelem(fnames, (uchar*)start->name, str, n);
1080 		if(k+8 < n)
1081 			sprint(str+k, ":%ld", line);
1082 	}
1083 /**********Remove comments for complete back-trace of include sequence
1084  *	if(start != base) {
1085  *		k = strlen(str);
1086  *		if(k+2 < n) {
1087  *			str[k++] = ' ';
1088  *			str[k++] = '{';
1089  *		}
1090  *		k += fileelem(fnames, (uchar*) base->name, str+k, n-k);
1091  *		if(k+10 < n)
1092  *			sprint(str+k, ":%ld}", start->line-delta);
1093  *	}
1094  ********************/
1095 	return 0;
1096 }
1097 /*
1098  *	convert an encoded file name to a string.
1099  */
1100 int
1101 fileelem(Sym **fp, uchar *cp, char *buf, int n)
1102 {
1103 	int i, j;
1104 	char *c, *bp, *end;
1105 
1106 	bp = buf;
1107 	end = buf+n-1;
1108 	for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){
1109 		c = fp[j]->name;
1110 		if(bp != buf && bp[-1] != '/' && bp < end)
1111 			*bp++ = '/';
1112 		while(bp < end && *c)
1113 			*bp++ = *c++;
1114 	}
1115 	*bp = 0;
1116 	return bp-buf;
1117 }
1118 /*
1119  *	compare the values of two symbol table entries.
1120  */
1121 static int
1122 symcomp(void *a, void *b)
1123 {
1124 	return (*(Sym**)a)->value - (*(Sym**)b)->value;
1125 }
1126 /*
1127  *	compare the values of the symbols referenced by two text table entries
1128  */
1129 static int
1130 txtcomp(void *a, void *b)
1131 {
1132 	return ((Txtsym*)a)->sym->value - ((Txtsym*)b)->sym->value;
1133 }
1134 /*
1135  *	compare the values of the symbols referenced by two file table entries
1136  */
1137 static int
1138 filecomp(void *a, void *b)
1139 {
1140 	return ((File*)a)->addr - ((File*)b)->addr;
1141 }
1142 /*
1143  *	fill an interface Symbol structure from a symbol table entry
1144  */
1145 static void
1146 fillsym(Sym *sp, Symbol *s)
1147 {
1148 	s->type = sp->type;
1149 	s->value = sp->value;
1150 	s->name = sp->name;
1151 	switch(sp->type) {
1152 	case 'b':
1153 	case 'B':
1154 	case 'D':
1155 	case 'd':
1156 		s->class = CDATA;
1157 		break;
1158 	case 't':
1159 	case 'T':
1160 	case 'l':
1161 	case 'L':
1162 		s->class = CTEXT;
1163 		break;
1164 	case 'a':
1165 		s->class = CAUTO;
1166 		break;
1167 	case 'p':
1168 		s->class = CPARAM;
1169 		break;
1170 	case 'm':
1171 		s->class = CSTAB;
1172 		break;
1173 	default:
1174 		s->class = CNONE;
1175 		break;
1176 	}
1177 	s->handle = 0;
1178 }
1179 /*
1180  *	find the stack frame, given the pc
1181  */
1182 long
1183 pc2sp(ulong pc)
1184 {
1185 	uchar *c;
1186 	uchar u;
1187 	ulong currpc;
1188 	long currsp;
1189 
1190 	if(spoff == 0)
1191 		return -1;
1192 	currsp = 0;
1193 	currpc = txtstart - mach->pcquant;
1194 
1195 	if(pc<currpc || pc>txtend)
1196 		return -1;
1197 	for(c = spoff; c < spoffend; c++) {
1198 		if (currpc >= pc)
1199 			return currsp;
1200 		u = *c;
1201 		if (u == 0) {
1202 			currsp += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
1203 			c += 4;
1204 		}
1205 		else if (u < 65)
1206 			currsp += 4*u;
1207 		else if (u < 129)
1208 			currsp -= 4*(u-64);
1209 		else
1210 			currpc += mach->pcquant*(u-129);
1211 		currpc += mach->pcquant;
1212 	}
1213 	return -1;
1214 }
1215 /*
1216  *	find the source file line number for a given value of the pc
1217  */
1218 long
1219 pc2line(ulong pc)
1220 {
1221 	File *fp;
1222 	return pc2fline(pc, &fp);
1223 }
1224 /*
1225  *	Convert a pc into a file table pointer & line offset
1226  */
1227 static long
1228 pc2fline(ulong pc, File **fpp)
1229 {
1230 	File *f;
1231 	int i;
1232 
1233 	i = pc2filex(pc);
1234 	if (i < 0)
1235 		return i;
1236 	if (i >= npcl){
1237 		/* Precompute starting points by file, else too slow */
1238 		if (npcl == 0){
1239 			Pcl pcl;
1240 
1241 			/* decode start for pcline machine */
1242 			pcl.pcline = pcline;
1243 			pcl.pc = txtstart - mach->pcquant;
1244 			pcl.line = 0;
1245 			pcl2line(files->addr, &pcl, &files[0].pcl);
1246 			npcl = 1;
1247 		}
1248 		for (f = &files[npcl]; npcl <= i; npcl++, f++)
1249 			if (pcl2line(f->addr, &(f-1)->pcl, &f->pcl) < 0)
1250 				break;
1251 	}
1252 	f = &files[i];
1253 	*fpp = f;
1254 	return pcl2line(pc, &f->pcl, 0);
1255 }
1256 /*
1257  *	Convert pc to line offset, given Pcl starting point,
1258  *	 saving Pcl result.
1259  */
1260 static long
1261 pcl2line(ulong pc, Pcl *bp, Pcl *rp)
1262 {
1263 	uchar *c;
1264 	uchar u;
1265 	ulong currpc;
1266 	long currline;
1267 
1268 	if(bp->pcline == 0)
1269 		return -1;
1270 	currpc = bp->pc;
1271 	currline = bp->line;
1272 	if(pc<currpc || pc>txtend)
1273 		return -1;
1274 	for(c = bp->pcline; c < pclineend; c++) {
1275 		if (currpc >= pc) {
1276 			if (rp){
1277 				rp->pc = currpc;
1278 				rp->line = currline;
1279 				rp->pcline = c;
1280 			}
1281 			return currline;
1282 		}
1283 		u = *c;
1284 		if(u == 0) {
1285 			currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
1286 			c += 4;
1287 		}
1288 		else if(u < 65)
1289 			currline += u;
1290 		else if(u < 129)
1291 			currline -= (u-64);
1292 		else
1293 			currpc += mach->pcquant*(u-129);
1294 		currpc += mach->pcquant;
1295 	}
1296 	return -1;
1297 }
1298 /*
1299  *	find the pc associated with a line number
1300  *	basepc and endpc are text addresses bounding the search.
1301  *	if endpc == 0, the end of the table is used (i.e., no upper bound).
1302  *	usually, basepc and endpc contain the first text address in
1303  *	a file and the first text address in the following file, respectively.
1304  */
1305 long
1306 line2addr(ulong line, ulong basepc, ulong endpc)
1307 {
1308 	uchar *c;
1309 	uchar u;
1310 	ulong currpc;
1311 	long currline;
1312 	long delta, d;
1313 	long pc, found;
1314 
1315 	if(pcline == 0 || line == 0)
1316 		return -1;
1317 
1318 	currline = 0;
1319 	currpc = txtstart-mach->pcquant;
1320 	pc = -1;
1321 	found = 0;
1322 	delta = HUGEINT;
1323 
1324 	for(c = pcline; c < pclineend; c++) {
1325 		if(endpc && currpc >= endpc)	/* end of file of interest */
1326 			break;
1327 		if(currpc >= basepc) {		/* proper file */
1328 			if(currline >= line) {
1329 				d = currline-line;
1330 				found = 1;
1331 			} else
1332 				d = line-currline;
1333 			if(d < delta) {
1334 				delta = d;
1335 				pc = currpc;
1336 			}
1337 		}
1338 		u = *c;
1339 		if(u == 0) {
1340 			currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
1341 			c += 4;
1342 		}
1343 		else if(u < 65)
1344 			currline += u;
1345 		else if(u < 129)
1346 			currline -= (u-64);
1347 		else
1348 			currpc += mach->pcquant*(u-129);
1349 		currpc += mach->pcquant;
1350 	}
1351 	if(found)
1352 		return pc;
1353 	return -1;
1354 }
1355 /*
1356  *	Print a history stack (debug). if count is 0, prints the whole stack
1357  */
1358 static void
1359 printhist(char *msg, Hist *hp, int count)
1360 {
1361 	int i;
1362 	uchar *cp;
1363 	char buf[128];
1364 
1365 	i = 0;
1366 	while(hp->name) {
1367 		if(count && ++i > count)
1368 			break;
1369 		fprint(2,"%s Line: %lx (%ld)  Offset: %lx (%ld)  Name: ", msg,
1370 			hp->line, hp->line, hp->offset, hp->offset);
1371 		for(cp = (uchar *)hp->name+1; (*cp<<8)|cp[1]; cp += 2) {
1372 			if (cp != (uchar *)hp->name+1)
1373 				fprint(2,"/");
1374 			fprint(2,"%x", (*cp<<8)|cp[1]);
1375 		}
1376 		fileelem(fnames, (uchar *) hp->name, buf, sizeof(buf));
1377 		fprint(2," (%s)\n", buf);
1378 		hp++;
1379 	}
1380 }
1381 
1382 #ifdef DEBUG
1383 /*
1384  *	print the history stack for a file. (debug only)
1385  *	if (name == 0) => print all history stacks.
1386  */
1387 void
1388 dumphist(char *name)
1389 {
1390 	int i;
1391 	File *f;
1392 	short *fname;
1393 
1394 	if(buildtbls() == 0)
1395 		return;
1396 	if(name)
1397 		fname = encfname(name);
1398 	for(i = 0, f = files; i < nfiles; i++, f++)
1399 		if(fname == 0 || hcomp(f->hist, fname))
1400 			printhist("> ", f->hist, f->n);
1401 
1402 	if(fname)
1403 		free(fname);
1404 }
1405 #endif
1406