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