// 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. package ld import ( "cmd/internal/obj" "fmt" "log" ) // funcpctab writes to dst a pc-value table mapping the code in func to the values // returned by valfunc parameterized by arg. The invocation of valfunc to update the // current value is, for each p, // // val = valfunc(func, val, p, 0, arg); // record val as value at p->pc; // val = valfunc(func, val, p, 1, arg); // // where func is the function, val is the current value, p is the instruction being // considered, and arg can be used to further parameterize valfunc. // pctofileline computes either the file number (arg == 0) // or the line number (arg == 1) to use at p. // Because p->lineno applies to p, phase == 0 (before p) // takes care of the update. // pctospadj computes the sp adjustment in effect. // It is oldval plus any adjustment made by p itself. // The adjustment by p takes effect only after p, so we // apply the change during phase == 1. // pctopcdata computes the pcdata value in effect at p. // A PCDATA instruction sets the value in effect at future // non-PCDATA instructions. // Since PCDATA instructions have no width in the final code, // it does not matter which phase we use for the update. // iteration over encoded pcdata tables. func getvarint(pp *[]byte) uint32 { v := uint32(0) p := *pp for shift := 0; ; shift += 7 { v |= uint32(p[0]&0x7F) << uint(shift) tmp4 := p p = p[1:] if tmp4[0]&0x80 == 0 { break } } *pp = p return v } func pciternext(it *Pciter) { it.pc = it.nextpc if it.done != 0 { return } if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) { it.done = 1 return } // value delta v := getvarint(&it.p) if v == 0 && it.start == 0 { it.done = 1 return } it.start = 0 dv := int32(v>>1) ^ (int32(v<<31) >> 31) it.value += dv // pc delta v = getvarint(&it.p) it.nextpc = it.pc + v*it.pcscale } func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) { it.d = *d it.p = it.d.P it.pc = 0 it.nextpc = 0 it.value = -1 it.start = 1 it.done = 0 it.pcscale = uint32(ctxt.Arch.Minlc) pciternext(it) } // 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. func addvarint(d *Pcdata, val uint32) { n := int32(0) for v := val; v >= 0x80; v >>= 7 { n++ } n++ old := len(d.P) for cap(d.P) < len(d.P)+int(n) { d.P = append(d.P[:cap(d.P)], 0) } d.P = d.P[:old+int(n)] p := d.P[old:] var v uint32 for v = val; v >= 0x80; v >>= 7 { p[0] = byte(v | 0x80) p = p[1:] } p[0] = byte(v) } func addpctab(ftab *LSym, off int32, d *Pcdata) int32 { var start int32 if len(d.P) > 0 { start = int32(len(ftab.P)) Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P))) copy(ftab.P[start:], d.P) } return int32(setuint32(Ctxt, ftab, int64(off), uint32(start))) } func ftabaddstring(ftab *LSym, s string) int32 { n := int32(len(s)) + 1 start := int32(len(ftab.P)) Symgrow(Ctxt, ftab, int64(start)+int64(n)+1) copy(ftab.P[start:], s) return start } func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) { var f *LSym // Give files numbers. for i := 0; i < len(files); i++ { f = files[i] if f.Type != obj.SFILEPATH { ctxt.Nhistfile++ f.Value = int64(ctxt.Nhistfile) f.Type = obj.SFILEPATH f.Next = ctxt.Filesyms ctxt.Filesyms = f } } newval := int32(-1) var out Pcdata var dv int32 var it Pciter var oldval int32 var v uint32 var val int32 for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) { // value delta oldval = it.value if oldval == -1 { val = -1 } else { if oldval < 0 || oldval >= int32(len(files)) { log.Fatalf("bad pcdata %d", oldval) } val = int32(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) *d = out } func container(s *LSym) int { // We want to generate func table entries only for the "lowest level" symbols, // not containers of subsymbols. if s != nil && s.Type&obj.SCONTAINER != 0 { return 1 } return 0 } // pclntab initializes the pclntab symbol with // runtime function and file name information. var pclntab_zpcln Pcln // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab. var pclntabNfunc int32 var pclntabFiletabOffset int32 var pclntabPclntabOffset int32 var pclntabFirstFunc *LSym var pclntabLastFunc *LSym func pclntab() { funcdata_bytes := int64(0) ftab := Linklookup(Ctxt, "runtime.pclntab", 0) ftab.Type = obj.SPCLNTAB ftab.Reachable = true // See golang.org/s/go12symtab for the format. Briefly: // 8-byte header // nfunc [thearch.ptrsize bytes] // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] // end PC [thearch.ptrsize bytes] // offset to file table [4 bytes] nfunc := int32(0) // Find container symbols, mark them with SCONTAINER for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { if Ctxt.Cursym.Outer != nil { Ctxt.Cursym.Outer.Type |= obj.SCONTAINER } } for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { if container(Ctxt.Cursym) == 0 { nfunc++ } } pclntabNfunc = nfunc Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4) setuint32(Ctxt, ftab, 0, 0xfffffffb) setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc)) setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize)) setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize)) pclntabPclntabOffset = int32(8 + Thearch.Ptrsize) nfunc = 0 var last *LSym var end int32 var funcstart int32 var i int32 var it Pciter var off int32 var pcln *Pcln for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { last = Ctxt.Cursym if container(Ctxt.Cursym) != 0 { continue } pcln = Ctxt.Cursym.Pcln if pcln == nil { pcln = &pclntab_zpcln } if pclntabFirstFunc == nil { pclntabFirstFunc = Ctxt.Cursym } funcstart = int32(len(ftab.P)) funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym) setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize)) // fixed size of struct, checked below off = funcstart end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize) if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) { end += 4 } Symgrow(Ctxt, ftab, int64(end)) // entry uintptr off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym)) // name int32 off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name)))) // args int32 // TODO: Move into funcinfo. off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args))) // frame int32 // This has been removed (it was never set quite correctly anyway). // Nothing should use it. // Leave an obviously incorrect value. // TODO: Remove entirely. off = int32(setuint32(Ctxt, ftab, int64(off), 0x1234567)) if pcln != &pclntab_zpcln { renumberfiles(Ctxt, pcln.File, &pcln.Pcfile) if false { // Sanity check the new numbering for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; 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 = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata))) off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata))) for i = 0; i < int32(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&int32(Thearch.Ptrsize-1) != 0 { off += 4 } for i = 0; i < int32(pcln.Nfuncdata); i++ { if pcln.Funcdata[i] == nil { setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize)) } else { // TODO: Dedup. funcdata_bytes += pcln.Funcdata[i].Size setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i]) } } off += int32(pcln.Nfuncdata) * int32(Thearch.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, Thearch.Ptrsize) errorexit() } nfunc++ } pclntabLastFunc = last // Final entry of table is just end pc. setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size) // Start file table. start := int32(len(ftab.P)) start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1) pclntabFiletabOffset = start setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start)) Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4) setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile)) for s := Ctxt.Filesyms; s != nil; s = s.Next { setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name))) } ftab.Size = int64(len(ftab.P)) if Debug['v'] != 0 { fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes)) } } const ( BUCKETSIZE = 256 * MINFUNC SUBBUCKETS = 16 SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS NOIDX = 0x7fffffff ) // findfunctab generates a lookup table to quickly find the containing // function for a pc. See src/runtime/symtab.go:findfunc for details. func findfunctab() { t := Linklookup(Ctxt, "runtime.findfunctab", 0) t.Type = obj.SRODATA t.Reachable = true t.Local = true // find min and max address min := Ctxt.Textp.Value max := int64(0) for s := Ctxt.Textp; s != nil; s = s.Next { max = s.Value + s.Size } // for each subbucket, compute the minimum of all symbol indexes // that map to that subbucket. n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) indexes := make([]int32, n) for i := int32(0); i < n; i++ { indexes[i] = NOIDX } idx := int32(0) var e *LSym var i int32 var p int64 var q int64 for s := Ctxt.Textp; s != nil; s = s.Next { if container(s) != 0 { continue } p = s.Value e = s.Next for container(e) != 0 { e = e.Next } if e != nil { q = e.Value } else { q = max } //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); for ; p < q; p += SUBBUCKETSIZE { i = int32((p - min) / SUBBUCKETSIZE) if indexes[i] > idx { indexes[i] = idx } } i = int32((q - 1 - min) / SUBBUCKETSIZE) if indexes[i] > idx { indexes[i] = idx } idx++ } // allocate table nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n)) // fill in table var base int32 var j int32 for i := int32(0); i < nbuckets; i++ { base = indexes[i*SUBBUCKETS] if base == NOIDX { Diag("hole in findfunctab") } setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base)) for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { idx = indexes[i*SUBBUCKETS+j] if idx == NOIDX { Diag("hole in findfunctab") } if idx-base >= 256 { Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) } setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) } } }