github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/src/cmd/ld/pcln.c (about)

     1  // Copyright 2013 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  #include	"l.h"
     6  #include	"lib.h"
     7  #include	"../../runtime/funcdata.h"
     8  
     9  static void
    10  addvarint(Pcdata *d, uint32 val)
    11  {
    12  	int32 n;
    13  	uint32 v;
    14  	uchar *p;
    15  
    16  	n = 0;
    17  	for(v = val; v >= 0x80; v >>= 7)
    18  		n++;
    19  	n++;
    20  
    21  	if(d->n + n > d->m) {
    22  		d->m = (d->n + n)*2;
    23  		d->p = erealloc(d->p, d->m);
    24  	}
    25  
    26  	p = d->p + d->n;
    27  	for(v = val; v >= 0x80; v >>= 7)
    28  		*p++ = v | 0x80;
    29  	*p = v;
    30  	d->n += n;
    31  }
    32  
    33  static int32
    34  addpctab(LSym *ftab, int32 off, Pcdata *d)
    35  {
    36  	int32 start;
    37  	
    38  	start = ftab->np;
    39  	symgrow(ctxt, ftab, start + d->n);
    40  	memmove(ftab->p + start, d->p, d->n);
    41  	
    42  	return setuint32(ctxt, ftab, off, start);
    43  }
    44  
    45  static int32
    46  ftabaddstring(LSym *ftab, char *s)
    47  {
    48  	int32 n, start;
    49  	
    50  	n = strlen(s)+1;
    51  	start = ftab->np;
    52  	symgrow(ctxt, ftab, start+n+1);
    53  	strcpy((char*)ftab->p + start, s);
    54  	return start;
    55  }
    56  
    57  static void
    58  renumberfiles(Link *ctxt, LSym **files, int nfiles, Pcdata *d)
    59  {
    60  	int i;
    61  	LSym *f;
    62  	Pcdata out;
    63  	Pciter it;
    64  	uint32 v;
    65  	int32 oldval, newval, val, dv;
    66  	
    67  	// Give files numbers.
    68  	for(i=0; i<nfiles; i++) {
    69  		f = files[i];
    70  		if(f->type != SFILEPATH) {
    71  			f->value = ++ctxt->nhistfile;
    72  			f->type = SFILEPATH;
    73  			f->next = ctxt->filesyms;
    74  			ctxt->filesyms = f;
    75  		}
    76  	}
    77  
    78  	newval = -1;
    79  	memset(&out, 0, sizeof out);
    80  
    81  	for(pciterinit(ctxt, &it, d); !it.done; pciternext(&it)) {
    82  		// value delta
    83  		oldval = it.value;
    84  		if(oldval == -1)
    85  			val = -1;
    86  		else {	
    87  			if(oldval < 0 || oldval >= nfiles)
    88  				sysfatal("bad pcdata %d", oldval);
    89  			val = files[oldval]->value;
    90  		}
    91  		dv = val - newval;
    92  		newval = val;
    93  		v = ((uint32)dv<<1) ^ (uint32)(int32)(dv>>31);
    94  		addvarint(&out, v);
    95  
    96  		// pc delta
    97  		addvarint(&out, (it.nextpc - it.pc) / it.pcscale);
    98  	}
    99  	
   100  	// terminating value delta
   101  	addvarint(&out, 0);
   102  
   103  	free(d->p);
   104  	*d = out;	
   105  }
   106  
   107  
   108  // pclntab initializes the pclntab symbol with
   109  // runtime function and file name information.
   110  void
   111  pclntab(void)
   112  {
   113  	int32 i, nfunc, start, funcstart;
   114  	LSym *ftab, *s;
   115  	int32 off, end, frameptrsize;
   116  	int64 funcdata_bytes;
   117  	Pcln *pcln;
   118  	Pciter it;
   119  	static Pcln zpcln;
   120  	
   121  	funcdata_bytes = 0;
   122  	ftab = linklookup(ctxt, "runtime.pclntab", 0);
   123  	ftab->type = SPCLNTAB;
   124  	ftab->reachable = 1;
   125  
   126  	// See golang.org/s/go12symtab for the format. Briefly:
   127  	//	8-byte header
   128  	//	nfunc [PtrSize bytes]
   129  	//	function table, alternating PC and offset to func struct [each entry PtrSize bytes]
   130  	//	end PC [PtrSize bytes]
   131  	//	offset to file table [4 bytes]
   132  	nfunc = 0;
   133  	for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next)
   134  		nfunc++;
   135  	symgrow(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize+4);
   136  	setuint32(ctxt, ftab, 0, 0xfffffffb);
   137  	setuint8(ctxt, ftab, 6, MINLC);
   138  	setuint8(ctxt, ftab, 7, PtrSize);
   139  	setuintxx(ctxt, ftab, 8, nfunc, PtrSize);
   140  
   141  	nfunc = 0;
   142  	for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next, nfunc++) {
   143  		pcln = ctxt->cursym->pcln;
   144  		if(pcln == nil)
   145  			pcln = &zpcln;
   146  	
   147  		funcstart = ftab->np;
   148  		funcstart += -ftab->np & (PtrSize-1);
   149  
   150  		setaddr(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize, ctxt->cursym);
   151  		setuintxx(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, funcstart, PtrSize);
   152  
   153  		// fixed size of struct, checked below
   154  		off = funcstart;
   155  		end = funcstart + PtrSize + 3*4 + 5*4 + pcln->npcdata*4 + pcln->nfuncdata*PtrSize;
   156  		if(pcln->nfuncdata > 0 && (end&(PtrSize-1)))
   157  			end += 4;
   158  		symgrow(ctxt, ftab, end);
   159  
   160  		// entry uintptr
   161  		off = setaddr(ctxt, ftab, off, ctxt->cursym);
   162  
   163  		// name int32
   164  		off = setuint32(ctxt, ftab, off, ftabaddstring(ftab, ctxt->cursym->name));
   165  		
   166  		// args int32
   167  		// TODO: Move into funcinfo.
   168  		off = setuint32(ctxt, ftab, off, ctxt->cursym->args);
   169  	
   170  		// frame int32
   171  		// TODO: Remove entirely. The pcsp table is more precise.
   172  		// This is only used by a fallback case during stack walking
   173  		// when a called function doesn't have argument information.
   174  		// We need to make sure everything has argument information
   175  		// and then remove this.
   176  		frameptrsize = PtrSize;
   177  		if(ctxt->cursym->leaf)
   178  			frameptrsize = 0;
   179  		off = setuint32(ctxt, ftab, off, ctxt->cursym->locals + frameptrsize);
   180  		
   181  		if(pcln != &zpcln) {
   182  			renumberfiles(ctxt, pcln->file, pcln->nfile, &pcln->pcfile);
   183  			if(0) {
   184  				// Sanity check the new numbering
   185  				for(pciterinit(ctxt, &it, &pcln->pcfile); !it.done; pciternext(&it)) {
   186  					if(it.value < 1 || it.value > ctxt->nhistfile) {
   187  						diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, ctxt->nhistfile);
   188  						errorexit();
   189  					}
   190  				}
   191  			}
   192  		}
   193  
   194  		// pcdata
   195  		off = addpctab(ftab, off, &pcln->pcsp);
   196  		off = addpctab(ftab, off, &pcln->pcfile);
   197  		off = addpctab(ftab, off, &pcln->pcline);
   198  		off = setuint32(ctxt, ftab, off, pcln->npcdata);
   199  		off = setuint32(ctxt, ftab, off, pcln->nfuncdata);
   200  		for(i=0; i<pcln->npcdata; i++)
   201  			off = addpctab(ftab, off, &pcln->pcdata[i]);
   202  
   203  		// funcdata, must be pointer-aligned and we're only int32-aligned.
   204  		// Missing funcdata will be 0 (nil pointer).
   205  		if(pcln->nfuncdata > 0) {
   206  			if(off&(PtrSize-1))
   207  				off += 4;
   208  			for(i=0; i<pcln->nfuncdata; i++) {
   209  				if(pcln->funcdata[i] == nil)
   210  					setuintxx(ctxt, ftab, off+PtrSize*i, pcln->funcdataoff[i], PtrSize);
   211  				else {
   212  					// TODO: Dedup.
   213  					funcdata_bytes += pcln->funcdata[i]->size;
   214  					setaddrplus(ctxt, ftab, off+PtrSize*i, pcln->funcdata[i], pcln->funcdataoff[i]);
   215  				}
   216  			}
   217  			off += pcln->nfuncdata*PtrSize;
   218  		}
   219  
   220  		if(off != end) {
   221  			diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln->npcdata, pcln->nfuncdata, PtrSize);
   222  			errorexit();
   223  		}
   224  	
   225  		// Final entry of table is just end pc.
   226  		if(ctxt->cursym->next == nil)
   227  			setaddrplus(ctxt, ftab, 8+PtrSize+(nfunc+1)*2*PtrSize, ctxt->cursym, ctxt->cursym->size);
   228  	}
   229  	
   230  	// Start file table.
   231  	start = ftab->np;
   232  	start += -ftab->np & (PtrSize-1);
   233  	setuint32(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, start);
   234  
   235  	symgrow(ctxt, ftab, start+(ctxt->nhistfile+1)*4);
   236  	setuint32(ctxt, ftab, start, ctxt->nhistfile);
   237  	for(s = ctxt->filesyms; s != S; s = s->next)
   238  		setuint32(ctxt, ftab, start + s->value*4, ftabaddstring(ftab, s->name));
   239  
   240  	ftab->size = ftab->np;
   241  	
   242  	if(debug['v'])
   243  		Bprint(&bso, "%5.2f pclntab=%lld bytes, funcdata total %lld bytes\n", cputime(), (vlong)ftab->size, (vlong)funcdata_bytes);
   244  }	
   245  
   246  enum {
   247  	BUCKETSIZE = 256*MINFUNC,
   248  	SUBBUCKETS = 16,
   249  };
   250  
   251  // findfunctab generates a lookup table to quickly find the containing
   252  // function for a pc.  See src/runtime/symtab.go:findfunc for details.
   253  void
   254  findfunctab(void)
   255  {
   256  	LSym *t, *s;
   257  	int32 idx, bidx, i, j, nbuckets;
   258  	vlong min, max;
   259  
   260  	t = linklookup(ctxt, "runtime.findfunctab", 0);
   261  	t->type = SRODATA;
   262  	t->reachable = 1;
   263  
   264  	// find min and max address
   265  	min = ctxt->textp->value;
   266  	max = 0;
   267  	for(s = ctxt->textp; s != nil; s = s->next)
   268  		max = s->value + s->size;
   269  
   270  	// allocate table
   271  	nbuckets = (max-min+BUCKETSIZE-1)/BUCKETSIZE;
   272  	symgrow(ctxt, t, nbuckets * (4+SUBBUCKETS));
   273  
   274  	// fill in table
   275  	s = ctxt->textp;
   276  	idx = 0;
   277  	for(i = 0; i < nbuckets; i++) {
   278  		// Find first function which overlaps this bucket.
   279  		// Only do leaf symbols; skip symbols which are just containers (sub != nil but outer == nil).
   280  		while(s != nil && (s->value+s->size <= min + i * BUCKETSIZE || s->sub != nil && s->outer == nil)) {
   281  			s = s->next;
   282  			idx++;
   283  		}
   284  		// record this function in bucket header
   285  		setuint32(ctxt, t, i*(4+SUBBUCKETS), idx);
   286  		bidx = idx;
   287  
   288  		// compute SUBBUCKETS deltas
   289  		for(j = 0; j < SUBBUCKETS; j++) {
   290  			while(s != nil && (s->value+s->size <= min + i * BUCKETSIZE + j * (BUCKETSIZE/SUBBUCKETS) || s->sub != nil && s->outer == nil)) {
   291  				s = s->next;
   292  				idx++;
   293  			}
   294  			if(idx - bidx >= 256)
   295  				diag("too many functions in a findfunc bucket! %d %s", idx-bidx, s->name);
   296  			setuint8(ctxt, t, i*(4+SUBBUCKETS)+4+j, idx-bidx);
   297  		}
   298  	}
   299  }