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