github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/libmach/sym.c (about)

     1  // Inferno libmach/sym.c
     2  // http://code.google.com/p/inferno-os/source/browse/utils/libmach/sym.c
     3  //
     4  //	Copyright © 1994-1999 Lucent Technologies Inc.
     5  //	Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
     6  //	Portions Copyright © 1997-1999 Vita Nuova Limited.
     7  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
     8  //	Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
     9  //	Portions Copyright © 2009 The Go Authors.  All rights reserved.
    10  //
    11  // Permission is hereby granted, free of charge, to any person obtaining a copy
    12  // of this software and associated documentation files (the "Software"), to deal
    13  // in the Software without restriction, including without limitation the rights
    14  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    15  // copies of the Software, and to permit persons to whom the Software is
    16  // furnished to do so, subject to the following conditions:
    17  //
    18  // The above copyright notice and this permission notice shall be included in
    19  // all copies or substantial portions of the Software.
    20  //
    21  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    22  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    23  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    24  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    25  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    26  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    27  // THE SOFTWARE.
    28  
    29  #include <u.h>
    30  #include <libc.h>
    31  #include <bio.h>
    32  #include <mach.h>
    33  
    34  #define	HUGEINT	0x7fffffff
    35  #define	NNAME	20		/* a relic of the past */
    36  
    37  typedef	struct txtsym Txtsym;
    38  typedef	struct file File;
    39  typedef	struct hist Hist;
    40  
    41  struct txtsym {				/* Text Symbol table */
    42  	int 	n;			/* number of local vars */
    43  	Sym	**locals;		/* array of ptrs to autos */
    44  	Sym	*sym;			/* function symbol entry */
    45  };
    46  
    47  struct hist {				/* Stack of include files & #line directives */
    48  	char	*name;			/* Assumes names Null terminated in file */
    49  	int32	line;			/* line # where it was included */
    50  	int32	offset;			/* line # of #line directive */
    51  };
    52  
    53  struct file {				/* Per input file header to history stack */
    54  	uvlong	addr;			/* address of first text sym */
    55  	union {
    56  		Txtsym	*txt;		/* first text symbol */
    57  		Sym	*sym;		/* only during initilization */
    58  	};
    59  	int	n;			/* size of history stack */
    60  	Hist	*hist;			/* history stack */
    61  };
    62  
    63  static	int	debug = 0;
    64  
    65  static	Sym	**autos;		/* Base of auto variables */
    66  static	File	*files;			/* Base of file arena */
    67  static	int	fmaxi;			/* largest file path index */
    68  static	Sym	**fnames;		/* file names path component table */
    69  static	Sym	**globals;		/* globals by addr table */
    70  static	Hist	*hist;			/* base of history stack */
    71  static	int	isbuilt;		/* internal table init flag */
    72  static	int32	nauto;			/* number of automatics */
    73  static	int32	nfiles;			/* number of files */
    74  static	int32	nglob;			/* number of globals */
    75  static	int32	nhist;			/* number of history stack entries */
    76  static	int32	nsym;			/* number of symbols */
    77  static	int	ntxt;			/* number of text symbols */
    78  static	uchar	*pcline;		/* start of pc-line state table */
    79  static	uchar 	*pclineend;		/* end of pc-line table */
    80  static	uchar	*spoff;			/* start of pc-sp state table */
    81  static	uchar	*spoffend;		/* end of pc-sp offset table */
    82  static	Sym	*symbols;		/* symbol table */
    83  static	Txtsym	*txt;			/* Base of text symbol table */
    84  static	uvlong	txtstart;		/* start of text segment */
    85  static	uvlong	txtend;			/* end of text segment */
    86  static	uvlong	firstinstr;		/* as found from symtab; needed for amd64 */
    87  
    88  static void	cleansyms(void);
    89  static int32	decodename(Biobuf*, Sym*);
    90  static short	*encfname(char*);
    91  static int 	fline(char*, int, int32, Hist*, Hist**);
    92  static void	fillsym(Sym*, Symbol*);
    93  static int	findglobal(char*, Symbol*);
    94  static int	findlocvar(Symbol*, char *, Symbol*);
    95  static int	findtext(char*, Symbol*);
    96  static int	hcomp(Hist*, short*);
    97  static int	hline(File*, short*, int32*);
    98  static void	printhist(char*, Hist*, int);
    99  static int	buildtbls(void);
   100  static int	symcomp(const void*, const void*);
   101  static int	symerrmsg(int, char*);
   102  static int	txtcomp(const void*, const void*);
   103  static int	filecomp(const void*, const void*);
   104  
   105  /*
   106   * Go 1.2 pcln table (also contains pcsp).
   107   */
   108  #define Go12PclnMagic 0xfffffffb
   109  #define Go12PclnMagicRev 0xfbffffff
   110  static int	isgo12pcline(void);
   111  static uvlong go12pc2sp(uvlong);
   112  static int32 go12fileline(char*, int, uvlong);
   113  static void	go12clean(void);
   114  static uvlong go12file2pc(char*, int);
   115  
   116  /*
   117   *	initialize the symbol tables
   118   */
   119  int
   120  syminit(int fd, Fhdr *fp)
   121  {
   122  	Sym *p;
   123  	int32 i, l, size, symsz;
   124  	vlong vl;
   125  	Biobuf b;
   126  	int svalsz, newformat, shift;
   127  	uvlong (*swav)(uvlong);
   128  	uint32 (*swal)(uint32);
   129  	uchar buf[8], c;
   130  
   131  	if(fp->symsz == 0)
   132  		return 0;
   133  	if(fp->type == FNONE)
   134  		return 0;
   135  
   136  	swav = beswav;
   137  	swal = beswal;
   138  
   139  	cleansyms();
   140  	textseg(fp->txtaddr, fp);
   141  		/* minimum symbol record size = 4+1+2 bytes */
   142  	symbols = malloc((fp->symsz/(4+1+2)+1)*sizeof(Sym));
   143  	if(symbols == 0) {
   144  		werrstr("can't malloc %d bytes", fp->symsz);
   145  		return -1;
   146  	}
   147  	Binit(&b, fd, OREAD);
   148  	Bseek(&b, fp->symoff, 0);
   149  	memset(buf, 0, sizeof buf);
   150  	Bread(&b, buf, sizeof buf);
   151  	newformat = 0;
   152  	symsz = fp->symsz;
   153  	if(memcmp(buf, "\xfd\xff\xff\xff\x00\x00\x00", 7) == 0) {
   154  		swav = leswav;
   155  		swal = leswal;
   156  		newformat = 1;
   157  	} else if(memcmp(buf, "\xff\xff\xff\xfd\x00\x00\x00", 7) == 0) {
   158  		newformat = 1;
   159  	} else if(memcmp(buf, "\xfe\xff\xff\xff\x00\x00", 6) == 0) {
   160  		// Table format used between Go 1.0 and Go 1.1:
   161  		// little-endian but otherwise same as the old Go 1.0 table.
   162  		// Not likely to be seen much in practice, but easy to handle.
   163  		swav = leswav;
   164  		swal = leswal;
   165  		Bseek(&b, fp->symoff+6, 0);
   166  		symsz -= 6;
   167  	} else {
   168  		Bseek(&b, fp->symoff, 0);
   169  	}
   170  	svalsz = 0;
   171  	if(newformat) {
   172  		svalsz = buf[7];
   173  		if(svalsz != 4 && svalsz != 8) {
   174  			werrstr("invalid word size %d bytes", svalsz);
   175  			return -1;
   176  		}
   177  		symsz -= 8;
   178  	}
   179  
   180  	nsym = 0;
   181  	size = 0;
   182  	for(p = symbols; size < symsz; p++, nsym++) {
   183  		if(newformat) {
   184  			// Go 1.1 format. See comment at top of ../pkg/runtime/symtab.c.
   185  			if(Bread(&b, &c, 1) != 1)
   186  				return symerrmsg(1, "symbol");
   187  			if((c&0x3F) < 26)
   188  				p->type = (c&0x3F)+ 'A';
   189  			else
   190  				p->type = (c&0x3F) - 26 + 'a';
   191  			size++;
   192  
   193  			if(c&0x40) {
   194  				// Fixed-width address.
   195  				if(svalsz == 8) {
   196  					if(Bread(&b, &vl, 8) != 8)
   197  						return symerrmsg(8, "symbol");
   198  					p->value = swav(vl);
   199  				} else {
   200  					if(Bread(&b, &l, 4) != 4)
   201  						return symerrmsg(4, "symbol");
   202  					p->value = (u32int)swal(l);
   203  				}
   204  				size += svalsz;
   205  			} else {
   206  				// Varint address.
   207  				shift = 0;
   208  				p->value = 0;
   209  				for(;;) {
   210  					if(Bread(&b, buf, 1) != 1)
   211  						return symerrmsg(1, "symbol");
   212  					p->value |= (uint64)(buf[0]&0x7F)<<shift;
   213  					shift += 7;
   214  					size++;
   215  					if((buf[0]&0x80) == 0)
   216  						break;
   217  				}
   218  			}
   219  			p->gotype = 0;
   220  			if(c&0x80) {
   221  				// Has Go type. Fixed-width address.
   222  				if(svalsz == 8) {
   223  					if(Bread(&b, &vl, 8) != 8)
   224  						return symerrmsg(8, "symbol");
   225  					p->gotype = swav(vl);
   226  				} else {
   227  					if(Bread(&b, &l, 4) != 4)
   228  						return symerrmsg(4, "symbol");
   229  					p->gotype = (u32int)swal(l);
   230  				}
   231  				size += svalsz;
   232  			}
   233  			
   234  			// Name.
   235  			p->type |= 0x80; // for decodename
   236  			i = decodename(&b, p);
   237  			if(i < 0)
   238  				return -1;
   239  			size += i;
   240  		} else {
   241  			// Go 1.0 format: Plan 9 format + go type symbol.
   242  			if(fp->_magic && (fp->magic & HDR_MAGIC)){
   243  				svalsz = 8;
   244  				if(Bread(&b, &vl, 8) != 8)
   245  					return symerrmsg(8, "symbol");
   246  				p->value = swav(vl);
   247  			}
   248  			else{
   249  				svalsz = 4;
   250  				if(Bread(&b, &l, 4) != 4)
   251  					return symerrmsg(4, "symbol");
   252  				p->value = (u32int)swal(l);
   253  			}
   254  			if(Bread(&b, &p->type, sizeof(p->type)) != sizeof(p->type))
   255  				return symerrmsg(sizeof(p->value), "symbol");
   256  	
   257  			i = decodename(&b, p);
   258  			if(i < 0)
   259  				return -1;
   260  			size += i+svalsz+sizeof(p->type);
   261  	
   262  			if(svalsz == 8){
   263  				if(Bread(&b, &vl, 8) != 8)
   264  					return symerrmsg(8, "symbol");
   265  				p->gotype = swav(vl);
   266  			}
   267  			else{
   268  				if(Bread(&b, &l, 4) != 4)
   269  					return symerrmsg(4, "symbol");
   270  				p->gotype = (u32int)swal(l);
   271  			}
   272  			size += svalsz;
   273  		}
   274  
   275  		/* count global & auto vars, text symbols, and file names */
   276  		switch (p->type) {
   277  		case 'l':
   278  		case 'L':
   279  		case 't':
   280  		case 'T':
   281  			ntxt++;
   282  			break;
   283  		case 'd':
   284  		case 'D':
   285  		case 'b':
   286  		case 'B':
   287  			nglob++;
   288  			break;
   289  		case 'f':
   290  			if(strcmp(p->name, ".frame") == 0) {
   291  				p->type = 'm';
   292  				nauto++;
   293  			}
   294  			else if(p->value > fmaxi)
   295  				fmaxi = p->value;	/* highest path index */
   296  			break;
   297  		case 'a':
   298  		case 'p':
   299  		case 'm':
   300  			nauto++;
   301  			break;
   302  		case 'z':
   303  			if(p->value == 1) {		/* one extra per file */
   304  				nhist++;
   305  				nfiles++;
   306  			}
   307  			nhist++;
   308  			break;
   309  		default:
   310  			break;
   311  		}
   312  	}
   313  	if (debug)
   314  		print("NG: %d NT: %d NF: %d\n", nglob, ntxt, fmaxi);
   315  	if (fp->sppcsz) {			/* pc-sp offset table */
   316  		spoff = (uchar *)malloc(fp->sppcsz);
   317  		if(spoff == 0) {
   318  			werrstr("can't malloc %d bytes", fp->sppcsz);
   319  			return -1;
   320  		}
   321  		Bseek(&b, fp->sppcoff, 0);
   322  		if(Bread(&b, spoff, fp->sppcsz) != fp->sppcsz){
   323  			spoff = 0;
   324  			return symerrmsg(fp->sppcsz, "sp-pc");
   325  		}
   326  		spoffend = spoff+fp->sppcsz;
   327  	}
   328  	if (fp->lnpcsz) {			/* pc-line number table */
   329  		pcline = (uchar *)malloc(fp->lnpcsz);
   330  		if(pcline == 0) {
   331  			werrstr("can't malloc %d bytes", fp->lnpcsz);
   332  			return -1;
   333  		}
   334  		Bseek(&b, fp->lnpcoff, 0);
   335  		if(Bread(&b, pcline, fp->lnpcsz) != fp->lnpcsz){
   336  			pcline = 0;
   337  			return symerrmsg(fp->lnpcsz, "pc-line");
   338  		}
   339  		pclineend = pcline+fp->lnpcsz;
   340  	}
   341  	return nsym;
   342  }
   343  
   344  static int
   345  symerrmsg(int n, char *table)
   346  {
   347  	werrstr("can't read %d bytes of %s table", n, table);
   348  	return -1;
   349  }
   350  
   351  static int32
   352  decodename(Biobuf *bp, Sym *p)
   353  {
   354  	char *cp;
   355  	int c1, c2;
   356  	int32 n;
   357  	vlong o;
   358  
   359  	if((p->type & 0x80) == 0) {		/* old-style, fixed length names */
   360  		p->name = malloc(NNAME);
   361  		if(p->name == 0) {
   362  			werrstr("can't malloc %d bytes", NNAME);
   363  			return -1;
   364  		}
   365  		if(Bread(bp, p->name, NNAME) != NNAME)
   366  			return symerrmsg(NNAME, "symbol");
   367  		Bseek(bp, 3, 1);
   368  		return NNAME+3;
   369  	}
   370  
   371  	p->type &= ~0x80;
   372  	if(p->type == 'z' || p->type == 'Z') {
   373  		o = Bseek(bp, 0, 1);
   374  		if(BGETC(bp) < 0) {
   375  			werrstr("can't read symbol name");
   376  			return -1;
   377  		}
   378  		for(;;) {
   379  			c1 = BGETC(bp);
   380  			c2 = BGETC(bp);
   381  			if(c1 < 0 || c2 < 0) {
   382  				werrstr("can't read symbol name");
   383  				return -1;
   384  			}
   385  			if(c1 == 0 && c2 == 0)
   386  				break;
   387  		}
   388  		n = Bseek(bp, 0, 1)-o;
   389  		p->name = malloc(n);
   390  		if(p->name == 0) {
   391  			werrstr("can't malloc %d bytes", n);
   392  			return -1;
   393  		}
   394  		Bseek(bp, -n, 1);
   395  		if(Bread(bp, p->name, n) != n) {
   396  			werrstr("can't read %d bytes of symbol name", n);
   397  			return -1;
   398  		}
   399  	} else {
   400  		cp = Brdline(bp, '\0');
   401  		if(cp == 0) {
   402  			werrstr("can't read symbol name");
   403  			return -1;
   404  		}
   405  		n = Blinelen(bp);
   406  		p->name = malloc(n);
   407  		if(p->name == 0) {
   408  			werrstr("can't malloc %d bytes", n);
   409  			return -1;
   410  		}
   411  		strcpy(p->name, cp);
   412  	}
   413  	return n;
   414  }
   415  
   416  /*
   417   *	free any previously loaded symbol tables
   418   */
   419  static void
   420  cleansyms(void)
   421  {
   422  	if(globals)
   423  		free(globals);
   424  	globals = 0;
   425  	nglob = 0;
   426  	if(txt)
   427  		free(txt);
   428  	txt = 0;
   429  	ntxt = 0;
   430  	if(fnames)
   431  		free(fnames);
   432  	fnames = 0;
   433  	fmaxi = 0;
   434  
   435  	if(files)
   436  		free(files);
   437  	files = 0;
   438  	nfiles = 0;
   439  	if(hist)
   440  		free(hist);
   441  	hist = 0;
   442  	nhist = 0;
   443  	if(autos)
   444  		free(autos);
   445  	autos = 0;
   446  	nauto = 0;
   447  	isbuilt = 0;
   448  	if(symbols)
   449  		free(symbols);
   450  	symbols = 0;
   451  	nsym = 0;
   452  	if(spoff)
   453  		free(spoff);
   454  	spoff = 0;
   455  	if(pcline)
   456  		free(pcline);
   457  	pcline = 0;
   458  	go12clean();
   459  }
   460  
   461  /*
   462   *	delimit the text segment
   463   */
   464  void
   465  textseg(uvlong base, Fhdr *fp)
   466  {
   467  	txtstart = base;
   468  	txtend = base+fp->txtsz;
   469  }
   470  
   471  /*
   472   *	symbase: return base and size of raw symbol table
   473   *		(special hack for high access rate operations)
   474   */
   475  Sym *
   476  symbase(int32 *n)
   477  {
   478  	*n = nsym;
   479  	return symbols;
   480  }
   481  
   482  /*
   483   *	Get the ith symbol table entry
   484   */
   485  Sym *
   486  getsym(int index)
   487  {
   488  	if(index >= 0 && index < nsym)
   489  		return &symbols[index];
   490  	return 0;
   491  }
   492  
   493  /*
   494   *	initialize internal symbol tables
   495   */
   496  static int
   497  buildtbls(void)
   498  {
   499  	int32 i;
   500  	int j, nh, ng, nt;
   501  	File *f;
   502  	Txtsym *tp;
   503  	Hist *hp;
   504  	Sym *p, **ap;
   505  
   506  	if(isbuilt)
   507  		return 1;
   508  	isbuilt = 1;
   509  			/* allocate the tables */
   510  	firstinstr = 0;
   511  	if(nglob) {
   512  		globals = malloc(nglob*sizeof(*globals));
   513  		if(!globals) {
   514  			werrstr("can't malloc global symbol table");
   515  			return 0;
   516  		}
   517  	}
   518  	if(ntxt) {
   519  		txt = malloc(ntxt*sizeof(*txt));
   520  		if (!txt) {
   521  			werrstr("can't malloc text symbol table");
   522  			return 0;
   523  		}
   524  	}
   525  	fnames = malloc((fmaxi+1)*sizeof(*fnames));
   526  	if (!fnames) {
   527  		werrstr("can't malloc file name table");
   528  		return 0;
   529  	}
   530  	memset(fnames, 0, (fmaxi+1)*sizeof(*fnames));
   531  	files = malloc(nfiles*sizeof(*files));
   532  	if(!files) {
   533  		werrstr("can't malloc file table");
   534  		return 0;
   535  	}
   536  	hist = malloc(nhist*sizeof(Hist));
   537  	if(hist == 0) {
   538  		werrstr("can't malloc history stack");
   539  		return 0;
   540  	}
   541  	autos = malloc(nauto*sizeof(Sym*));
   542  	if(autos == 0) {
   543  		werrstr("can't malloc auto symbol table");
   544  		return 0;
   545  	}
   546  		/* load the tables */
   547  	ng = nt = nh = 0;
   548  	f = 0;
   549  	tp = 0;
   550  	i = nsym;
   551  	hp = hist;
   552  	ap = autos;
   553  	for(p = symbols; i-- > 0; p++) {
   554  //print("sym %d type %c name %s value %llux\n", p-symbols, p->type, p->name, p->value);
   555  		switch(p->type) {
   556  		case 'D':
   557  		case 'd':
   558  		case 'B':
   559  		case 'b':
   560  			if(debug)
   561  				print("Global: %s %llux\n", p->name, p->value);
   562  			globals[ng++] = p;
   563  			break;
   564  		case 'z':
   565  			if(p->value == 1) {		/* New file */
   566  				if(f) {
   567  					f->n = nh;
   568  					f->hist[nh].name = 0;	/* one extra */
   569  					hp += nh+1;
   570  					f++;
   571  				}
   572  				else
   573  					f = files;
   574  				f->hist = hp;
   575  				f->sym = 0;
   576  				f->addr = 0;
   577  				nh = 0;
   578  			}
   579  				/* alloc one slot extra as terminator */
   580  			f->hist[nh].name = p->name;
   581  			f->hist[nh].line = p->value;
   582  			f->hist[nh].offset = 0;
   583  			if(debug)
   584  				printhist("-> ", &f->hist[nh], 1);
   585  			nh++;
   586  			break;
   587  		case 'Z':
   588  			if(f && nh > 0)
   589  				f->hist[nh-1].offset = p->value;
   590  			break;
   591  		case 'T':
   592  		case 't':	/* Text: terminate history if first in file */
   593  		case 'L':
   594  		case 'l':
   595  			tp = &txt[nt++];
   596  			tp->n = 0;
   597  			tp->sym = p;
   598  			tp->locals = ap;
   599  			if(debug)
   600  				print("TEXT: %s at %llux\n", p->name, p->value);
   601  			if (firstinstr == 0 || p->value < firstinstr)
   602  				firstinstr = p->value;
   603  			if(f && !f->sym) {			/* first  */
   604  				f->sym = p;
   605  				f->addr = p->value;
   606  			}
   607  			break;
   608  		case 'a':
   609  		case 'p':
   610  		case 'm':		/* Local Vars */
   611  			if(!tp)
   612  				print("Warning: Free floating local var: %s\n",
   613  					p->name);
   614  			else {
   615  				if(debug)
   616  					print("Local: %s %llux\n", p->name, p->value);
   617  				tp->locals[tp->n] = p;
   618  				tp->n++;
   619  				ap++;
   620  			}
   621  			break;
   622  		case 'f':		/* File names */
   623  			if(debug)
   624  				print("Fname: %s\n", p->name);
   625  			fnames[p->value] = p;
   626  			break;
   627  		default:
   628  			break;
   629  		}
   630  	}
   631  		/* sort global and text tables into ascending address order */
   632  	qsort(globals, nglob, sizeof(Sym*), symcomp);
   633  	qsort(txt, ntxt, sizeof(Txtsym), txtcomp);
   634  	qsort(files, nfiles, sizeof(File), filecomp);
   635  	tp = txt;
   636  	for(i = 0, f = files; i < nfiles; i++, f++) {
   637  		for(j = 0; j < ntxt; j++) {
   638  			if(f->sym == tp->sym) {
   639  				if(debug) {
   640  					print("LINK: %s to at %llux", f->sym->name, f->addr);
   641  					printhist("... ", f->hist, 1);
   642  				}
   643  				f->txt = tp++;
   644  				break;
   645  			}
   646  			if(++tp >= txt+ntxt)	/* wrap around */
   647  				tp = txt;
   648  		}
   649  	}
   650  	return 1;
   651  }
   652  
   653  /*
   654   * find symbol function.var by name.
   655   *	fn != 0 && var != 0	=> look for fn in text, var in data
   656   *	fn != 0 && var == 0	=> look for fn in text
   657   *	fn == 0 && var != 0	=> look for var first in text then in data space.
   658   */
   659  int
   660  lookup(char *fn, char *var, Symbol *s)
   661  {
   662  	int found;
   663  
   664  	if(buildtbls() == 0)
   665  		return 0;
   666  	if(fn) {
   667  		found = findtext(fn, s);
   668  		if(var == 0)		/* case 2: fn not in text */
   669  			return found;
   670  		else if(!found)		/* case 1: fn not found */
   671  			return 0;
   672  	} else if(var) {
   673  		found = findtext(var, s);
   674  		if(found)
   675  			return 1;	/* case 3: var found in text */
   676  	} else return 0;		/* case 4: fn & var == zero */
   677  
   678  	if(found)
   679  		return findlocal(s, var, s);	/* case 1: fn found */
   680  	return findglobal(var, s);		/* case 3: var not found */
   681  
   682  }
   683  
   684  /*
   685   * strcmp, but allow '_' to match center dot (rune 00b7 == bytes c2 b7)
   686   */
   687  int
   688  cdotstrcmp(char *sym, char *user)
   689  {
   690  	for (;;) {
   691  		while (*sym == *user) {
   692  			if (*sym++ == '\0')
   693  				return 0;
   694  			user++;
   695  		}
   696  		/* unequal - but maybe '_' matches center dot */
   697  		if (user[0] == '_' && (sym[0]&0xFF) == 0xc2 && (sym[1]&0xFF) == 0xb7) {
   698  			/* '_' matches center dot - advance and continue */
   699  			user++;
   700  			sym += 2;
   701  			continue;
   702  		}
   703  		break;
   704  	}
   705  	return *user - *sym;
   706  }
   707  
   708  /*
   709   * find a function by name
   710   */
   711  static int
   712  findtext(char *name, Symbol *s)
   713  {
   714  	int i;
   715  
   716  	for(i = 0; i < ntxt; i++) {
   717  		if(cdotstrcmp(txt[i].sym->name, name) == 0) {
   718  			fillsym(txt[i].sym, s);
   719  			s->handle = (void *) &txt[i];
   720  			s->index = i;
   721  			return 1;
   722  		}
   723  	}
   724  	return 0;
   725  }
   726  /*
   727   * find global variable by name
   728   */
   729  static int
   730  findglobal(char *name, Symbol *s)
   731  {
   732  	int32 i;
   733  
   734  	for(i = 0; i < nglob; i++) {
   735  		if(cdotstrcmp(globals[i]->name, name) == 0) {
   736  			fillsym(globals[i], s);
   737  			s->index = i;
   738  			return 1;
   739  		}
   740  	}
   741  	return 0;
   742  }
   743  
   744  /*
   745   *	find the local variable by name within a given function
   746   */
   747  int
   748  findlocal(Symbol *s1, char *name, Symbol *s2)
   749  {
   750  	if(s1 == 0)
   751  		return 0;
   752  	if(buildtbls() == 0)
   753  		return 0;
   754  	return findlocvar(s1, name, s2);
   755  }
   756  
   757  /*
   758   *	find the local variable by name within a given function
   759   *		(internal function - does no parameter validation)
   760   */
   761  static int
   762  findlocvar(Symbol *s1, char *name, Symbol *s2)
   763  {
   764  	Txtsym *tp;
   765  	int i;
   766  
   767  	tp = (Txtsym *)s1->handle;
   768  	if(tp && tp->locals) {
   769  		for(i = 0; i < tp->n; i++)
   770  			if (cdotstrcmp(tp->locals[i]->name, name) == 0) {
   771  				fillsym(tp->locals[i], s2);
   772  				s2->handle = (void *)tp;
   773  				s2->index = tp->n-1 - i;
   774  				return 1;
   775  			}
   776  	}
   777  	return 0;
   778  }
   779  
   780  /*
   781   *	Get ith text symbol
   782   */
   783  int
   784  textsym(Symbol *s, int index)
   785  {
   786  
   787  	if(buildtbls() == 0)
   788  		return 0;
   789  	if(index < 0 || index >= ntxt)
   790  		return 0;
   791  	fillsym(txt[index].sym, s);
   792  	s->handle = (void *)&txt[index];
   793  	s->index = index;
   794  	return 1;
   795  }
   796  
   797  /*
   798   *	Get ith file name
   799   */
   800  int
   801  filesym(int index, char *buf, int n)
   802  {
   803  	Hist *hp;
   804  
   805  	if(buildtbls() == 0)
   806  		return 0;
   807  	if(index < 0 || index >= nfiles)
   808  		return 0;
   809  	hp = files[index].hist;
   810  	if(!hp || !hp->name)
   811  		return 0;
   812  	return fileelem(fnames, (uchar*)hp->name, buf, n);
   813  }
   814  
   815  /*
   816   *	Lookup name of local variable located at an offset into the frame.
   817   *	The type selects either a parameter or automatic.
   818   */
   819  int
   820  getauto(Symbol *s1, int off, int type, Symbol *s2)
   821  {
   822  	Txtsym *tp;
   823  	Sym *p;
   824  	int i, t;
   825  
   826  	if(s1 == 0)
   827  		return 0;
   828  	if(type == CPARAM)
   829  		t = 'p';
   830  	else if(type == CAUTO)
   831  		t = 'a';
   832  	else
   833  		return 0;
   834  	if(buildtbls() == 0)
   835  		return 0;
   836  	tp = (Txtsym *)s1->handle;
   837  	if(tp == 0)
   838  		return 0;
   839  	for(i = 0; i < tp->n; i++) {
   840  		p = tp->locals[i];
   841  		if(p->type == t && p->value == off) {
   842  			fillsym(p, s2);
   843  			s2->handle = s1->handle;
   844  			s2->index = tp->n-1 - i;
   845  			return 1;
   846  		}
   847  	}
   848  	return 0;
   849  }
   850  
   851  /*
   852   * Find text symbol containing addr; binary search assumes text array is sorted by addr
   853   */
   854  static int
   855  srchtext(uvlong addr)
   856  {
   857  	uvlong val;
   858  	int top, bot, mid;
   859  	Sym *sp;
   860  
   861  	val = addr;
   862  	bot = 0;
   863  	top = ntxt;
   864  	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
   865  		sp = txt[mid].sym;
   866  		if(val < sp->value)
   867  			top = mid;
   868  		else if(mid != ntxt-1 && val >= txt[mid+1].sym->value)
   869  			bot = mid;
   870  		else
   871  			return mid;
   872  	}
   873  	return -1;
   874  }
   875  
   876  /*
   877   * Find data symbol containing addr; binary search assumes data array is sorted by addr
   878   */
   879  static int
   880  srchdata(uvlong addr)
   881  {
   882  	uvlong val;
   883  	int top, bot, mid;
   884  	Sym *sp;
   885  
   886  	bot = 0;
   887  	top = nglob;
   888  	val = addr;
   889  	for(mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
   890  		sp = globals[mid];
   891  		if(val < sp->value)
   892  			top = mid;
   893  		else if(mid < nglob-1 && val >= globals[mid+1]->value)
   894  			bot = mid;
   895  		else
   896  			return mid;
   897  	}
   898  	return -1;
   899  }
   900  
   901  /*
   902   * Find symbol containing val in specified search space
   903   * There is a special case when a value falls beyond the end
   904   * of the text segment; if the search space is CTEXT, that value
   905   * (usually etext) is returned.  If the search space is CANY, symbols in the
   906   * data space are searched for a match.
   907   */
   908  int
   909  findsym(uvlong val, int type, Symbol *s)
   910  {
   911  	int i;
   912  
   913  	if(buildtbls() == 0)
   914  		return 0;
   915  
   916  	if(type == CTEXT || type == CANY) {
   917  		i = srchtext(val);
   918  		if(i >= 0) {
   919  			if(type == CTEXT || i != ntxt-1) {
   920  				fillsym(txt[i].sym, s);
   921  				s->handle = (void *) &txt[i];
   922  				s->index = i;
   923  				return 1;
   924  			}
   925  		}
   926  	}
   927  	if(type == CDATA || type == CANY) {
   928  		i = srchdata(val);
   929  		if(i >= 0) {
   930  			fillsym(globals[i], s);
   931  			s->index = i;
   932  			return 1;
   933  		}
   934  	}
   935  	return 0;
   936  }
   937  
   938  /*
   939   *	Find the start and end address of the function containing addr
   940   */
   941  int
   942  fnbound(uvlong addr, uvlong *bounds)
   943  {
   944  	int i;
   945  
   946  	if(buildtbls() == 0)
   947  		return 0;
   948  
   949  	i = srchtext(addr);
   950  	if(0 <= i && i < ntxt-1) {
   951  		bounds[0] = txt[i].sym->value;
   952  		bounds[1] = txt[i+1].sym->value;
   953  		return 1;
   954  	}
   955  	return 0;
   956  }
   957  
   958  /*
   959   * get the ith local symbol for a function
   960   * the input symbol table is reverse ordered, so we reverse
   961   * accesses here to maintain approx. parameter ordering in a stack trace.
   962   */
   963  int
   964  localsym(Symbol *s, int index)
   965  {
   966  	Txtsym *tp;
   967  
   968  	if(s == 0 || index < 0)
   969  		return 0;
   970  	if(buildtbls() == 0)
   971  		return 0;
   972  
   973  	tp = (Txtsym *)s->handle;
   974  	if(tp && tp->locals && index < tp->n) {
   975  		fillsym(tp->locals[tp->n-index-1], s);	/* reverse */
   976  		s->handle = (void *)tp;
   977  		s->index = index;
   978  		return 1;
   979  	}
   980  	return 0;
   981  }
   982  
   983  /*
   984   * get the ith global symbol
   985   */
   986  int
   987  globalsym(Symbol *s, int index)
   988  {
   989  	if(s == 0)
   990  		return 0;
   991  	if(buildtbls() == 0)
   992  		return 0;
   993  
   994  	if(index >=0 && index < nglob) {
   995  		fillsym(globals[index], s);
   996  		s->index = index;
   997  		return 1;
   998  	}
   999  	return 0;
  1000  }
  1001  
  1002  /*
  1003   *	find the pc given a file name and line offset into it.
  1004   */
  1005  uvlong
  1006  file2pc(char *file, int32 line)
  1007  {
  1008  	File *fp;
  1009  	int32 i;
  1010  	uvlong pc, start, end;
  1011  	short *name;
  1012  
  1013  	if(isgo12pcline())
  1014  		return go12file2pc(file, line);
  1015  	if(buildtbls() == 0 || files == 0)
  1016  		return ~(uvlong)0;
  1017  	name = encfname(file);
  1018  	if(name == 0) {			/* encode the file name */
  1019  		werrstr("file %s not found", file);
  1020  		return ~(uvlong)0;
  1021  	}
  1022  		/* find this history stack */
  1023  	for(i = 0, fp = files; i < nfiles; i++, fp++)
  1024  		if (hline(fp, name, &line))
  1025  			break;
  1026  	free(name);
  1027  	if(i >= nfiles) {
  1028  		werrstr("line %d in file %s not found", line, file);
  1029  		return ~(uvlong)0;
  1030  	}
  1031  	start = fp->addr;		/* first text addr this file */
  1032  	if(i < nfiles-1)
  1033  		end = (fp+1)->addr;	/* first text addr next file */
  1034  	else
  1035  		end = 0;		/* last file in load module */
  1036  	/*
  1037  	 * At this point, line contains the offset into the file.
  1038  	 * run the state machine to locate the pc closest to that value.
  1039  	 */
  1040  	if(debug)
  1041  		print("find pc for %d - between: %llux and %llux\n", line, start, end);
  1042  	pc = line2addr(line, start, end);
  1043  	if(pc == ~(uvlong)0) {
  1044  		werrstr("line %d not in file %s", line, file);
  1045  		return ~(uvlong)0;
  1046  	}
  1047  	return pc;
  1048  }
  1049  
  1050  /*
  1051   *	search for a path component index
  1052   */
  1053  static int
  1054  pathcomp(char *s, int n)
  1055  {
  1056  	int i;
  1057  
  1058  	for(i = 0; i <= fmaxi; i++)
  1059  		if(fnames[i] && strncmp(s, fnames[i]->name, n) == 0)
  1060  			return i;
  1061  	return -1;
  1062  }
  1063  
  1064  /*
  1065   *	Encode a char file name as a sequence of short indices
  1066   *	into the file name dictionary.
  1067   */
  1068  static short*
  1069  encfname(char *file)
  1070  {
  1071  	int i, j;
  1072  	char *cp, *cp2;
  1073  	short *dest;
  1074  
  1075  	if(*file == '/')	/* always check first '/' */
  1076  		cp2 = file+1;
  1077  	else {
  1078  		cp2 = strchr(file, '/');
  1079  		if(!cp2)
  1080  			cp2 = strchr(file, 0);
  1081  	}
  1082  	cp = file;
  1083  	dest = 0;
  1084  	for(i = 0; *cp; i++) {
  1085  		j = pathcomp(cp, cp2-cp);
  1086  		if(j < 0)
  1087  			return 0;	/* not found */
  1088  		dest = realloc(dest, (i+1)*sizeof(short));
  1089  		dest[i] = j;
  1090  		cp = cp2;
  1091  		while(*cp == '/')	/* skip embedded '/'s */
  1092  			cp++;
  1093  		cp2 = strchr(cp, '/');
  1094  		if(!cp2)
  1095  			cp2 = strchr(cp, 0);
  1096  	}
  1097  	dest = realloc(dest, (i+1)*sizeof(short));
  1098  	dest[i] = 0;
  1099  	return dest;
  1100  }
  1101  
  1102  /*
  1103   *	Search a history stack for a matching file name accumulating
  1104   *	the size of intervening files in the stack.
  1105   */
  1106  static int
  1107  hline(File *fp, short *name, int32 *line)
  1108  {
  1109  	Hist *hp;
  1110  	int offset, depth;
  1111  	int32 ln;
  1112  
  1113  	for(hp = fp->hist; hp->name; hp++)		/* find name in stack */
  1114  		if(hp->name[1] || hp->name[2]) {
  1115  			if(hcomp(hp, name))
  1116  				break;
  1117  		}
  1118  	if(!hp->name)		/* match not found */
  1119  		return 0;
  1120  	if(debug)
  1121  		printhist("hline found ... ", hp, 1);
  1122  	/*
  1123  	 * unwind the stack until empty or we hit an entry beyond our line
  1124  	 */
  1125  	ln = *line;
  1126  	offset = hp->line-1;
  1127  	depth = 1;
  1128  	for(hp++; depth && hp->name; hp++) {
  1129  		if(debug)
  1130  			printhist("hline inspect ... ", hp, 1);
  1131  		if(hp->name[1] || hp->name[2]) {
  1132  			if(hp->offset){			/* Z record */
  1133  				offset = 0;
  1134  				if(hcomp(hp, name)) {
  1135  					if(*line <= hp->offset)
  1136  						break;
  1137  					ln = *line+hp->line-hp->offset;
  1138  					depth = 1;	/* implicit pop */
  1139  				} else
  1140  					depth = 2;	/* implicit push */
  1141  			} else if(depth == 1 && ln < hp->line-offset)
  1142  					break;		/* Beyond our line */
  1143  			else if(depth++ == 1)		/* push	*/
  1144  				offset -= hp->line;
  1145  		} else if(--depth == 1)		/* pop */
  1146  			offset += hp->line;
  1147  	}
  1148  	*line = ln+offset;
  1149  	return 1;
  1150  }
  1151  
  1152  /*
  1153   *	compare two encoded file names
  1154   */
  1155  static int
  1156  hcomp(Hist *hp, short *sp)
  1157  {
  1158  	uchar *cp;
  1159  	int i, j;
  1160  	short *s;
  1161  
  1162  	cp = (uchar *)hp->name;
  1163  	s = sp;
  1164  	if (*s == 0)
  1165  		return 0;
  1166  	for (i = 1; j = (cp[i]<<8)|cp[i+1]; i += 2) {
  1167  		if(j == 0)
  1168  			break;
  1169  		if(*s == j)
  1170  			s++;
  1171  		else
  1172  			s = sp;
  1173  	}
  1174  	return *s == 0;
  1175  }
  1176  
  1177  /*
  1178   *	Convert a pc to a "file:line {file:line}" string.
  1179   */
  1180  int32
  1181  fileline(char *str, int n, uvlong dot)
  1182  {
  1183  	int32 line, top, bot, mid;
  1184  	File *f;
  1185  
  1186  	if(isgo12pcline())
  1187  		return go12fileline(str, n, dot);
  1188  
  1189  	*str = 0;
  1190  	if(buildtbls() == 0)
  1191  		return 0;
  1192  		/* binary search assumes file list is sorted by addr */
  1193  	bot = 0;
  1194  	top = nfiles;
  1195  	for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
  1196  		f = &files[mid];
  1197  		if(dot < f->addr)
  1198  			top = mid;
  1199  		else if(mid < nfiles-1 && dot >= (f+1)->addr)
  1200  			bot = mid;
  1201  		else {
  1202  			line = pc2line(dot);
  1203  			if(line > 0 && fline(str, n, line, f->hist, 0) >= 0)
  1204  				return 1;
  1205  			break;
  1206  		}
  1207  	}
  1208  	return 0;
  1209  }
  1210  
  1211  /*
  1212   *	Convert a line number within a composite file to relative line
  1213   *	number in a source file.  A composite file is the source
  1214   *	file with included files inserted in line.
  1215   */
  1216  static int
  1217  fline(char *str, int n, int32 line, Hist *base, Hist **ret)
  1218  {
  1219  	Hist *start;			/* start of current level */
  1220  	Hist *h;			/* current entry */
  1221  	int32 delta;			/* sum of size of files this level */
  1222  	int k;
  1223  
  1224  	start = base;
  1225  	h = base;
  1226  	delta = h->line;
  1227  	while(h && h->name && line > h->line) {
  1228  		if(h->name[1] || h->name[2]) {
  1229  			if(h->offset != 0) {	/* #line Directive */
  1230  				delta = h->line-h->offset+1;
  1231  				start = h;
  1232  				base = h++;
  1233  			} else {		/* beginning of File */
  1234  				if(start == base)
  1235  					start = h++;
  1236  				else {
  1237  					k = fline(str, n, line, start, &h);
  1238  					if(k <= 0)
  1239  						return k;
  1240  				}
  1241  			}
  1242  		} else {
  1243  			if(start == base && ret) {	/* end of recursion level */
  1244  				*ret = h;
  1245  				return 1;
  1246  			} else {			/* end of included file */
  1247  				delta += h->line-start->line;
  1248  				h++;
  1249  				start = base;
  1250  			}
  1251  		}
  1252  	}
  1253  	if(!h)
  1254  		return -1;
  1255  	if(start != base)
  1256  		line = line-start->line+1;
  1257  	else
  1258  		line = line-delta+1;
  1259  	if(!h->name)
  1260  		strncpy(str, "<eof>", n);
  1261  	else {
  1262  		k = fileelem(fnames, (uchar*)start->name, str, n);
  1263  		if(k+8 < n)
  1264  			sprint(str+k, ":%d", line);
  1265  	}
  1266  /**********Remove comments for complete back-trace of include sequence
  1267   *	if(start != base) {
  1268   *		k = strlen(str);
  1269   *		if(k+2 < n) {
  1270   *			str[k++] = ' ';
  1271   *			str[k++] = '{';
  1272   *		}
  1273   *		k += fileelem(fnames, (uchar*) base->name, str+k, n-k);
  1274   *		if(k+10 < n)
  1275   *			sprint(str+k, ":%ld}", start->line-delta);
  1276   *	}
  1277   ********************/
  1278  	return 0;
  1279  }
  1280  
  1281  /*
  1282   *	convert an encoded file name to a string.
  1283   */
  1284  int
  1285  fileelem(Sym **fp, uchar *cp, char *buf, int n)
  1286  {
  1287  	int i, j;
  1288  	char *c, *bp, *end;
  1289  
  1290  	bp = buf;
  1291  	end = buf+n-1;
  1292  	for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){
  1293  		c = fp[j]->name;
  1294  		if(bp != buf && bp[-1] != '/' && bp < end)
  1295  			*bp++ = '/';
  1296  		while(bp < end && *c)
  1297  			*bp++ = *c++;
  1298  	}
  1299  	*bp = 0;
  1300  	i =  bp-buf;
  1301  	if(i > 1) {
  1302  		cleanname(buf);
  1303  		i = strlen(buf);
  1304  	}
  1305  	return i;
  1306  }
  1307  
  1308  /*
  1309   *	compare the values of two symbol table entries.
  1310   */
  1311  static int
  1312  symcomp(const void *a, const void *b)
  1313  {
  1314  	int i;
  1315  
  1316  	i = (*(Sym**)a)->value - (*(Sym**)b)->value;
  1317  	if (i)
  1318  		return i;
  1319  	return strcmp((*(Sym**)a)->name, (*(Sym**)b)->name);
  1320  }
  1321  
  1322  /*
  1323   *	compare the values of the symbols referenced by two text table entries
  1324   */
  1325  static int
  1326  txtcomp(const void *a, const void *b)
  1327  {
  1328  	return ((Txtsym*)a)->sym->value - ((Txtsym*)b)->sym->value;
  1329  }
  1330  
  1331  /*
  1332   *	compare the values of the symbols referenced by two file table entries
  1333   */
  1334  static int
  1335  filecomp(const void *a, const void *b)
  1336  {
  1337  	return ((File*)a)->addr - ((File*)b)->addr;
  1338  }
  1339  
  1340  /*
  1341   *	fill an interface Symbol structure from a symbol table entry
  1342   */
  1343  static void
  1344  fillsym(Sym *sp, Symbol *s)
  1345  {
  1346  	s->type = sp->type;
  1347  	s->value = sp->value;
  1348  	s->name = sp->name;
  1349  	s->index = 0;
  1350  	switch(sp->type) {
  1351  	case 'b':
  1352  	case 'B':
  1353  	case 'D':
  1354  	case 'd':
  1355  		s->class = CDATA;
  1356  		break;
  1357  	case 't':
  1358  	case 'T':
  1359  	case 'l':
  1360  	case 'L':
  1361  		s->class = CTEXT;
  1362  		break;
  1363  	case 'a':
  1364  		s->class = CAUTO;
  1365  		break;
  1366  	case 'p':
  1367  		s->class = CPARAM;
  1368  		break;
  1369  	case 'm':
  1370  		s->class = CSTAB;
  1371  		break;
  1372  	default:
  1373  		s->class = CNONE;
  1374  		break;
  1375  	}
  1376  	s->handle = 0;
  1377  }
  1378  
  1379  /*
  1380   *	find the stack frame, given the pc
  1381   */
  1382  uvlong
  1383  pc2sp(uvlong pc)
  1384  {
  1385  	uchar *c, u;
  1386  	uvlong currpc, currsp;
  1387  
  1388  	if(isgo12pcline())
  1389  		return go12pc2sp(pc);
  1390  
  1391  	if(spoff == 0)
  1392  		return ~(uvlong)0;
  1393  	currsp = 0;
  1394  	currpc = txtstart - mach->pcquant;
  1395  
  1396  	if(pc<currpc || pc>txtend)
  1397  		return ~(uvlong)0;
  1398  	for(c = spoff; c < spoffend; c++) {
  1399  		if (currpc >= pc)
  1400  			return currsp;
  1401  		u = *c;
  1402  		if (u == 0) {
  1403  			currsp += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
  1404  			c += 4;
  1405  		}
  1406  		else if (u < 65)
  1407  			currsp += 4*u;
  1408  		else if (u < 129)
  1409  			currsp -= 4*(u-64);
  1410  		else
  1411  			currpc += mach->pcquant*(u-129);
  1412  		currpc += mach->pcquant;
  1413  	}
  1414  	return ~(uvlong)0;
  1415  }
  1416  
  1417  /*
  1418   *	find the source file line number for a given value of the pc
  1419   */
  1420  int32
  1421  pc2line(uvlong pc)
  1422  {
  1423  	uchar *c, u;
  1424  	uvlong currpc;
  1425  	int32 currline;
  1426  
  1427  	if(pcline == 0)
  1428  		return -1;
  1429  	currline = 0;
  1430  	if (firstinstr != 0)
  1431  		currpc = firstinstr-mach->pcquant;
  1432  	else
  1433  		currpc = txtstart-mach->pcquant;
  1434  	if(pc<currpc || pc>txtend)
  1435  		return -1;
  1436  
  1437  	for(c = pcline; c < pclineend && currpc < pc; c++) {
  1438  		u = *c;
  1439  		if(u == 0) {
  1440  			currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
  1441  			c += 4;
  1442  		}
  1443  		else if(u < 65)
  1444  			currline += u;
  1445  		else if(u < 129)
  1446  			currline -= (u-64);
  1447  		else
  1448  			currpc += mach->pcquant*(u-129);
  1449  		currpc += mach->pcquant;
  1450  	}
  1451  	return currline;
  1452  }
  1453  
  1454  /*
  1455   *	find the pc associated with a line number
  1456   *	basepc and endpc are text addresses bounding the search.
  1457   *	if endpc == 0, the end of the table is used (i.e., no upper bound).
  1458   *	usually, basepc and endpc contain the first text address in
  1459   *	a file and the first text address in the following file, respectively.
  1460   */
  1461  uvlong
  1462  line2addr(int32 line, uvlong basepc, uvlong endpc)
  1463  {
  1464  	uchar *c,  u;
  1465  	uvlong currpc, pc;
  1466  	int32 currline;
  1467  	int32 delta, d;
  1468  	int found;
  1469  
  1470  	if(pcline == 0 || line == 0)
  1471  		return ~(uvlong)0;
  1472  
  1473  	currline = 0;
  1474  	currpc = txtstart-mach->pcquant;
  1475  	pc = ~(uvlong)0;
  1476  	found = 0;
  1477  	delta = HUGEINT;
  1478  
  1479  	for(c = pcline; c < pclineend; c++) {
  1480  		if(endpc && currpc >= endpc)	/* end of file of interest */
  1481  			break;
  1482  		if(currpc >= basepc) {		/* proper file */
  1483  			if(currline >= line) {
  1484  				d = currline-line;
  1485  				found = 1;
  1486  			} else
  1487  				d = line-currline;
  1488  			if(d < delta) {
  1489  				delta = d;
  1490  				pc = currpc;
  1491  			}
  1492  		}
  1493  		u = *c;
  1494  		if(u == 0) {
  1495  			currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
  1496  			c += 4;
  1497  		}
  1498  		else if(u < 65)
  1499  			currline += u;
  1500  		else if(u < 129)
  1501  			currline -= (u-64);
  1502  		else
  1503  			currpc += mach->pcquant*(u-129);
  1504  		currpc += mach->pcquant;
  1505  	}
  1506  	if(found)
  1507  		return pc;
  1508  	return ~(uvlong)0;
  1509  }
  1510  
  1511  /*
  1512   *	Print a history stack (debug). if count is 0, prints the whole stack
  1513   */
  1514  static void
  1515  printhist(char *msg, Hist *hp, int count)
  1516  {
  1517  	int i;
  1518  	uchar *cp;
  1519  	char buf[128];
  1520  
  1521  	i = 0;
  1522  	while(hp->name) {
  1523  		if(count && ++i > count)
  1524  			break;
  1525  		print("%s Line: %x (%d)  Offset: %x (%d)  Name: ", msg,
  1526  			hp->line, hp->line, hp->offset, hp->offset);
  1527  		for(cp = (uchar *)hp->name+1; (*cp<<8)|cp[1]; cp += 2) {
  1528  			if (cp != (uchar *)hp->name+1)
  1529  				print("/");
  1530  			print("%x", (*cp<<8)|cp[1]);
  1531  		}
  1532  		fileelem(fnames, (uchar *) hp->name, buf, sizeof(buf));
  1533  		print(" (%s)\n", buf);
  1534  		hp++;
  1535  	}
  1536  }
  1537  
  1538  #ifdef DEBUG
  1539  /*
  1540   *	print the history stack for a file. (debug only)
  1541   *	if (name == 0) => print all history stacks.
  1542   */
  1543  void
  1544  dumphist(char *name)
  1545  {
  1546  	int i;
  1547  	File *f;
  1548  	short *fname;
  1549  
  1550  	if(buildtbls() == 0)
  1551  		return;
  1552  	if(name)
  1553  		fname = encfname(name);
  1554  	for(i = 0, f = files; i < nfiles; i++, f++)
  1555  		if(fname == 0 || hcomp(f->hist, fname))
  1556  			printhist("> ", f->hist, f->n);
  1557  
  1558  	if(fname)
  1559  		free(fname);
  1560  }
  1561  #endif
  1562  
  1563  // Go 1.2 pcln table
  1564  // See golang.org/s/go12symtab.
  1565  
  1566  // Func layout
  1567  #define FuncEntry (0)
  1568  #define FuncName (pcptrsize)
  1569  #define FuncArgs (pcptrsize+4)
  1570  #define FuncFrame (pcptrsize+2*4)
  1571  #define FuncPCSP (pcptrsize+3*4)
  1572  #define FuncPCFile (pcptrsize+4*4)
  1573  #define FuncPCLine (pcptrsize+5*4)
  1574  
  1575  static int32 pcquantum;
  1576  static int32 pcptrsize;
  1577  static uvlong (*pcswav)(uvlong);
  1578  static uint32 (*pcswal)(uint32);
  1579  static uvlong (*pcuintptr)(uchar*);
  1580  static uchar *functab;
  1581  static uint32 nfunctab;
  1582  static uint32 *filetab;
  1583  static uint32 nfiletab;
  1584  
  1585  static uint32
  1586  xswal(uint32 v)
  1587  {
  1588  	return (v>>24) | ((v>>8)&0xFF00) | ((v<<8)&0xFF0000) | v<<24;
  1589  }
  1590  
  1591  static uvlong
  1592  xswav(uvlong v)
  1593  {
  1594  	return (uvlong)xswal(v)<<32 | xswal(v>>32);
  1595  }
  1596  
  1597  static uvlong
  1598  noswav(uvlong v)
  1599  {
  1600  	return v;
  1601  }
  1602  
  1603  static uint32
  1604  noswal(uint32 v)
  1605  {
  1606  	return v;
  1607  }
  1608  
  1609  static uvlong
  1610  readuintptr64(uchar *p)
  1611  {
  1612  	return pcswav(*(uvlong*)p);
  1613  }
  1614  
  1615  static uvlong
  1616  readuintptr32(uchar *p)
  1617  {
  1618  	return pcswal(*(uint32*)p);
  1619  }
  1620  
  1621  static void
  1622  go12clean(void)
  1623  {
  1624  	pcquantum = 0;
  1625  	pcswav = nil;
  1626  	pcswal = nil;
  1627  	functab = nil;
  1628  	nfunctab = 0;
  1629  	filetab = nil;
  1630  	nfiletab = 0;
  1631  }
  1632  
  1633  static void
  1634  go12init(void)
  1635  {
  1636  	uint32 m;
  1637  	uchar *p;
  1638  
  1639  	if(pcquantum != 0)
  1640  		return;
  1641  	pcquantum = -1; // not go 1.2
  1642  	if(pcline == nil || pclineend - pcline < 16 ||
  1643  		pcline[4] != 0 || pcline[5] != 0 ||
  1644  		(pcline[6] != 1 && pcline[6] != 4) ||
  1645  		(pcline[7] != 4 && pcline[7] != 8))
  1646  		return;
  1647  
  1648  	// header is magic, 00 00 pcquantum ptrsize
  1649  	m = *(uint32*)pcline;
  1650  	if(m == Go12PclnMagic) {
  1651  		pcswav = noswav;
  1652  		pcswal = noswal;
  1653  	} else {
  1654  		pcswav = xswav;
  1655  		pcswal = xswal;
  1656  	}
  1657  	pcptrsize = pcline[7];
  1658  	
  1659  	if(pcptrsize == 4)
  1660  		pcuintptr = readuintptr32;
  1661  	else
  1662  		pcuintptr = readuintptr64;
  1663  
  1664  	nfunctab = pcuintptr(pcline+8);
  1665  	functab = pcline + 8 + pcptrsize;
  1666  	
  1667  	// functab is 2*nfunctab pointer-sized values.
  1668  	// The offset to the file table follows.
  1669  	p = functab + nfunctab*2*pcptrsize + pcptrsize;
  1670  	if(p+4 > pclineend)
  1671  		return;
  1672  	filetab = (uint32*)(pcline + pcswal(*(uint32*)p));
  1673  	if((uchar*)filetab+4 > pclineend)
  1674  		return;
  1675  	
  1676  	// File table begins with count.
  1677  	nfiletab = pcswal(filetab[0]);
  1678  	if((uchar*)(filetab + nfiletab) > pclineend)
  1679  		return;
  1680  
  1681  	// Committed.
  1682  	pcquantum = pcline[6];
  1683  }
  1684  
  1685  static int
  1686  isgo12pcline(void)
  1687  {
  1688  	go12init();
  1689  	return pcquantum > 0;
  1690  }
  1691  
  1692  static uchar*
  1693  go12findfunc(uvlong pc)
  1694  {
  1695  	uchar *f, *fm;
  1696  	int32 nf, m;
  1697  
  1698  	if(pc < pcuintptr(functab) || pc >= pcuintptr(functab+2*nfunctab*pcptrsize))
  1699  		return nil;
  1700  
  1701  	// binary search to find func with entry <= addr.
  1702  	f = functab;
  1703  	nf = nfunctab;
  1704  	while(nf > 0) {
  1705  		m = nf/2;
  1706  		fm = f + 2*pcptrsize*m;
  1707  		if(pcuintptr(fm) <= pc && pc < pcuintptr(fm+2*pcptrsize)) {
  1708  			f = pcline + pcuintptr(fm+pcptrsize);
  1709  			if(f >= pclineend)
  1710  				return nil;
  1711  			return f;
  1712  		} else if(pc < pcuintptr(fm))
  1713  			nf = m;
  1714  		else {
  1715  			f += (m+1)*2*pcptrsize;
  1716  			nf -= m+1;
  1717  		}
  1718  	}
  1719  	return nil;
  1720  }
  1721  
  1722  static uint32
  1723  readvarint(uchar **pp)
  1724  {
  1725  	uchar *p;
  1726  	uint32 v;
  1727  	int32 shift;
  1728  	
  1729  	v = 0;
  1730  	p = *pp;
  1731  	for(shift = 0;; shift += 7) {
  1732  		v |= (*p & 0x7F) << shift;
  1733  		if(!(*p++ & 0x80))
  1734  			break;
  1735  	}
  1736  	*pp = p;
  1737  	return v;
  1738  }
  1739  
  1740  static char*
  1741  pcstring(uint32 off)
  1742  {
  1743  	if(off == 0 || off >= pclineend - pcline ||
  1744  	   memchr(pcline + off, '\0', pclineend - (pcline + off)) == nil)
  1745  		return "?";
  1746  	return (char*)pcline+off;
  1747  }
  1748  
  1749  
  1750  static int
  1751  step(uchar **pp, uvlong *pc, int32 *value, int first)
  1752  {
  1753  	uint32 uvdelta, pcdelta;
  1754  	int32 vdelta;
  1755  
  1756  	uvdelta = readvarint(pp);
  1757  	if(uvdelta == 0 && !first)
  1758  		return 0;
  1759  	if(uvdelta&1)
  1760  		uvdelta = ~(uvdelta>>1);
  1761  	else
  1762  		uvdelta >>= 1;
  1763  	vdelta = (int32)uvdelta;
  1764  	pcdelta = readvarint(pp) * pcquantum;
  1765  	*value += vdelta;
  1766  	*pc += pcdelta;
  1767  	return 1;
  1768  }
  1769  
  1770  static int32
  1771  pcvalue(uint32 off, uvlong entry, uvlong targetpc)
  1772  {
  1773  	uvlong pc;
  1774  	int32 val;
  1775  	uchar *p;
  1776  	
  1777  	val = -1;
  1778  	pc = entry;
  1779  	if(off == 0 || off >= pclineend - pcline)
  1780  		return -1;	
  1781  	p = pcline + off;
  1782  	while(step(&p, &pc, &val, pc == entry)) {
  1783  		if(targetpc < pc)
  1784  			return val;
  1785  	}
  1786  	return -1;
  1787  }
  1788  
  1789  static uvlong
  1790  go12pc2sp(uvlong pc)
  1791  {
  1792  	uchar *f;
  1793  	uint32 off;
  1794  	uvlong entry;
  1795  	int32 sp;
  1796  
  1797  	f = go12findfunc(pc);
  1798  	if(f == nil)
  1799  		return ~(uvlong)0;
  1800  	entry = pcuintptr(f+FuncEntry);
  1801  	off = pcswal(*(uint32*)(f+FuncPCSP));
  1802  	sp = pcvalue(off, entry, pc);
  1803  	if(sp < 0)
  1804  		return ~(uvlong)0;
  1805  	return sp;
  1806  }
  1807  
  1808  static int32
  1809  go12fileline(char *str, int n, uvlong pc)
  1810  {
  1811  	uchar *f;
  1812  	uint32 fileoff, lineoff;
  1813  	uvlong entry;
  1814  	int lno, fno;
  1815  
  1816  	f = go12findfunc(pc);
  1817  	if(f == nil)
  1818  		return 0;
  1819  	entry = pcuintptr(f+FuncEntry);
  1820  	fileoff = pcswal(*(uint32*)(f+FuncPCFile));
  1821  	lineoff = pcswal(*(uint32*)(f+FuncPCLine));
  1822  	lno = pcvalue(lineoff, entry, pc);
  1823  	fno = pcvalue(fileoff, entry, pc);
  1824  	if(lno < 0 || fno <= 0 || fno >= nfiletab) {
  1825  		return 0;
  1826  	}
  1827  	snprint(str, n, "%s:%d", pcstring(pcswal(filetab[fno])), lno);
  1828  	return 1;
  1829  }
  1830  
  1831  static uvlong
  1832  go12file2pc(char *file, int line)
  1833  {
  1834  	int fno;
  1835  	int32 i, fval, lval;
  1836  	uchar *func, *fp, *lp;
  1837  	uvlong fpc, lpc, fstartpc, lstartpc, entry;
  1838  
  1839  	// Map file to file number.
  1840  	// NOTE(rsc): Could introduce a hash table for repeated
  1841  	// lookups if anyone ever calls this.
  1842  	for(fno=1; fno<nfiletab; fno++)
  1843  		if(strcmp(pcstring(pcswal(filetab[fno])), file) == 0)
  1844  			goto havefile;
  1845  	werrstr("cannot find file");
  1846  	return ~(uvlong)0;
  1847  
  1848  havefile:
  1849  	// Consider each func.
  1850  	// Run file number program to find file match,
  1851  	// then run line number program to find line match.
  1852  	// Most file number programs are tiny, and most will
  1853  	// not mention the file number, so this should be fairly
  1854  	// quick.
  1855  	for(i=0; i<nfunctab; i++) {
  1856  		func = pcline + pcuintptr(functab+i*2*pcptrsize+pcptrsize);
  1857  		entry = pcuintptr(func+FuncEntry);
  1858  		fp = pcline + pcswal(*(uint32*)(func+FuncPCFile));
  1859  		lp = pcline + pcswal(*(uint32*)(func+FuncPCLine));
  1860  		fval = lval = -1;
  1861  		fpc = lpc = entry;
  1862  		fstartpc = fpc;
  1863  		while(step(&fp, &fpc, &fval, fpc==entry)) {
  1864  			if(fval == fno && fstartpc < fpc) {
  1865  				lstartpc = lpc;
  1866  				while(lpc < fpc && step(&lp, &lpc, &lval, lpc==entry)) {
  1867  					if(lval == line) {
  1868  						if(fstartpc <= lstartpc) {
  1869  							return lstartpc;
  1870  						}
  1871  						if(fstartpc < lpc) {
  1872  							return fstartpc;
  1873  						}
  1874  					}
  1875  					lstartpc = lpc;
  1876  				}
  1877  			}
  1878  			fstartpc = fpc;
  1879  		}
  1880  	}
  1881  	werrstr("cannot find line in file");
  1882  	return ~(uvlong)0;
  1883  }