| // 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 { |
| var p []byte |
| var shift int |
| var v uint32 |
| |
| v = 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) { |
| var v uint32 |
| var dv int32 |
| |
| 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) { |
| var n int32 |
| var v uint32 |
| var p []byte |
| |
| n = 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:] |
| 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 |
| |
| 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 { |
| var n int32 |
| var start 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 i int |
| var f *LSym |
| var out Pcdata |
| var it Pciter |
| var v uint32 |
| var oldval int32 |
| var newval int32 |
| var val int32 |
| var dv int32 |
| |
| // Give files numbers. |
| for i = 0; i < len(files); i++ { |
| f = files[i] |
| if f.Type != SFILEPATH { |
| ctxt.Nhistfile++ |
| f.Value = int64(ctxt.Nhistfile) |
| f.Type = SFILEPATH |
| f.Next = ctxt.Filesyms |
| ctxt.Filesyms = f |
| } |
| } |
| |
| newval = -1 |
| out = Pcdata{} |
| |
| 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.Sub != nil { |
| return 1 |
| } |
| return 0 |
| } |
| |
| // pclntab initializes the pclntab symbol with |
| // runtime function and file name information. |
| |
| var pclntab_zpcln Pcln |
| |
| func pclntab() { |
| var i int32 |
| var nfunc int32 |
| var start int32 |
| var funcstart int32 |
| var ftab *LSym |
| var s *LSym |
| var last *LSym |
| var off int32 |
| var end int32 |
| var frameptrsize int32 |
| var funcdata_bytes int64 |
| var pcln *Pcln |
| var it Pciter |
| |
| funcdata_bytes = 0 |
| ftab = Linklookup(Ctxt, "runtime.pclntab", 0) |
| ftab.Type = 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 = 0 |
| |
| for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { |
| if container(Ctxt.Cursym) == 0 { |
| 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)) |
| |
| nfunc = 0 |
| last = nil |
| 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 |
| } |
| |
| 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 |
| // 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 = int32(Thearch.Ptrsize) |
| |
| if Ctxt.Cursym.Leaf != 0 { |
| frameptrsize = 0 |
| } |
| off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Locals)+uint32(frameptrsize))) |
| |
| 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++ |
| } |
| |
| // 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) |
| 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() { |
| var t *LSym |
| var s *LSym |
| var e *LSym |
| var idx int32 |
| var i int32 |
| var j int32 |
| var nbuckets int32 |
| var n int32 |
| var base int32 |
| var min int64 |
| var max int64 |
| var p int64 |
| var q int64 |
| var indexes []int32 |
| |
| t = Linklookup(Ctxt, "runtime.findfunctab", 0) |
| t.Type = SRODATA |
| t.Reachable = true |
| |
| // find min and max address |
| min = Ctxt.Textp.Value |
| |
| max = 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 = 0; i < n; i++ { |
| indexes[i] = NOIDX |
| } |
| idx = 0 |
| 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 |
| for i = 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)) |
| } |
| } |
| } |