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