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