root/src/cmd/ld/pcln.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. addvarint
  2. addpctab
  3. ftabaddstring
  4. renumberfiles
  5. pclntab

// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include        "l.h"
#include        "lib.h"
#include        "../../pkg/runtime/funcdata.h"

static void
addvarint(Pcdata *d, uint32 val)
{
        int32 n;
        uint32 v;
        uchar *p;

        n = 0;
        for(v = val; v >= 0x80; v >>= 7)
                n++;
        n++;

        if(d->n + n > d->m) {
                d->m = (d->n + n)*2;
                d->p = erealloc(d->p, d->m);
        }

        p = d->p + d->n;
        for(v = val; v >= 0x80; v >>= 7)
                *p++ = v | 0x80;
        *p = v;
        d->n += n;
}

static int32
addpctab(LSym *ftab, int32 off, Pcdata *d)
{
        int32 start;
        
        start = ftab->np;
        symgrow(ctxt, ftab, start + d->n);
        memmove(ftab->p + start, d->p, d->n);
        
        return setuint32(ctxt, ftab, off, start);
}

static int32
ftabaddstring(LSym *ftab, char *s)
{
        int32 n, start;
        
        n = strlen(s)+1;
        start = ftab->np;
        symgrow(ctxt, ftab, start+n+1);
        strcpy((char*)ftab->p + start, s);
        return start;
}

static void
renumberfiles(Link *ctxt, LSym **files, int nfiles, Pcdata *d)
{
        int i;
        LSym *f;
        Pcdata out;
        Pciter it;
        uint32 v;
        int32 oldval, newval, val, dv;
        
        // Give files numbers.
        for(i=0; i<nfiles; i++) {
                f = files[i];
                if(f->type != SFILEPATH) {
                        f->value = ++ctxt->nhistfile;
                        f->type = SFILEPATH;
                        f->next = ctxt->filesyms;
                        ctxt->filesyms = f;
                }
        }

        newval = -1;
        memset(&out, 0, sizeof out);

        for(pciterinit(ctxt, &it, d); !it.done; pciternext(&it)) {
                // value delta
                oldval = it.value;
                if(oldval == -1)
                        val = -1;
                else {  
                        if(oldval < 0 || oldval >= nfiles)
                                sysfatal("bad pcdata %d", oldval);
                        val = files[oldval]->value;
                }
                dv = val - newval;
                newval = val;
                v = (uint32)(dv<<1) ^ (uint32)(int32)(dv>>31);
                addvarint(&out, v);

                // pc delta
                addvarint(&out, (it.nextpc - it.pc) / it.pcscale);
        }
        
        // terminating value delta
        addvarint(&out, 0);

        free(d->p);
        *d = out;       
}


// pclntab initializes the pclntab symbol with
// runtime function and file name information.
void
pclntab(void)
{
        int32 i, nfunc, start, funcstart;
        LSym *ftab, *s;
        int32 off, end, frameptrsize;
        int64 funcdata_bytes;
        Pcln *pcln;
        Pciter it;
        static Pcln zpcln;
        
        funcdata_bytes = 0;
        ftab = linklookup(ctxt, "pclntab", 0);
        ftab->type = SPCLNTAB;
        ftab->reachable = 1;

        // See golang.org/s/go12symtab for the format. Briefly:
        //      8-byte header
        //      nfunc [PtrSize bytes]
        //      function table, alternating PC and offset to func struct [each entry PtrSize bytes]
        //      end PC [PtrSize bytes]
        //      offset to file table [4 bytes]
        nfunc = 0;
        for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next)
                nfunc++;
        symgrow(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize+4);
        setuint32(ctxt, ftab, 0, 0xfffffffb);
        setuint8(ctxt, ftab, 6, MINLC);
        setuint8(ctxt, ftab, 7, PtrSize);
        setuintxx(ctxt, ftab, 8, nfunc, PtrSize);

        nfunc = 0;
        for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next, nfunc++) {
                pcln = ctxt->cursym->pcln;
                if(pcln == nil)
                        pcln = &zpcln;
        
                funcstart = ftab->np;
                funcstart += -ftab->np & (PtrSize-1);

                setaddr(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize, ctxt->cursym);
                setuintxx(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, funcstart, PtrSize);

                // fixed size of struct, checked below
                off = funcstart;
                end = funcstart + PtrSize + 3*4 + 5*4 + pcln->npcdata*4 + pcln->nfuncdata*PtrSize;
                if(pcln->nfuncdata > 0 && (end&(PtrSize-1)))
                        end += 4;
                symgrow(ctxt, ftab, end);

                // entry uintptr
                off = setaddr(ctxt, ftab, off, ctxt->cursym);

                // name int32
                off = setuint32(ctxt, ftab, off, ftabaddstring(ftab, ctxt->cursym->name));
                
                // args int32
                // TODO: Move into funcinfo.
                off = setuint32(ctxt, ftab, off, ctxt->cursym->args);
        
                // frame int32
                // TODO: Remove entirely. The pcsp table is more precise.
                // This is only used by a fallback case during stack walking
                // when a called function doesn't have argument information.
                // We need to make sure everything has argument information
                // and then remove this.
                frameptrsize = PtrSize;
                if(ctxt->cursym->leaf)
                        frameptrsize = 0;
                off = setuint32(ctxt, ftab, off, ctxt->cursym->locals + frameptrsize);
                
                if(pcln != &zpcln) {
                        renumberfiles(ctxt, pcln->file, pcln->nfile, &pcln->pcfile);
                        if(0) {
                                // Sanity check the new numbering
                                for(pciterinit(ctxt, &it, &pcln->pcfile); !it.done; pciternext(&it)) {
                                        if(it.value < 1 || it.value > ctxt->nhistfile) {
                                                diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, ctxt->nhistfile);
                                                errorexit();
                                        }
                                }
                        }
                }

                // pcdata
                off = addpctab(ftab, off, &pcln->pcsp);
                off = addpctab(ftab, off, &pcln->pcfile);
                off = addpctab(ftab, off, &pcln->pcline);
                off = setuint32(ctxt, ftab, off, pcln->npcdata);
                off = setuint32(ctxt, ftab, off, pcln->nfuncdata);
                for(i=0; i<pcln->npcdata; i++)
                        off = addpctab(ftab, off, &pcln->pcdata[i]);

                // funcdata, must be pointer-aligned and we're only int32-aligned.
                // Missing funcdata will be 0 (nil pointer).
                if(pcln->nfuncdata > 0) {
                        if(off&(PtrSize-1))
                                off += 4;
                        for(i=0; i<pcln->nfuncdata; i++) {
                                if(pcln->funcdata[i] == nil)
                                        setuintxx(ctxt, ftab, off+PtrSize*i, pcln->funcdataoff[i], PtrSize);
                                else {
                                        // TODO: Dedup.
                                        funcdata_bytes += pcln->funcdata[i]->size;
                                        setaddrplus(ctxt, ftab, off+PtrSize*i, pcln->funcdata[i], pcln->funcdataoff[i]);
                                }
                        }
                        off += pcln->nfuncdata*PtrSize;
                }

                if(off != end) {
                        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);
                        errorexit();
                }
        
                // Final entry of table is just end pc.
                if(ctxt->cursym->next == nil)
                        setaddrplus(ctxt, ftab, 8+PtrSize+(nfunc+1)*2*PtrSize, ctxt->cursym, ctxt->cursym->size);
        }
        
        // Start file table.
        start = ftab->np;
        start += -ftab->np & (PtrSize-1);
        setuint32(ctxt, ftab, 8+PtrSize+nfunc*2*PtrSize+PtrSize, start);

        symgrow(ctxt, ftab, start+(ctxt->nhistfile+1)*4);
        setuint32(ctxt, ftab, start, ctxt->nhistfile);
        for(s = ctxt->filesyms; s != S; s = s->next)
                setuint32(ctxt, ftab, start + s->value*4, ftabaddstring(ftab, s->name));

        ftab->size = ftab->np;
        
        if(debug['v'])
                Bprint(&bso, "%5.2f pclntab=%lld bytes, funcdata total %lld bytes\n", cputime(), (vlong)ftab->size, (vlong)funcdata_bytes);
}       

/* [<][>][^][v][top][bottom][index][help] */