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