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