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