diff --git a/src/cmd/internal/goobj/funcinfo.go b/src/cmd/internal/goobj/funcinfo.go index e0e6068b4b2..2cca8f6c4ef 100644 --- a/src/cmd/internal/goobj/funcinfo.go +++ b/src/cmd/internal/goobj/funcinfo.go @@ -23,12 +23,11 @@ type FuncInfo struct { Locals uint32 FuncID objabi.FuncID - Pcsp uint32 - Pcfile uint32 - Pcline uint32 - Pcinline uint32 - Pcdata []uint32 - PcdataEnd uint32 + Pcsp SymRef + Pcfile SymRef + Pcline SymRef + Pcinline SymRef + Pcdata []SymRef Funcdataoff []uint32 File []CUFileIndex @@ -41,20 +40,24 @@ func (a *FuncInfo) Write(w *bytes.Buffer) { binary.LittleEndian.PutUint32(b[:], x) w.Write(b[:]) } + writeSymRef := func(s SymRef) { + writeUint32(s.PkgIdx) + writeUint32(s.SymIdx) + } writeUint32(a.Args) writeUint32(a.Locals) writeUint32(uint32(a.FuncID)) - writeUint32(a.Pcsp) - writeUint32(a.Pcfile) - writeUint32(a.Pcline) - writeUint32(a.Pcinline) + writeSymRef(a.Pcsp) + writeSymRef(a.Pcfile) + writeSymRef(a.Pcline) + writeSymRef(a.Pcinline) writeUint32(uint32(len(a.Pcdata))) - for _, x := range a.Pcdata { - writeUint32(x) + for _, sym := range a.Pcdata { + writeSymRef(sym) } - writeUint32(a.PcdataEnd) + writeUint32(uint32(len(a.Funcdataoff))) for _, x := range a.Funcdataoff { writeUint32(x) @@ -75,21 +78,23 @@ func (a *FuncInfo) Read(b []byte) { b = b[4:] return x } + readSymIdx := func() SymRef { + return SymRef{readUint32(), readUint32()} + } a.Args = readUint32() a.Locals = readUint32() a.FuncID = objabi.FuncID(readUint32()) - a.Pcsp = readUint32() - a.Pcfile = readUint32() - a.Pcline = readUint32() - a.Pcinline = readUint32() - pcdatalen := readUint32() - a.Pcdata = make([]uint32, pcdatalen) + a.Pcsp = readSymIdx() + a.Pcfile = readSymIdx() + a.Pcline = readSymIdx() + a.Pcinline = readSymIdx() + a.Pcdata = make([]SymRef, readUint32()) for i := range a.Pcdata { - a.Pcdata[i] = readUint32() + a.Pcdata[i] = readSymIdx() } - a.PcdataEnd = readUint32() + funcdataofflen := readUint32() a.Funcdataoff = make([]uint32, funcdataofflen) for i := range a.Funcdataoff { @@ -127,11 +132,13 @@ type FuncInfoLengths struct { func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths { var result FuncInfoLengths - const numpcdataOff = 28 + // Offset to the number of pcdata values. This value is determined by counting + // the number of bytes until we write pcdata to the file. + const numpcdataOff = 44 result.NumPcdata = binary.LittleEndian.Uint32(b[numpcdataOff:]) result.PcdataOff = numpcdataOff + 4 - numfuncdataoffOff := result.PcdataOff + 4*(result.NumPcdata+1) + numfuncdataoffOff := result.PcdataOff + 8*result.NumPcdata result.NumFuncdataoff = binary.LittleEndian.Uint32(b[numfuncdataoffOff:]) result.FuncdataoffOff = numfuncdataoffOff + 4 @@ -154,29 +161,28 @@ func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32 func (*FuncInfo) ReadFuncID(b []byte) uint32 { return binary.LittleEndian.Uint32(b[8:]) } -// return start and end offsets. -func (*FuncInfo) ReadPcsp(b []byte) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:]) +func (*FuncInfo) ReadPcsp(b []byte) SymRef { + return SymRef{binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])} } -// return start and end offsets. -func (*FuncInfo) ReadPcfile(b []byte) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[16:]), binary.LittleEndian.Uint32(b[20:]) +func (*FuncInfo) ReadPcfile(b []byte) SymRef { + return SymRef{binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:])} } -// return start and end offsets. -func (*FuncInfo) ReadPcline(b []byte) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:]) +func (*FuncInfo) ReadPcline(b []byte) SymRef { + return SymRef{binary.LittleEndian.Uint32(b[28:]), binary.LittleEndian.Uint32(b[32:])} } -// return start and end offsets. -func (*FuncInfo) ReadPcinline(b []byte, pcdataoffset uint32) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[24:]), binary.LittleEndian.Uint32(b[pcdataoffset:]) +func (*FuncInfo) ReadPcinline(b []byte) SymRef { + return SymRef{binary.LittleEndian.Uint32(b[36:]), binary.LittleEndian.Uint32(b[40:])} } -// return start and end offsets. -func (*FuncInfo) ReadPcdata(b []byte, pcdataoffset uint32, k uint32) (uint32, uint32) { - return binary.LittleEndian.Uint32(b[pcdataoffset+4*k:]), binary.LittleEndian.Uint32(b[pcdataoffset+4+4*k:]) +func (*FuncInfo) ReadPcdata(b []byte) []SymRef { + syms := make([]SymRef, binary.LittleEndian.Uint32(b[44:])) + for i := range syms { + syms[i] = SymRef{binary.LittleEndian.Uint32(b[48+i*8:]), binary.LittleEndian.Uint32(b[52+i*8:])} + } + return syms } func (*FuncInfo) ReadFuncdataoff(b []byte, funcdataofffoff uint32, k uint32) int64 { diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go index 8ec7c481d6a..6e76bea1117 100644 --- a/src/cmd/internal/goobj/objfile.go +++ b/src/cmd/internal/goobj/objfile.go @@ -433,8 +433,11 @@ const ( AuxDwarfLoc AuxDwarfRanges AuxDwarfLines - - // TODO: more. Pcdata? + AuxPcsp + AuxPcfile + AuxPcline + AuxPcinline + AuxPcdata ) func (a *Aux) Type() uint8 { return a[0] } @@ -839,11 +842,6 @@ func (r *Reader) Data(i uint32) []byte { return r.BytesAt(base+off, int(end-off)) } -// AuxDataBase returns the base offset of the aux data block. -func (r *Reader) PcdataBase() uint32 { - return r.h.Offsets[BlkPcdata] -} - // NRefName returns the number of referenced symbol names. func (r *Reader) NRefName() int { return int(r.h.Offsets[BlkRefName+1]-r.h.Offsets[BlkRefName]) / RefNameSize diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 1d4217b5f55..014c78dbfc1 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -634,11 +634,12 @@ func (s *LSym) CanBeAnSSASym() { } type Pcln struct { - Pcsp Pcdata - Pcfile Pcdata - Pcline Pcdata - Pcinline Pcdata - Pcdata []Pcdata + // Aux symbols for pcln + Pcsp *LSym + Pcfile *LSym + Pcline *LSym + Pcinline *LSym + Pcdata []*LSym Funcdata []*LSym Funcdataoff []int64 UsedFiles map[goobj.CUFileIndex]struct{} // file indices used while generating pcfile @@ -660,10 +661,6 @@ type Auto struct { Gotype *LSym } -type Pcdata struct { - P []byte -} - // Link holds the context for writing object code from a compiler // to be linker input or for reading that input into the linker. type Link struct { diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index 186016c9706..fa60c9ad6d8 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -185,7 +185,11 @@ func WriteObjFile(ctxt *Link, b *bio.Writer) { // Pcdata h.Offsets[goobj.BlkPcdata] = w.Offset() for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms - if s.Func != nil { + // Because of the phase order, it's possible that we try to write an invalid + // object file, and the Pcln variables haven't been filled in. As such, we + // need to check that Pcsp exists, and assume the other pcln variables exist + // as well. Tests like test/fixedbugs/issue22200.go demonstrate this issue. + if s.Func != nil && s.Func.Pcln.Pcsp != nil { pc := &s.Func.Pcln w.Bytes(pc.Pcsp.P) w.Bytes(pc.Pcfile.P) @@ -372,6 +376,19 @@ func contentHash64(s *LSym) goobj.Hash64Type { // hashed symbols. func (w *writer) contentHash(s *LSym) goobj.HashType { h := sha1.New() + var tmp [14]byte + + // Include the size of the symbol in the hash. + // This preserves the length of symbols, preventing the following two symbols + // from hashing the same: + // + // [2]int{1,2} ≠ [10]int{1,2,0,0,0...} + // + // In this case, if the smaller symbol is alive, the larger is not kept unless + // needed. + binary.LittleEndian.PutUint64(tmp[:8], uint64(s.Size)) + h.Write(tmp[:8]) + // Don't dedup type symbols with others, as they are in a different // section. if strings.HasPrefix(s.Name, "type.") { @@ -382,7 +399,6 @@ func (w *writer) contentHash(s *LSym) goobj.HashType { // The compiler trims trailing zeros _sometimes_. We just do // it always. h.Write(bytes.TrimRight(s.P, "\x00")) - var tmp [14]byte for i := range s.R { r := &s.R[i] binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off)) @@ -473,6 +489,22 @@ func (w *writer) Aux(s *LSym) { if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym) } + if s.Func.Pcln.Pcsp != nil && s.Func.Pcln.Pcsp.Size != 0 { + w.aux1(goobj.AuxPcsp, s.Func.Pcln.Pcsp) + } + if s.Func.Pcln.Pcfile != nil && s.Func.Pcln.Pcfile.Size != 0 { + w.aux1(goobj.AuxPcfile, s.Func.Pcln.Pcfile) + } + if s.Func.Pcln.Pcline != nil && s.Func.Pcln.Pcline.Size != 0 { + w.aux1(goobj.AuxPcline, s.Func.Pcln.Pcline) + } + if s.Func.Pcln.Pcinline != nil && s.Func.Pcln.Pcinline.Size != 0 { + w.aux1(goobj.AuxPcinline, s.Func.Pcln.Pcinline) + } + for _, pcSym := range s.Func.Pcln.Pcdata { + w.aux1(goobj.AuxPcdata, pcSym) + } + } } @@ -554,6 +586,19 @@ func nAuxSym(s *LSym) int { if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 { n++ } + if s.Func.Pcln.Pcsp != nil && s.Func.Pcln.Pcsp.Size != 0 { + n++ + } + if s.Func.Pcln.Pcfile != nil && s.Func.Pcln.Pcfile.Size != 0 { + n++ + } + if s.Func.Pcln.Pcline != nil && s.Func.Pcln.Pcline.Size != 0 { + n++ + } + if s.Func.Pcln.Pcinline != nil && s.Func.Pcln.Pcinline.Size != 0 { + n++ + } + n += len(s.Func.Pcln.Pcdata) } return n } @@ -561,7 +606,17 @@ func nAuxSym(s *LSym) int { // generate symbols for FuncInfo. func genFuncInfoSyms(ctxt *Link) { infosyms := make([]*LSym, 0, len(ctxt.Text)) - var pcdataoff uint32 + hashedsyms := make([]*LSym, 0, 4*len(ctxt.Text)) + preparePcSym := func(s *LSym) *LSym { + if s == nil { + return s + } + s.PkgIdx = goobj.PkgIdxHashed + s.SymIdx = int32(len(hashedsyms) + len(ctxt.hasheddefs)) + s.Set(AttrIndexed, true) + hashedsyms = append(hashedsyms, s) + return s + } var b bytes.Buffer symidx := int32(len(ctxt.defs)) for _, s := range ctxt.Text { @@ -574,20 +629,14 @@ func genFuncInfoSyms(ctxt *Link) { FuncID: objabi.FuncID(s.Func.FuncID), } pc := &s.Func.Pcln - o.Pcsp = pcdataoff - pcdataoff += uint32(len(pc.Pcsp.P)) - o.Pcfile = pcdataoff - pcdataoff += uint32(len(pc.Pcfile.P)) - o.Pcline = pcdataoff - pcdataoff += uint32(len(pc.Pcline.P)) - o.Pcinline = pcdataoff - pcdataoff += uint32(len(pc.Pcinline.P)) - o.Pcdata = make([]uint32, len(pc.Pcdata)) - for i, pcd := range pc.Pcdata { - o.Pcdata[i] = pcdataoff - pcdataoff += uint32(len(pcd.P)) - } - o.PcdataEnd = pcdataoff + o.Pcsp = makeSymRef(preparePcSym(pc.Pcsp)) + o.Pcfile = makeSymRef(preparePcSym(pc.Pcfile)) + o.Pcline = makeSymRef(preparePcSym(pc.Pcline)) + o.Pcinline = makeSymRef(preparePcSym(pc.Pcinline)) + o.Pcdata = make([]goobj.SymRef, len(pc.Pcdata)) + for i, pcSym := range pc.Pcdata { + o.Pcdata[i] = makeSymRef(preparePcSym(pcSym)) + } o.Funcdataoff = make([]uint32, len(pc.Funcdataoff)) for i, x := range pc.Funcdataoff { o.Funcdataoff[i] = uint32(x) @@ -637,9 +686,9 @@ func genFuncInfoSyms(ctxt *Link) { } } ctxt.defs = append(ctxt.defs, infosyms...) + ctxt.hasheddefs = append(ctxt.hasheddefs, hashedsyms...) } -// debugDumpAux is a dumper for selected aux symbols. func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) { // Most aux symbols (ex: funcdata) are not interesting-- // pick out just the DWARF ones for now. diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go index bffeda041dc..ce0d3714c06 100644 --- a/src/cmd/internal/obj/pcln.go +++ b/src/cmd/internal/obj/pcln.go @@ -6,6 +6,7 @@ package obj import ( "cmd/internal/goobj" + "cmd/internal/objabi" "encoding/binary" "log" ) @@ -14,16 +15,19 @@ import ( // 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); +// sym = valfunc(func, p, 0, arg); +// record sym.P as value at p->pc; +// sym = valfunc(func, 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. -func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) { +func funcpctab(ctxt *Link, func_ *LSym, desc string, valfunc func(*Link, *LSym, int32, *Prog, int32, interface{}) int32, arg interface{}) *LSym { dbg := desc == ctxt.Debugpcln - - dst.P = dst.P[:0] + dst := []byte{} + sym := &LSym{ + Type: objabi.SRODATA, + Attribute: AttrContentAddressable, + } if dbg { ctxt.Logf("funcpctab %s [valfunc=%s]\n", func_.Name, desc) @@ -32,7 +36,8 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(* val := int32(-1) oldval := val if func_.Func.Text == nil { - return + // Return the emtpy symbol we've built so far. + return sym } pc := func_.Func.Text.Pc @@ -88,13 +93,13 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(* if started { pcdelta := (p.Pc - pc) / int64(ctxt.Arch.MinLC) n := binary.PutUvarint(buf, uint64(pcdelta)) - dst.P = append(dst.P, buf[:n]...) + dst = append(dst, buf[:n]...) pc = p.Pc } delta := val - oldval n := binary.PutVarint(buf, int64(delta)) - dst.P = append(dst.P, buf[:n]...) + dst = append(dst, buf[:n]...) oldval = val started = true val = valfunc(ctxt, func_, val, p, 1, arg) @@ -109,18 +114,22 @@ func funcpctab(ctxt *Link, dst *Pcdata, func_ *LSym, desc string, valfunc func(* ctxt.Diag("negative pc offset: %v", v) } n := binary.PutUvarint(buf, uint64(v)) - dst.P = append(dst.P, buf[:n]...) + dst = append(dst, buf[:n]...) // add terminating varint-encoded 0, which is just 0 - dst.P = append(dst.P, 0) + dst = append(dst, 0) } if dbg { - ctxt.Logf("wrote %d bytes to %p\n", len(dst.P), dst) - for _, p := range dst.P { + ctxt.Logf("wrote %d bytes to %p\n", len(dst), dst) + for _, p := range dst { ctxt.Logf(" %02x", p) } ctxt.Logf("\n") } + + sym.Size = int64(len(dst)) + sym.P = dst + return sym } // pctofileline computes either the file number (arg == 0) @@ -268,15 +277,14 @@ func linkpcln(ctxt *Link, cursym *LSym) { } } - pcln.Pcdata = make([]Pcdata, npcdata) - pcln.Pcdata = pcln.Pcdata[:npcdata] + pcln.Pcdata = make([]*LSym, npcdata) pcln.Funcdata = make([]*LSym, nfuncdata) pcln.Funcdataoff = make([]int64, nfuncdata) pcln.Funcdataoff = pcln.Funcdataoff[:nfuncdata] - funcpctab(ctxt, &pcln.Pcsp, cursym, "pctospadj", pctospadj, nil) - funcpctab(ctxt, &pcln.Pcfile, cursym, "pctofile", pctofileline, pcln) - funcpctab(ctxt, &pcln.Pcline, cursym, "pctoline", pctofileline, nil) + pcln.Pcsp = funcpctab(ctxt, cursym, "pctospadj", pctospadj, nil) + pcln.Pcfile = funcpctab(ctxt, cursym, "pctofile", pctofileline, pcln) + pcln.Pcline = funcpctab(ctxt, cursym, "pctoline", pctofileline, nil) // Check that all the Progs used as inline markers are still reachable. // See issue #40473. @@ -294,7 +302,7 @@ func linkpcln(ctxt *Link, cursym *LSym) { } pcinlineState := new(pcinlineState) - funcpctab(ctxt, &pcln.Pcinline, cursym, "pctoinline", pcinlineState.pctoinline, nil) + pcln.Pcinline = funcpctab(ctxt, cursym, "pctoinline", pcinlineState.pctoinline, nil) for _, inlMark := range cursym.Func.InlMarks { pcinlineState.setParentPC(ctxt, int(inlMark.id), int32(inlMark.p.Pc)) } @@ -324,9 +332,14 @@ func linkpcln(ctxt *Link, cursym *LSym) { // pcdata. for i := 0; i < npcdata; i++ { if (havepc[i/32]>>uint(i%32))&1 == 0 { - continue + // use an empty symbol. + pcln.Pcdata[i] = &LSym{ + Type: objabi.SRODATA, + Attribute: AttrContentAddressable, + } + } else { + pcln.Pcdata[i] = funcpctab(ctxt, cursym, "pctopcdata", pctopcdata, interface{}(uint32(i))) } - funcpctab(ctxt, &pcln.Pcdata[i], cursym, "pctopcdata", pctopcdata, interface{}(uint32(i))) } // funcdata diff --git a/src/cmd/internal/objfile/goobj.go b/src/cmd/internal/objfile/goobj.go index af9ada3324e..7f74a8256ca 100644 --- a/src/cmd/internal/objfile/goobj.go +++ b/src/cmd/internal/objfile/goobj.go @@ -234,7 +234,15 @@ func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { if f.arch == nil { return "", 0, nil } - pcdataBase := r.PcdataBase() + getSymData := func(s goobj.SymRef) []byte { + if s.PkgIdx != goobj.PkgIdxHashed { + // We don't need the data for non-hashed symbols, yet. + panic("not supported") + } + i := uint32(s.SymIdx + uint32(r.NSym()+r.NHashed64def())) + return r.BytesAt(r.DataOff(i), r.DataSize(i)) + } + ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) for i := uint32(0); i < ndef; i++ { osym := r.Sym(i) @@ -260,11 +268,9 @@ func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { b := r.BytesAt(r.DataOff(isym), r.DataSize(isym)) var info *goobj.FuncInfo lengths := info.ReadFuncInfoLengths(b) - off, end := info.ReadPcline(b) - pcline := r.BytesAt(pcdataBase+off, int(end-off)) + pcline := getSymData(info.ReadPcline(b)) line := int(pcValue(pcline, pc-addr, f.arch)) - off, end = info.ReadPcfile(b) - pcfile := r.BytesAt(pcdataBase+off, int(end-off)) + pcfile := getSymData(info.ReadPcfile(b)) fileID := pcValue(pcfile, pc-addr, f.arch) globalFileID := info.ReadFile(b, lengths.FileOff, uint32(fileID)) fileName := r.File(int(globalFileID)) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index ed948d51b15..3cd7b4ad0b4 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1941,7 +1941,10 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect) ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pcheader", 0), sect) ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.funcnametab", 0), sect) - ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab_old", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.cutab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pctab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.functab", 0), sect) ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect) if ctxt.HeadType == objabi.Haix { xcoffUpdateOuterSize(ctxt, int64(sect.Length), sym.SPCLNTAB) @@ -2525,7 +2528,10 @@ func (ctxt *Link) address() []*sym.Segment { ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr)) ctxt.defineInternal("runtime.pcheader", sym.SRODATA) ctxt.defineInternal("runtime.funcnametab", sym.SRODATA) - ctxt.defineInternal("runtime.pclntab_old", sym.SRODATA) + ctxt.defineInternal("runtime.cutab", sym.SRODATA) + ctxt.defineInternal("runtime.filetab", sym.SRODATA) + ctxt.defineInternal("runtime.pctab", sym.SRODATA) + ctxt.defineInternal("runtime.functab", sym.SRODATA) ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length)) ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr)) ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length)) diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index d1f2ac583dc..2b95ad5a67d 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1421,7 +1421,7 @@ func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo { deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(thearch.Dwarfreglr)) } - for pcsp.Init(fpcsp); !pcsp.Done; pcsp.Next() { + for pcsp.Init(d.linkctxt.loader.Data(fpcsp)); !pcsp.Done; pcsp.Next() { nextpc := pcsp.NextPC // pciterinit goes up to the end of the function, diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index b2ca658c3ce..f5d41a0b4a6 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -543,7 +543,7 @@ func (ctxt *Link) loadlib() { } // Add non-package symbols and references of externally defined symbols. - ctxt.loader.LoadNonpkgSyms(ctxt.Arch) + ctxt.loader.LoadSyms(ctxt.Arch) // Load symbols from shared libraries, after all Go object symbols are loaded. for _, lib := range ctxt.Library { @@ -2259,7 +2259,7 @@ func (sc *stkChk) check(up *chain, depth int) int { var ch1 chain pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) ri := 0 - for pcsp.Init(info.Pcsp()); !pcsp.Done; pcsp.Next() { + for pcsp.Init(ldr.Data(info.Pcsp())); !pcsp.Done; pcsp.Next() { // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc). // Check stack size in effect for this span. diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index a2c8552e940..f26d051a491 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -71,7 +71,6 @@ type Link struct { LibraryByPkg map[string]*sym.Library Shlibs []Shlib Textp []loader.Sym - NumFilesyms int Moduledata loader.Sym PackageFile map[string]string diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 6f4ccbfb7a1..778b0e9245a 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -36,12 +36,14 @@ import ( "cmd/internal/objabi" "cmd/internal/sys" "cmd/link/internal/benchmark" + "cmd/link/internal/loader" "flag" "log" "os" "runtime" "runtime/pprof" "strings" + "sync" ) var ( @@ -324,9 +326,15 @@ func Main(arch *sys.Arch, theArch Arch) { bench.Start("Asmb") asmb(ctxt) // Generate large symbols. + var wg sync.WaitGroup for s, f := range ctxt.generatorSyms { - f(ctxt, s) + wg.Add(1) + go func(f generatorFunc, s loader.Sym) { + defer wg.Done() + f(ctxt, s) + }(f, s) } + wg.Wait() bench.Start("Asmb2") asmb2(ctxt) diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index 30e0bdc839c..75e63248df6 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -6,45 +6,24 @@ package ld import ( "cmd/internal/goobj" - "cmd/internal/obj" "cmd/internal/objabi" - "cmd/internal/src" "cmd/internal/sys" "cmd/link/internal/loader" "cmd/link/internal/sym" - "encoding/binary" "fmt" - "log" - "math" "os" "path/filepath" "strings" ) -// oldPclnState holds state information used during pclntab generation. Here -// 'ldr' is just a pointer to the context's loader, 'deferReturnSym' is the -// index for the symbol "runtime.deferreturn", 'nameToOffset' is a helper -// function for capturing function names, 'numberedFiles' records the file -// number assigned to a given file symbol, 'filepaths' is a slice of expanded -// paths (indexed by file number). -// -// NB: This is deprecated, and will be eliminated when pclntab_old is -// eliminated. -type oldPclnState struct { - ldr *loader.Loader - deferReturnSym loader.Sym - numberedFiles map[string]int64 - filepaths []string -} - // pclntab holds the state needed for pclntab generation. type pclntab struct { + // The size of the func object in the runtime. + funcSize uint32 + // The first and last functions found. firstFunc, lastFunc loader.Sym - // The offset to the filetab. - filetabOffset int32 - // Running total size of pclntab. size int64 @@ -54,6 +33,9 @@ type pclntab struct { pcheader loader.Sym funcnametab loader.Sym findfunctab loader.Sym + cutab loader.Sym + filetab loader.Sym + pctab loader.Sym // The number of functions + number of TEXT sections - 1. This is such an // unexpected value because platforms that have more than one TEXT section @@ -64,11 +46,8 @@ type pclntab struct { // On most platforms this is the number of reachable functions. nfunc int32 - // maps the function symbol to offset in runtime.funcnametab - // This doesn't need to reside in the state once pclntab_old's been - // deleted -- it can live in generateFuncnametab. - // TODO(jfaller): Delete me! - funcNameOffset map[loader.Sym]int32 + // The number of filenames in runtime.filetab. + nfiles uint32 } // addGeneratedSym adds a generator symbol to pclntab, returning the new Sym. @@ -83,40 +62,29 @@ func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f gen return s } -func makeOldPclnState(ctxt *Link) *oldPclnState { - ldr := ctxt.loader - drs := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal) - state := &oldPclnState{ - ldr: ldr, - deferReturnSym: drs, - numberedFiles: make(map[string]int64), - // NB: initial entry in filepaths below is to reserve the zero value, - // so that when we do a map lookup in numberedFiles fails, it will not - // return a value slot in filepaths. - filepaths: []string{""}, - } - - return state -} - // makePclntab makes a pclntab object, and assembles all the compilation units -// we'll need to write pclntab. -func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit) { +// we'll need to write pclntab. Returns the pclntab structure, a slice of the +// CompilationUnits we need, and a slice of the function symbols we need to +// generate pclntab. +func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) { ldr := ctxt.loader state := &pclntab{ - funcNameOffset: make(map[loader.Sym]int32), + // This is the size of the _func object in runtime/runtime2.go. + funcSize: uint32(ctxt.Arch.PtrSize + 9*4), } // Gather some basic stats and info. seenCUs := make(map[*sym.CompilationUnit]struct{}) prevSect := ldr.SymSect(ctxt.Textp[0]) compUnits := []*sym.CompilationUnit{} + funcs := []loader.Sym{} for _, s := range ctxt.Textp { if !emitPcln(ctxt, s, container) { continue } + funcs = append(funcs, s) state.nfunc++ if state.firstFunc == 0 { state.firstFunc = s @@ -142,87 +110,7 @@ func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.Compilat compUnits = append(compUnits, cu) } } - return state, compUnits -} - -func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 { - start := len(ftab.Data()) - ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL - ftd := ftab.Data() - copy(ftd[start:], s) - return int32(start) -} - -// numberfile assigns a file number to the file if it hasn't been assigned already. -// This funciton looks at a CU's file at index [i], and if it's a new filename, -// stores that filename in the global file table, and adds it to the map lookup -// for renumbering pcfile. -func (state *oldPclnState) numberfile(cu *sym.CompilationUnit, i goobj.CUFileIndex) int64 { - file := cu.FileTable[i] - if val, ok := state.numberedFiles[file]; ok { - return val - } - path := file - if strings.HasPrefix(path, src.FileSymPrefix) { - path = file[len(src.FileSymPrefix):] - } - val := int64(len(state.filepaths)) - state.numberedFiles[file] = val - state.filepaths = append(state.filepaths, expandGoroot(path)) - return val -} - -func (state *oldPclnState) fileVal(cu *sym.CompilationUnit, i int32) int64 { - file := cu.FileTable[i] - if val, ok := state.numberedFiles[file]; ok { - return val - } - panic("should have been numbered first") -} - -func (state *oldPclnState) renumberfiles(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, d *sym.Pcdata) { - // Give files numbers. - nf := fi.NumFile() - for i := uint32(0); i < nf; i++ { - state.numberfile(cu, fi.File(int(i))) - } - - buf := make([]byte, binary.MaxVarintLen32) - newval := int32(-1) - var out sym.Pcdata - it := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) - for it.Init(d.P); !it.Done; it.Next() { - // value delta - oldval := it.Value - - var val int32 - if oldval == -1 { - val = -1 - } else { - if oldval < 0 || oldval >= int32(len(cu.FileTable)) { - log.Fatalf("bad pcdata %d", oldval) - } - val = int32(state.fileVal(cu, oldval)) - } - - dv := val - newval - newval = val - - // value - n := binary.PutVarint(buf, int64(dv)) - out.P = append(out.P, buf[:n]...) - - // pc delta - pc := (it.NextPC - it.PC) / it.PCScale - n = binary.PutUvarint(buf, uint64(pc)) - out.P = append(out.P, buf[:n]...) - } - - // terminating value delta - // we want to write varint-encoded 0, which is just 0 - out.P = append(out.P, 0) - - *d = out + return state, compUnits, funcs } // onlycsymbol looks at a symbol's name to report whether this is a @@ -247,11 +135,13 @@ func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool { return !container.Has(s) } -func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 { +func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 { + ldr := ctxt.loader + target := ctxt.Target deferreturn := uint32(0) lastWasmAddr := uint32(0) - relocs := state.ldr.Relocs(s) + relocs := ldr.Relocs(s) for ri := 0; ri < relocs.Count(); ri++ { r := relocs.At(ri) if target.IsWasm() && r.Type() == objabi.R_ADDR { @@ -262,7 +152,7 @@ func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint // set the resumption point to PC_B. lastWasmAddr = uint32(r.Add()) } - if r.Type().IsDirectCall() && (r.Sym() == state.deferReturnSym || state.ldr.IsDeferReturnTramp(r.Sym())) { + if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) { if target.IsWasm() { deferreturn = lastWasmAddr - 1 } else { @@ -295,8 +185,8 @@ func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint // genInlTreeSym generates the InlTree sym for a function with the // specified FuncInfo. -func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, newState *pclntab) loader.Sym { - ldr := state.ldr +func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym { + ldr := ctxt.loader its := ldr.CreateExtSym("", 0) inlTreeSym := ldr.MakeSymbolUpdater(its) // Note: the generated symbol is given a type of sym.SGOFUNC, as a @@ -308,13 +198,8 @@ func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.Func ninl := fi.NumInlTree() for i := 0; i < int(ninl); i++ { call := fi.InlTree(i) - // Usually, call.File is already numbered since the file - // shows up in the Pcfile table. However, two inlined calls - // might overlap exactly so that only the innermost file - // appears in the Pcfile table. In that case, this assigns - // the outer file a number. - val := state.numberfile(cu, call.File) - nameoff, ok := newState.funcNameOffset[call.Func] + val := call.File + nameoff, ok := nameOffsets[call.Func] if !ok { panic("couldn't find function name offset") } @@ -337,6 +222,22 @@ func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.Func return its } +// makeInlSyms returns a map of loader.Sym that are created inlSyms. +func makeInlSyms(ctxt *Link, funcs []loader.Sym, nameOffsets map[loader.Sym]uint32) map[loader.Sym]loader.Sym { + ldr := ctxt.loader + // Create the inline symbols we need. + inlSyms := make(map[loader.Sym]loader.Sym) + for _, s := range funcs { + if fi := ldr.FuncInfo(s); fi.Valid() { + fi.Preload() + if fi.NumInlTree() > 0 { + inlSyms[s] = genInlTreeSym(ctxt, ldr.SymUnit(s), fi, ctxt.Arch, nameOffsets) + } + } + } + return inlSyms +} + // generatePCHeader creates the runtime.pcheader symbol, setting it up as a // generator to fill in its data later. func (state *pclntab) generatePCHeader(ctxt *Link) { @@ -359,24 +260,24 @@ func (state *pclntab) generatePCHeader(ctxt *Link) { header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC)) header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize)) off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc)) + off = header.SetUint(ctxt.Arch, off, uint64(state.nfiles)) off = writeSymOffset(off, state.funcnametab) + off = writeSymOffset(off, state.cutab) + off = writeSymOffset(off, state.filetab) + off = writeSymOffset(off, state.pctab) off = writeSymOffset(off, state.pclntab) } - size := int64(8 + 3*ctxt.Arch.PtrSize) + size := int64(8 + 7*ctxt.Arch.PtrSize) state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader) } -// walkFuncs iterates over the Textp, calling a function for each unique +// walkFuncs iterates over the funcs, calling a function for each unique // function and inlined function. -func (state *pclntab) walkFuncs(ctxt *Link, container loader.Bitmap, f func(loader.Sym)) { +func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) { ldr := ctxt.loader seen := make(map[loader.Sym]struct{}) - for _, ls := range ctxt.Textp { - s := loader.Sym(ls) - if !emitPcln(ctxt, s, container) { - continue - } + for _, s := range funcs { if _, ok := seen[s]; !ok { f(s) seen[s] = struct{}{} @@ -397,207 +298,484 @@ func (state *pclntab) walkFuncs(ctxt *Link, container loader.Bitmap, f func(load } } -// generateFuncnametab creates the function name table. -func (state *pclntab) generateFuncnametab(ctxt *Link, container loader.Bitmap) { +// generateFuncnametab creates the function name table. Returns a map of +// func symbol to the name offset in runtime.funcnamtab. +func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 { + nameOffsets := make(map[loader.Sym]uint32, state.nfunc) + // Write the null terminated strings. writeFuncNameTab := func(ctxt *Link, s loader.Sym) { symtab := ctxt.loader.MakeSymbolUpdater(s) - for s, off := range state.funcNameOffset { + for s, off := range nameOffsets { symtab.AddStringAt(int64(off), ctxt.loader.SymName(s)) } } // Loop through the CUs, and calculate the size needed. var size int64 - state.walkFuncs(ctxt, container, func(s loader.Sym) { - state.funcNameOffset[s] = int32(size) + walkFuncs(ctxt, funcs, func(s loader.Sym) { + nameOffsets[s] = uint32(size) size += int64(ctxt.loader.SymNameLen(s)) + 1 // NULL terminate }) state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab) + return nameOffsets } -// pclntab initializes the pclntab symbol with -// runtime function and file name information. +// walkFilenames walks funcs, calling a function for each filename used in each +// function's line table. +func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) { + ldr := ctxt.loader -// pclntab generates the pcln table for the link output. -func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { - // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the - // layout and data has changed since that time. - // - // As of July 2020, here's the layout of pclntab: - // - // .gopclntab/__gopclntab [elf/macho section] - // runtime.pclntab - // Carrier symbol for the entire pclntab section. - // - // runtime.pcheader (see: runtime/symtab.go:pcHeader) - // 8-byte magic - // nfunc [thearch.ptrsize bytes] - // offset to runtime.funcnametab from the beginning of runtime.pcheader - // offset to runtime.pclntab_old from beginning of runtime.pcheader + // Loop through all functions, finding the filenames we need. + for _, s := range funcs { + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() + + cu := ldr.SymUnit(s) + for i, nf := 0, int(fi.NumFile()); i < nf; i++ { + f(cu, fi.File(i)) + } + for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ { + call := fi.InlTree(i) + f(cu, call.File) + } + } +} + +// generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice +// of the index at which each CU begins in runtime.cutab. +// +// Function objects keep track of the files they reference to print the stack. +// This function creates a per-CU list of filenames if CU[M] references +// files[1-N], the following is generated: +// +// runtime.cutab: +// CU[M] +// offsetToFilename[0] +// offsetToFilename[1] +// .. +// +// runtime.filetab +// filename[0] +// filename[1] +// +// Looking up a filename then becomes: +// 0) Given a func, and filename index [K] +// 1) Get Func.CUIndex: M := func.cuOffset +// 2) Find filename offset: fileOffset := runtime.cutab[M+K] +// 3) Get the filename: getcstring(runtime.filetab[fileOffset]) +func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 { + // On a per-CU basis, keep track of all the filenames we need. // - // runtime.funcnametab - // []list of null terminated function names + // Note, that we store the filenames in a separate section in the object + // files, and deduplicate based on the actual value. It would be better to + // store the filenames as symbols, using content addressable symbols (and + // then not loading extra filenames), and just use the hash value of the + // symbol name to do this cataloging. // - // runtime.pclntab_old - // 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] - // func structures, pcdata tables. - // filetable + // TOOD: Store filenames as symbols. (Note this would be easiest if you + // also move strings to ALWAYS using the larger content addressable hash + // function, and use that hash value for uniqueness testing.) + cuEntries := make([]goobj.CUFileIndex, len(compUnits)) + fileOffsets := make(map[string]uint32) + + // Walk the filenames. + // We store the total filename string length we need to load, and the max + // file index we've seen per CU so we can calculate how large the + // CU->global table needs to be. + var fileSize int64 + walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) { + // Note we use the raw filename for lookup, but use the expanded filename + // when we save the size. + filename := cu.FileTable[i] + if _, ok := fileOffsets[filename]; !ok { + fileOffsets[filename] = uint32(fileSize) + fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate + } + + // Find the maximum file index we've seen. + if cuEntries[cu.PclnIndex] < i+1 { + cuEntries[cu.PclnIndex] = i + 1 // Store max + 1 + } + }) - oldState := makeOldPclnState(ctxt) - state, _ := makePclntab(ctxt, container) + // Calculate the size of the runtime.cutab variable. + var totalEntries uint32 + cuOffsets := make([]uint32, len(cuEntries)) + for i, entries := range cuEntries { + // Note, cutab is a slice of uint32, so an offset to a cu's entry is just the + // running total of all cu indices we've needed to store so far, not the + // number of bytes we've stored so far. + cuOffsets[i] = totalEntries + totalEntries += uint32(entries) + } + // Write cutab. + writeCutab := func(ctxt *Link, s loader.Sym) { + sb := ctxt.loader.MakeSymbolUpdater(s) + + var off int64 + for i, max := range cuEntries { + // Write the per CU LUT. + cu := compUnits[i] + for j := goobj.CUFileIndex(0); j < max; j++ { + fileOffset, ok := fileOffsets[cu.FileTable[j]] + if !ok { + // We're looping through all possible file indices. It's possible a file's + // been deadcode eliminated, and although it's a valid file in the CU, it's + // not needed in this binary. When that happens, use an invalid offset. + fileOffset = ^uint32(0) + } + off = sb.SetUint32(ctxt.Arch, off, fileOffset) + } + } + } + state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab) + + // Write filetab. + writeFiletab := func(ctxt *Link, s loader.Sym) { + sb := ctxt.loader.MakeSymbolUpdater(s) + + // Write the strings. + for filename, loc := range fileOffsets { + sb.AddStringAt(int64(loc), expandFile(filename)) + } + } + state.nfiles = uint32(len(fileOffsets)) + state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab) + + return cuOffsets +} + +// generatePctab creates the runtime.pctab variable, holding all the +// deduplicated pcdata. +func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) { ldr := ctxt.loader - state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0) - ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB) - ldr.SetAttrReachable(state.carrier, true) - // runtime.pclntab_old is just a placeholder,and will eventually be deleted. - // It contains the pieces of runtime.pclntab that haven't moved to a more - // rational form. - state.pclntab = ldr.LookupOrCreateSym("runtime.pclntab_old", 0) - state.generatePCHeader(ctxt) - state.generateFuncnametab(ctxt, container) - - funcdataBytes := int64(0) - ldr.SetCarrierSym(state.pclntab, state.carrier) - ldr.SetAttrNotInSymbolTable(state.pclntab, true) - ftab := ldr.MakeSymbolUpdater(state.pclntab) - ftab.SetValue(state.size) - ftab.SetType(sym.SPCLNTAB) - ftab.SetReachable(true) - - ftab.Grow(int64(state.nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4) - - szHint := len(ctxt.Textp) * 2 - pctaboff := make(map[string]uint32, szHint) - writepctab := func(off int32, p []byte) int32 { - start, ok := pctaboff[string(p)] - if !ok { - if len(p) > 0 { - start = uint32(len(ftab.Data())) - ftab.AddBytes(p) + // Pctab offsets of 0 are considered invalid in the runtime. We respect + // that by just padding a single byte at the beginning of runtime.pctab, + // that way no real offsets can be zero. + size := int64(1) + + // Walk the functions, finding offset to store each pcdata. + seen := make(map[loader.Sym]struct{}) + saveOffset := func(pcSym loader.Sym) { + if _, ok := seen[pcSym]; !ok { + datSize := ldr.SymSize(pcSym) + if datSize != 0 { + ldr.SetSymValue(pcSym, size) + } else { + // Invalid PC data, record as zero. + ldr.SetSymValue(pcSym, 0) } - pctaboff[string(p)] = start - } - newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start)) - return newoff - } - - setAddr := (*loader.SymbolBuilder).SetAddrPlus - if ctxt.IsExe() && ctxt.IsInternal() { - // Internal linking static executable. At this point the function - // addresses are known, so we can just use them instead of emitting - // relocations. - // For other cases we are generating a relocatable binary so we - // still need to emit relocations. - setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 { - if v := ldr.SymValue(tgt); v != 0 { - return s.SetUint(arch, off, uint64(v+add)) + size += datSize + seen[pcSym] = struct{}{} + } + } + for _, s := range funcs { + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() + + pcSyms := []loader.Sym{fi.Pcsp(), fi.Pcfile(), fi.Pcline()} + for _, pcSym := range pcSyms { + saveOffset(pcSym) + } + for _, pcSym := range fi.Pcdata() { + saveOffset(pcSym) + } + if fi.NumInlTree() > 0 { + saveOffset(fi.Pcinline()) + } + } + + // TODO: There is no reason we need a generator for this variable, and it + // could be moved to a carrier symbol. However, carrier symbols containing + // carrier symbols don't work yet (as of Aug 2020). Once this is fixed, + // runtime.pctab could just be a carrier sym. + writePctab := func(ctxt *Link, s loader.Sym) { + ldr := ctxt.loader + sb := ldr.MakeSymbolUpdater(s) + for sym := range seen { + sb.SetBytesAt(ldr.SymValue(sym), ldr.Data(sym)) + } + } + + state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab) +} + +// numPCData returns the number of PCData syms for the FuncInfo. +// NB: Preload must be called on valid FuncInfos before calling this function. +func numPCData(fi loader.FuncInfo) uint32 { + if !fi.Valid() { + return 0 + } + numPCData := uint32(len(fi.Pcdata())) + if fi.NumInlTree() > 0 { + if numPCData < objabi.PCDATA_InlTreeIndex+1 { + numPCData = objabi.PCDATA_InlTreeIndex + 1 + } + } + return numPCData +} + +// Helper types for iterating pclntab. +type pclnSetAddr func(*loader.SymbolBuilder, *sys.Arch, int64, loader.Sym, int64) int64 +type pclnSetUint func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64 + +// generateFunctab creates the runtime.functab +// +// runtime.functab contains two things: +// +// - pc->func look up table. +// - array of func objects, interleaved with pcdata and funcdata +// +// Because of timing in the linker, generating this table takes two passes. +// The first pass is executed early in the link, and it creates any needed +// relocations to layout the data. The pieces that need relocations are: +// 1) the PC->func table. +// 2) The entry points in the func objects. +// 3) The funcdata. +// (1) and (2) are handled in walkPCToFunc. (3) is handled in walkFuncdata. +// +// After relocations, once we know where to write things in the output buffer, +// we execute the second pass, which is actually writing the data. +func (state *pclntab) generateFunctab(ctxt *Link, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) { + // Calculate the size of the table. + size, startLocations := state.calculateFunctabSize(ctxt, funcs) + + // If we are internally linking a static executable, the function addresses + // are known, so we can just use them instead of emitting relocations. For + // other cases we still need to emit relocations. + // + // This boolean just helps us figure out which callback to use. + useSymValue := ctxt.IsExe() && ctxt.IsInternal() + + writePcln := func(ctxt *Link, s loader.Sym) { + ldr := ctxt.loader + sb := ldr.MakeSymbolUpdater(s) + + // Create our callbacks. + var setAddr pclnSetAddr + if useSymValue { + // We need to write the offset. + setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 { + if v := ldr.SymValue(tgt); v != 0 { + s.SetUint(arch, off, uint64(v+add)) + } + return 0 } - return s.SetAddrPlus(arch, off, tgt, add) + } else { + // We already wrote relocations. + setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 { return 0 } } + + // Write the data. + writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, (*loader.SymbolBuilder).SetUint) + writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets) + state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, (*loader.SymbolBuilder).SetUint) } - pcsp := sym.Pcdata{} - pcfile := sym.Pcdata{} - pcline := sym.Pcdata{} - pcdata := []sym.Pcdata{} - funcdata := []loader.Sym{} - funcdataoff := []int64{} + state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln) - var nfunc int32 - prevFunc := ctxt.Textp[0] - for _, s := range ctxt.Textp { - if !emitPcln(ctxt, s, container) { - continue + // Create the relocations we need. + ldr := ctxt.loader + sb := ldr.MakeSymbolUpdater(state.pclntab) + + var setAddr pclnSetAddr + if useSymValue { + // If we should use the symbol value, and we don't have one, write a relocation. + setAddr = func(sb *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 { + if v := ldr.SymValue(tgt); v == 0 { + sb.SetAddrPlus(arch, off, tgt, add) + } + return 0 } + } else { + // If we're externally linking, write a relocation. + setAddr = (*loader.SymbolBuilder).SetAddrPlus + } + setUintNOP := func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64 { return 0 } + writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, setUintNOP) + if !useSymValue { + // Generate relocations for funcdata when externally linking. + state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, setUintNOP) + } +} - thisSect := ldr.SymSect(s) - prevSect := ldr.SymSect(prevFunc) - if thisSect != prevSect { - // With multiple text sections, there may be a hole here - // in the address space (see the comment above). We use an - // invalid funcoff value to mark the hole. See also - // runtime/symtab.go:findfunc - prevFuncSize := int64(ldr.SymSize(prevFunc)) - setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFuncSize) - ftab.SetUint(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0)) - nfunc++ +// funcData returns the funcdata and offsets for the FuncInfo. +// The funcdata and offsets are written into runtime.functab after each func +// object. This is a helper function to make querying the FuncInfo object +// cleaner. +// +// Note, the majority of fdOffsets are 0, meaning there is no offset between +// the compiler's generated symbol, and what the runtime needs. They are +// plumbed through for no loss of generality. +// +// NB: Preload must be called on the FuncInfo before calling. +// NB: fdSyms and fdOffs are used as scratch space. +func funcData(fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym, fdOffs []int64) ([]loader.Sym, []int64) { + fdSyms, fdOffs = fdSyms[:0], fdOffs[:0] + if fi.Valid() { + numOffsets := int(fi.NumFuncdataoff()) + for i := 0; i < numOffsets; i++ { + fdOffs = append(fdOffs, fi.Funcdataoff(i)) + } + fdSyms = fi.Funcdata(fdSyms) + if fi.NumInlTree() > 0 { + if len(fdSyms) < objabi.FUNCDATA_InlTree+1 { + fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...) + fdOffs = append(fdOffs, make([]int64, objabi.FUNCDATA_InlTree+1-len(fdOffs))...) + } + fdSyms[objabi.FUNCDATA_InlTree] = inlSym } - prevFunc = s + } + return fdSyms, fdOffs +} - pcsp.P = pcsp.P[:0] - pcline.P = pcline.P[:0] - pcfile.P = pcfile.P[:0] - pcdata = pcdata[:0] - funcdataoff = funcdataoff[:0] - funcdata = funcdata[:0] +// calculateFunctabSize calculates the size of the pclntab, and the offsets in +// the output buffer for individual func entries. +func (state pclntab) calculateFunctabSize(ctxt *Link, funcs []loader.Sym) (int64, []uint32) { + ldr := ctxt.loader + startLocations := make([]uint32, len(funcs)) + + // Allocate space for the pc->func table. This structure consists of a pc + // and an offset to the func structure. After that, we have a single pc + // value that marks the end of the last function in the binary. + size := int64(int(state.nfunc)*2*ctxt.Arch.PtrSize + ctxt.Arch.PtrSize) + + // Now find the space for the func objects. We do this in a running manner, + // so that we can find individual starting locations, and because funcdata + // requires alignment. + for i, s := range funcs { + size = Rnd(size, int64(ctxt.Arch.PtrSize)) + startLocations[i] = uint32(size) fi := ldr.FuncInfo(s) + size += int64(state.funcSize) if fi.Valid() { fi.Preload() - npc := fi.NumPcdata() - for i := uint32(0); i < npc; i++ { - pcdata = append(pcdata, sym.Pcdata{P: fi.Pcdata(int(i))}) + numFuncData := int(fi.NumFuncdataoff()) + if fi.NumInlTree() > 0 { + if numFuncData < objabi.FUNCDATA_InlTree+1 { + numFuncData = objabi.FUNCDATA_InlTree + 1 + } } - nfd := fi.NumFuncdataoff() - for i := uint32(0); i < nfd; i++ { - funcdataoff = append(funcdataoff, fi.Funcdataoff(int(i))) + size += int64(numPCData(fi) * 4) + if numFuncData > 0 { // Func data is aligned. + size = Rnd(size, int64(ctxt.Arch.PtrSize)) } - funcdata = fi.Funcdata(funcdata) + size += int64(numFuncData * ctxt.Arch.PtrSize) } + } - if fi.Valid() && fi.NumInlTree() > 0 { - - if len(pcdata) <= objabi.PCDATA_InlTreeIndex { - // Create inlining pcdata table. - newpcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1) - copy(newpcdata, pcdata) - pcdata = newpcdata - } + return size, startLocations +} - if len(funcdataoff) <= objabi.FUNCDATA_InlTree { - // Create inline tree funcdata. - newfuncdata := make([]loader.Sym, objabi.FUNCDATA_InlTree+1) - newfuncdataoff := make([]int64, objabi.FUNCDATA_InlTree+1) - copy(newfuncdata, funcdata) - copy(newfuncdataoff, funcdataoff) - funcdata = newfuncdata - funcdataoff = newfuncdataoff - } +// writePcToFunc writes the PC->func lookup table. +// This function walks the pc->func lookup table, executing callbacks +// to generate relocations and writing the values for the table. +func writePcToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) { + ldr := ctxt.loader + var prevFunc loader.Sym + prevSect := ldr.SymSect(funcs[0]) + funcIndex := 0 + for i, s := range funcs { + if thisSect := ldr.SymSect(s); thisSect != prevSect { + // With multiple text sections, there may be a hole here in the + // address space. We use an invalid funcoff value to mark the hole. + // See also runtime/symtab.go:findfunc + prevFuncSize := int64(ldr.SymSize(prevFunc)) + setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), prevFunc, prevFuncSize) + setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), ^uint64(0)) + funcIndex++ + prevSect = thisSect } + prevFunc = s + // TODO: We don't actually need these relocations, provided we go to a + // module->func look-up-table like we do for filenames. We could have a + // single relocation for the module, and have them all laid out as + // offsets from the beginning of that module. + setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), s, 0) + setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), uint64(startLocations[i])) + funcIndex++ + + // Write the entry location. + setAddr(sb, ctxt.Arch, int64(startLocations[i]), s, 0) + } - dSize := len(ftab.Data()) - funcstart := int32(dSize) - funcstart += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize + // Final entry of table is just end pc. + setAddr(sb, ctxt.Arch, int64(funcIndex)*2*int64(ctxt.Arch.PtrSize), prevFunc, ldr.SymSize(prevFunc)) +} - setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s, 0) - ftab.SetUint(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart)) +// writeFuncData writes the funcdata tables. +// +// This function executes a callback for each funcdata needed in +// runtime.functab. It should be called once for internally linked static +// binaries, or twice (once to generate the needed relocations) for other +// build modes. +// +// Note the output of this function is interwoven with writeFuncs, but this is +// a separate function, because it's needed in different passes in +// generateFunctab. +func (state *pclntab) writeFuncData(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) { + ldr := ctxt.loader + funcdata, funcdataoff := []loader.Sym{}, []int64{} + for i, s := range funcs { + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() - // Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func - // and package debug/gosym. + // funcdata, must be pointer-aligned and we're only int32-aligned. + // Missing funcdata will be 0 (nil pointer). + funcdata, funcdataoff := funcData(fi, inlSyms[s], funcdata, funcdataoff) + if len(funcdata) > 0 { + off := int64(startLocations[i] + state.funcSize + numPCData(fi)*4) + off = Rnd(off, int64(ctxt.Arch.PtrSize)) + for j := range funcdata { + dataoff := off + int64(ctxt.Arch.PtrSize*j) + if funcdata[j] == 0 { + setUint(sb, ctxt.Arch, dataoff, uint64(funcdataoff[j])) + continue + } + // TODO: Does this need deduping? + setAddr(sb, ctxt.Arch, dataoff, funcdata[j], funcdataoff[j]) + } + } + } +} - // fixed size of struct, checked below - off := funcstart +// writeFuncs writes the func structures and pcdata to runtime.functab. +func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) { + ldr := ctxt.loader + deferReturnSym := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal) + funcdata, funcdataoff := []loader.Sym{}, []int64{} - end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcdata))*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize) - if len(funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) { - end += 4 + // Write the individual func objects. + for i, s := range funcs { + fi := ldr.FuncInfo(s) + if fi.Valid() { + fi.Preload() } - ftab.Grow(int64(end)) - // entry uintptr - off = int32(setAddr(ftab, ctxt.Arch, int64(off), s, 0)) + // Note we skip the space for the entry value -- that's handled inn + // walkPCToFunc. We don't write it here, because it might require a + // relocation. + off := startLocations[i] + uint32(ctxt.Arch.PtrSize) // entry // name int32 - nameoff, ok := state.funcNameOffset[s] + nameoff, ok := nameOffsets[s] if !ok { panic("couldn't find function name offset") } - off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff))) + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(nameoff))) // args int32 // TODO: Move into funcinfo. @@ -605,118 +783,106 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { if fi.Valid() { args = uint32(fi.Args()) } - off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args)) + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), args)) // deferreturn - deferreturn := oldState.computeDeferReturn(&ctxt.Target, s) - off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn)) + deferreturn := computeDeferReturn(ctxt, deferReturnSym, s) + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), deferreturn)) - cu := ldr.SymUnit(s) + // pcdata if fi.Valid() { - pcsp = sym.Pcdata{P: fi.Pcsp()} - pcfile = sym.Pcdata{P: fi.Pcfile()} - pcline = sym.Pcdata{P: fi.Pcline()} - oldState.renumberfiles(ctxt, cu, fi, &pcfile) - if false { - // Sanity check the new numbering - it := obj.NewPCIter(uint32(ctxt.Arch.MinLC)) - for it.Init(pcfile.P); !it.Done; it.Next() { - if it.Value < 1 || it.Value > int32(len(oldState.numberedFiles)) { - ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(oldState.numberedFiles)) - errorexit() - } - } - } - } - - if fi.Valid() && fi.NumInlTree() > 0 { - its := oldState.genInlTreeSym(cu, fi, ctxt.Arch, state) - funcdata[objabi.FUNCDATA_InlTree] = its - pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()} + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcsp())))) + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcfile())))) + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcline())))) + } else { + off += 12 } + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(numPCData(fi)))) - // pcdata - off = writepctab(off, pcsp.P) - off = writepctab(off, pcfile.P) - off = writepctab(off, pcline.P) - off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcdata)))) - - // Store the compilation unit index. - cuIdx := ^uint16(0) + // Store the offset to compilation unit's file table. + cuIdx := ^uint32(0) if cu := ldr.SymUnit(s); cu != nil { - if cu.PclnIndex > math.MaxUint16 { - panic("cu limit reached.") - } - cuIdx = uint16(cu.PclnIndex) + cuIdx = cuOffsets[cu.PclnIndex] } - off = int32(ftab.SetUint16(ctxt.Arch, int64(off), cuIdx)) + off = uint32(sb.SetUint32(ctxt.Arch, int64(off), cuIdx)) // funcID uint8 var funcID objabi.FuncID if fi.Valid() { funcID = fi.FuncID() } - off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID))) + off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(funcID))) + + off += 2 // pad // nfuncdata must be the final entry. - off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata)))) - for i := range pcdata { - off = writepctab(off, pcdata[i].P) - } + funcdata, funcdataoff = funcData(fi, 0, funcdata, funcdataoff) + off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata)))) - // funcdata, must be pointer-aligned and we're only int32-aligned. - // Missing funcdata will be 0 (nil pointer). - if len(funcdata) > 0 { - if off&int32(ctxt.Arch.PtrSize-1) != 0 { - off += 4 + // Output the pcdata. + if fi.Valid() { + for j, pcSym := range fi.Pcdata() { + sb.SetUint32(ctxt.Arch, int64(off+uint32(j*4)), uint32(ldr.SymValue(pcSym))) } - for i := range funcdata { - dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i) - if funcdata[i] == 0 { - ftab.SetUint(ctxt.Arch, dataoff, uint64(funcdataoff[i])) - continue - } - // TODO: Dedup. - funcdataBytes += int64(len(ldr.Data(funcdata[i]))) - setAddr(ftab, ctxt.Arch, dataoff, funcdata[i], funcdataoff[i]) + if fi.NumInlTree() > 0 { + sb.SetUint32(ctxt.Arch, int64(off+objabi.PCDATA_InlTreeIndex*4), uint32(ldr.SymValue(fi.Pcinline()))) } - off += int32(len(funcdata)) * int32(ctxt.Arch.PtrSize) } - - if off != end { - ctxt.Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcdata), len(funcdata), ctxt.Arch.PtrSize) - errorexit() - } - - nfunc++ } +} - // Final entry of table is just end pc. - setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), state.lastFunc, ldr.SymSize(state.lastFunc)) - - // Start file table. - dSize := len(ftab.Data()) - start := int32(dSize) - start += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) - state.filetabOffset = start - ftab.SetUint32(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start)) +// pclntab initializes the pclntab symbol with +// runtime function and file name information. - nf := len(oldState.numberedFiles) - ftab.Grow(int64(start) + int64((nf+1)*4)) - ftab.SetUint32(ctxt.Arch, int64(start), uint32(nf+1)) - for i := nf; i > 0; i-- { - path := oldState.filepaths[i] - val := int64(i) - ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(ftabaddstring(ftab, path))) - } +// pclntab generates the pcln table for the link output. +func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { + // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the + // layout and data has changed since that time. + // + // As of August 2020, here's the layout of pclntab: + // + // .gopclntab/__gopclntab [elf/macho section] + // runtime.pclntab + // Carrier symbol for the entire pclntab section. + // + // runtime.pcheader (see: runtime/symtab.go:pcHeader) + // 8-byte magic + // nfunc [thearch.ptrsize bytes] + // offset to runtime.funcnametab from the beginning of runtime.pcheader + // offset to runtime.pclntab_old from beginning of runtime.pcheader + // + // runtime.funcnametab + // []list of null terminated function names + // + // runtime.cutab + // for i=0..#CUs + // for j=0..#max used file index in CU[i] + // uint32 offset into runtime.filetab for the filename[j] + // + // runtime.filetab + // []null terminated filename strings + // + // runtime.pctab + // []byte of deduplicated pc data. + // + // runtime.functab + // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes] + // end PC [thearch.ptrsize bytes] + // func structures, pcdata offsets, func data. - ftab.SetSize(int64(len(ftab.Data()))) + state, compUnits, funcs := makePclntab(ctxt, container) - ctxt.NumFilesyms = len(oldState.numberedFiles) + ldr := ctxt.loader + state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0) + ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB) + ldr.SetAttrReachable(state.carrier, true) - if ctxt.Debugvlog != 0 { - ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes) - } + state.generatePCHeader(ctxt) + nameOffsets := state.generateFuncnametab(ctxt, funcs) + cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs) + state.generatePctab(ctxt, funcs) + inlSyms := makeInlSyms(ctxt, funcs, nameOffsets) + state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets) return state } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 56363cdaaea..dd82963a416 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -619,6 +619,18 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { moduledata.AddAddr(ctxt.Arch, pcln.funcnametab) moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab))) moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab))) + // The cutab slice + moduledata.AddAddr(ctxt.Arch, pcln.cutab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab))) + // The filetab slice + moduledata.AddAddr(ctxt.Arch, pcln.filetab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab))) + // The pctab slice + moduledata.AddAddr(ctxt.Arch, pcln.pctab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pctab))) // The pclntab slice moduledata.AddAddr(ctxt.Arch, pcln.pclntab) moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab))) @@ -627,10 +639,6 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { moduledata.AddAddr(ctxt.Arch, pcln.pclntab) moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) - // The filetab slice - moduledata.AddAddrPlus(ctxt.Arch, pcln.pclntab, int64(pcln.filetabOffset)) - moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1) - moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1) // findfunctab moduledata.AddAddr(ctxt.Arch, pcln.findfunctab) // minpc, maxpc diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 4025edc23f7..19d8d98b1e9 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -330,7 +330,7 @@ func NewLoader(flags uint32, elfsetstring elfsetstringFunc, reporter *ErrorRepor ldr := &Loader{ start: make(map[*oReader]Sym), objs: []objIdx{{}, {extReader, 0}}, // reserve index 0 for nil symbol, 1 for external symbols - objSyms: make([]objSym, 1, 100000), // reserve index 0 for nil symbol + objSyms: make([]objSym, 1, 1), // This will get overwritten later. extReader: extReader, symsByName: [2]map[string]Sym{make(map[string]Sym, 80000), make(map[string]Sym, 50000)}, // preallocate ~2MB for ABI0 and ~1MB for ABI1 symbols objByPkg: make(map[string]uint32), @@ -1875,19 +1875,24 @@ func (fi *FuncInfo) FuncID() objabi.FuncID { return objabi.FuncID((*goobj.FuncInfo)(nil).ReadFuncID(fi.data)) } -func (fi *FuncInfo) Pcsp() []byte { - pcsp, end := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data) - return fi.r.BytesAt(fi.r.PcdataBase()+pcsp, int(end-pcsp)) +func (fi *FuncInfo) Pcsp() Sym { + sym := (*goobj.FuncInfo)(nil).ReadPcsp(fi.data) + return fi.l.resolve(fi.r, sym) } -func (fi *FuncInfo) Pcfile() []byte { - pcf, end := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data) - return fi.r.BytesAt(fi.r.PcdataBase()+pcf, int(end-pcf)) +func (fi *FuncInfo) Pcfile() Sym { + sym := (*goobj.FuncInfo)(nil).ReadPcfile(fi.data) + return fi.l.resolve(fi.r, sym) } -func (fi *FuncInfo) Pcline() []byte { - pcln, end := (*goobj.FuncInfo)(nil).ReadPcline(fi.data) - return fi.r.BytesAt(fi.r.PcdataBase()+pcln, int(end-pcln)) +func (fi *FuncInfo) Pcline() Sym { + sym := (*goobj.FuncInfo)(nil).ReadPcline(fi.data) + return fi.l.resolve(fi.r, sym) +} + +func (fi *FuncInfo) Pcinline() Sym { + sym := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data) + return fi.l.resolve(fi.r, sym) } // Preload has to be called prior to invoking the various methods @@ -1896,27 +1901,16 @@ func (fi *FuncInfo) Preload() { fi.lengths = (*goobj.FuncInfo)(nil).ReadFuncInfoLengths(fi.data) } -func (fi *FuncInfo) Pcinline() []byte { +func (fi *FuncInfo) Pcdata() []Sym { if !fi.lengths.Initialized { panic("need to call Preload first") } - pcinl, end := (*goobj.FuncInfo)(nil).ReadPcinline(fi.data, fi.lengths.PcdataOff) - return fi.r.BytesAt(fi.r.PcdataBase()+pcinl, int(end-pcinl)) -} - -func (fi *FuncInfo) NumPcdata() uint32 { - if !fi.lengths.Initialized { - panic("need to call Preload first") + syms := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data) + ret := make([]Sym, len(syms)) + for i := range ret { + ret[i] = fi.l.resolve(fi.r, syms[i]) } - return fi.lengths.NumPcdata -} - -func (fi *FuncInfo) Pcdata(k int) []byte { - if !fi.lengths.Initialized { - panic("need to call Preload first") - } - pcdat, end := (*goobj.FuncInfo)(nil).ReadPcdata(fi.data, fi.lengths.PcdataOff, uint32(k)) - return fi.r.BytesAt(fi.r.PcdataBase()+pcdat, int(end-pcdat)) + return ret } func (fi *FuncInfo) NumFuncdataoff() uint32 { @@ -2019,8 +2013,9 @@ func (l *Loader) FuncInfo(i Sym) FuncInfo { return FuncInfo{} } -// Preload a package: add autolibs, add defined package symbols to the symbol table. -// Does not add non-package symbols yet, which will be done in LoadNonpkgSyms. +// Preload a package: adds autolib. +// Does not add defined package or non-packaged symbols to the symbol table. +// These are done in LoadSyms. // Does not read symbol data. // Returns the fingerprint of the object. func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64) goobj.FingerprintType { @@ -2063,8 +2058,6 @@ func (l *Loader) Preload(localSymVersion int, f *bio.Reader, lib *sym.Library, u } l.addObj(lib.Pkg, or) - st := loadState{l: l} - st.preloadSyms(or, pkgDef) // The caller expects us consuming all the data f.MustSeek(length, os.SEEK_CUR) @@ -2147,17 +2140,30 @@ func (st *loadState) preloadSyms(r *oReader, kind int) { } } -// Add hashed (content-addressable) symbols, non-package symbols, and +// Add syms, hashed (content-addressable) symbols, non-package symbols, and // references to external symbols (which are always named). -func (l *Loader) LoadNonpkgSyms(arch *sys.Arch) { +func (l *Loader) LoadSyms(arch *sys.Arch) { + // Allocate space for symbols, making a guess as to how much space we need. + // This function was determined empirically by looking at the cmd/compile on + // Darwin, and picking factors for hashed and hashed64 syms. + var symSize, hashedSize, hashed64Size int + for _, o := range l.objs[goObjStart:] { + symSize += o.r.ndef + o.r.nhasheddef/2 + o.r.nhashed64def/2 + o.r.NNonpkgdef() + hashedSize += o.r.nhasheddef / 2 + hashed64Size += o.r.nhashed64def / 2 + } + // Index 0 is invalid for symbols. + l.objSyms = make([]objSym, 1, symSize) + l.npkgsyms = l.NSym() - // Preallocate some space (a few hundreds KB) for some symbols. - // As of Go 1.15, linking cmd/compile has ~8000 hashed64 symbols and - // ~27000 hashed symbols. st := loadState{ l: l, - hashed64Syms: make(map[uint64]symAndSize, 10000), - hashedSyms: make(map[goobj.HashType]symAndSize, 30000), + hashed64Syms: make(map[uint64]symAndSize, hashed64Size), + hashedSyms: make(map[goobj.HashType]symAndSize, hashedSize), + } + + for _, o := range l.objs[goObjStart:] { + st.preloadSyms(o.r, pkgDef) } for _, o := range l.objs[goObjStart:] { st.preloadSyms(o.r, hashed64Def) diff --git a/src/cmd/link/internal/loader/symbolbuilder.go b/src/cmd/link/internal/loader/symbolbuilder.go index e14d89a9273..c0c723d7f00 100644 --- a/src/cmd/link/internal/loader/symbolbuilder.go +++ b/src/cmd/link/internal/loader/symbolbuilder.go @@ -336,6 +336,15 @@ func (sb *SymbolBuilder) Addstring(str string) int64 { return r } +func (sb *SymbolBuilder) SetBytesAt(off int64, b []byte) int64 { + datLen := int64(len(b)) + if off+datLen > int64(len(sb.data)) { + panic("attempt to write past end of buffer") + } + copy(sb.data[off:off+datLen], b) + return off + datLen +} + func (sb *SymbolBuilder) addSymRef(tgt Sym, add int64, typ objabi.RelocType, rsize int) int64 { if sb.kind == 0 { sb.kind = sym.SDATA diff --git a/src/cmd/link/internal/sym/symbol.go b/src/cmd/link/internal/sym/symbol.go index 1a4165ebf7c..70cf36a87ee 100644 --- a/src/cmd/link/internal/sym/symbol.go +++ b/src/cmd/link/internal/sym/symbol.go @@ -33,7 +33,3 @@ func VersionToABI(v int) (obj.ABI, bool) { } return ^obj.ABI(0), false } - -type Pcdata struct { - P []byte -} diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go index e5c50520fca..a72f9847d7c 100644 --- a/src/debug/gosym/pclntab.go +++ b/src/debug/gosym/pclntab.go @@ -53,14 +53,19 @@ type LineTable struct { quantum uint32 ptrsize uint32 funcnametab []byte + cutab []byte funcdata []byte functab []byte nfunctab uint32 filetab []byte + pctab []byte // points to the pctables. nfiletab uint32 - fileMap map[string]uint32 funcNames map[uint32]string // cache the function names strings map[uint32]string // interned substrings of Data, keyed by offset + // fileMap varies depending on the version of the object file. + // For ver12, it maps the name to the index in the file table. + // For ver116, it maps the name to the offset in filetab. + fileMap map[string]uint32 } // NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4, @@ -223,22 +228,26 @@ func (t *LineTable) parsePclnTab() { switch possibleVersion { case ver116: t.nfunctab = uint32(t.uintptr(t.Data[8:])) - offset := t.uintptr(t.Data[8+t.ptrsize:]) + t.nfiletab = uint32(t.uintptr(t.Data[8+t.ptrsize:])) + offset := t.uintptr(t.Data[8+2*t.ptrsize:]) t.funcnametab = t.Data[offset:] - offset = t.uintptr(t.Data[8+2*t.ptrsize:]) + offset = t.uintptr(t.Data[8+3*t.ptrsize:]) + t.cutab = t.Data[offset:] + offset = t.uintptr(t.Data[8+4*t.ptrsize:]) + t.filetab = t.Data[offset:] + offset = t.uintptr(t.Data[8+5*t.ptrsize:]) + t.pctab = t.Data[offset:] + offset = t.uintptr(t.Data[8+6*t.ptrsize:]) t.funcdata = t.Data[offset:] t.functab = t.Data[offset:] functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize - fileoff := t.binary.Uint32(t.functab[functabsize:]) - t.filetab = t.functab[fileoff:] t.functab = t.functab[:functabsize] - t.nfiletab = t.binary.Uint32(t.filetab) - t.filetab = t.filetab[:t.nfiletab*4] case ver12: t.nfunctab = uint32(t.uintptr(t.Data[8:])) t.funcdata = t.Data t.funcnametab = t.Data t.functab = t.Data[8+t.ptrsize:] + t.pctab = t.Data functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize fileoff := t.binary.Uint32(t.functab[functabsize:]) t.functab = t.functab[:functabsize] @@ -330,17 +339,22 @@ func (t *LineTable) funcName(off uint32) string { return s } -// string returns a Go string found at off. -func (t *LineTable) string(off uint32) string { +// stringFrom returns a Go string found at off from a position. +func (t *LineTable) stringFrom(arr []byte, off uint32) string { if s, ok := t.strings[off]; ok { return s } - i := bytes.IndexByte(t.funcdata[off:], 0) - s := string(t.funcdata[off : off+uint32(i)]) + i := bytes.IndexByte(arr[off:], 0) + s := string(arr[off : off+uint32(i)]) t.strings[off] = s return s } +// string returns a Go string found at off. +func (t *LineTable) string(off uint32) string { + return t.stringFrom(t.funcdata, off) +} + // step advances to the next pc, value pair in the encoded table. func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool { uvdelta := t.readvarint(p) @@ -363,7 +377,7 @@ func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool { // off is the offset to the beginning of the pc-value table, // and entry is the start PC for the corresponding function. func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 { - p := t.funcdata[off:] + p := t.pctab[off:] val := int32(-1) pc := entry @@ -381,21 +395,25 @@ func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 { // to file number. Since most functions come from a single file, these // are usually short and quick to scan. If a file match is found, then the // code goes to the expense of looking for a simultaneous line number match. -func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 { +func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32, cutab []byte) uint64 { if filetab == 0 || linetab == 0 { return 0 } - fp := t.funcdata[filetab:] - fl := t.funcdata[linetab:] + fp := t.pctab[filetab:] + fl := t.pctab[linetab:] fileVal := int32(-1) filePC := entry lineVal := int32(-1) linePC := entry fileStartPC := filePC for t.step(&fp, &filePC, &fileVal, filePC == entry) { - if fileVal == filenum && fileStartPC < filePC { - // fileVal is in effect starting at fileStartPC up to + fileIndex := fileVal + if t.version == ver116 { + fileIndex = int32(t.binary.Uint32(cutab[fileVal*4:])) + } + if fileIndex == filenum && fileStartPC < filePC { + // fileIndex is in effect starting at fileStartPC up to // but not including filePC, and it's the file we want. // Run the PC table looking for a matching line number // or until we reach filePC. @@ -450,13 +468,24 @@ func (t *LineTable) go12PCToFile(pc uint64) (file string) { entry := t.uintptr(f) filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) fno := t.pcvalue(filetab, entry, pc) - if fno <= 0 { + if t.version == ver12 { + if fno <= 0 { + return "" + } + return t.string(t.binary.Uint32(t.filetab[4*fno:])) + } + // Go ≥ 1.16 + if fno < 0 { // 0 is valid for ≥ 1.16 return "" } - return t.string(t.binary.Uint32(t.filetab[4*fno:])) + cuoff := t.binary.Uint32(f[t.ptrsize+7*4:]) + if fnoff := t.binary.Uint32(t.cutab[(cuoff+uint32(fno))*4:]); fnoff != ^uint32(0) { + return t.stringFrom(t.filetab, fnoff) + } + return "" } -// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table. +// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2/1.16 pcln table. func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) { defer func() { if recover() != nil { @@ -465,20 +494,25 @@ func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) { }() t.initFileMap() - filenum := t.fileMap[file] - if filenum == 0 { + filenum, ok := t.fileMap[file] + if !ok { return 0 } // Scan all functions. // If this turns out to be a bottleneck, we could build a map[int32][]int32 // mapping file number to a list of functions with code from that file. + var cutab []byte for i := uint32(0); i < t.nfunctab; i++ { f := t.funcdata[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):] entry := t.uintptr(f) filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) - pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line)) + if t.version == ver116 { + cuoff := t.binary.Uint32(f[t.ptrsize+7*4:]) * 4 + cutab = t.cutab[cuoff:] + } + pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line), cutab) if pc != 0 { return pc } @@ -496,9 +530,18 @@ func (t *LineTable) initFileMap() { } m := make(map[string]uint32) - for i := uint32(1); i < t.nfiletab; i++ { - s := t.string(t.binary.Uint32(t.filetab[4*i:])) - m[s] = i + if t.version == ver12 { + for i := uint32(1); i < t.nfiletab; i++ { + s := t.string(t.binary.Uint32(t.filetab[4*i:])) + m[s] = i + } + } else { + var pos uint32 + for i := uint32(0); i < t.nfiletab; i++ { + s := t.stringFrom(t.filetab, pos) + m[s] = pos + pos += uint32(len(s) + 1) + } } t.fileMap = m } diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 48e07f789b2..519872b8e2e 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -810,13 +810,14 @@ type _func struct { args int32 // in/out args size deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any. - pcsp int32 - pcfile int32 - pcln int32 - npcdata int32 - cuIndex uint16 // TODO(jfaller): 16 bits is never enough, make this larger. - funcID funcID // set for certain special runtime functions - nfuncdata uint8 // must be last + pcsp uint32 + pcfile uint32 + pcln uint32 + npcdata uint32 + cuOffset uint32 // runtime.cutab offset of this function's CU + funcID funcID // set for certain special runtime functions + _ [2]byte // pad + nfuncdata uint8 // must be last } // Pseudo-Func that is returned for PCs that occur in inlined code. diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index fa8d17035e1..a14f5c13d9f 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -337,14 +337,18 @@ const ( funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.) ) -// PCHeader holds data used by the pclntab lookups. +// pcHeader holds data used by the pclntab lookups. type pcHeader struct { magic uint32 // 0xFFFFFFFA pad1, pad2 uint8 // 0,0 minLC uint8 // min instruction size ptrSize uint8 // size of a ptr in bytes nfunc int // number of functions in the module + nfiles uint // number of entries in the file tab. funcnameOffset uintptr // offset to the funcnametab variable from pcHeader + cuOffset uintptr // offset to the cutab variable from pcHeader + filetabOffset uintptr // offset to the filetab variable from pcHeader + pctabOffset uintptr // offset to the pctab varible from pcHeader pclnOffset uintptr // offset to the pclntab variable from pcHeader } @@ -356,9 +360,11 @@ type pcHeader struct { type moduledata struct { pcHeader *pcHeader funcnametab []byte + cutab []uint32 + filetab []byte + pctab []byte pclntable []byte ftab []functab - filetab []uint32 findfunctab uintptr minpc, maxpc uintptr @@ -720,7 +726,7 @@ type pcvalueCache struct { type pcvalueCacheEnt struct { // targetpc and off together are the key of this cache entry. targetpc uintptr - off int32 + off uint32 // val is the value of this cached pcvalue entry. val int32 } @@ -735,7 +741,7 @@ func pcvalueCacheKey(targetpc uintptr) uintptr { // Returns the PCData value, and the PC where this value starts. // TODO: the start PC is returned only when cache is nil. -func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, strict bool) (int32, uintptr) { +func pcvalue(f funcInfo, off uint32, targetpc uintptr, cache *pcvalueCache, strict bool) (int32, uintptr) { if off == 0 { return -1, 0 } @@ -769,7 +775,7 @@ func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, stric return -1, 0 } datap := f.datap - p := datap.pclntable[off:] + p := datap.pctab[off:] pc := f.entry prevpc := pc val := int32(-1) @@ -811,7 +817,7 @@ func pcvalue(f funcInfo, off int32, targetpc uintptr, cache *pcvalueCache, stric print("runtime: invalid pc-encoded table f=", funcname(f), " pc=", hex(pc), " targetpc=", hex(targetpc), " tab=", p, "\n") - p = datap.pclntable[off:] + p = datap.pctab[off:] pc = f.entry val = -1 for { @@ -854,7 +860,12 @@ func funcfile(f funcInfo, fileno int32) string { if !f.valid() { return "?" } - return gostringnocopy(&datap.pclntable[datap.filetab[fileno]]) + // Make sure the cu index and file offset are valid + if fileoff := datap.cutab[f.cuOffset+uint32(fileno)]; fileoff != ^uint32(0) { + return gostringnocopy(&datap.filetab[fileoff]) + } + // pcln section is corrupt. + return "?" } func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int32) { @@ -868,7 +879,7 @@ func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int // print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n") return "?", 0 } - file = gostringnocopy(&datap.pclntable[datap.filetab[fileno]]) + file = funcfile(f, fileno) return } @@ -887,7 +898,7 @@ func funcspdelta(f funcInfo, targetpc uintptr, cache *pcvalueCache) int32 { // funcMaxSPDelta returns the maximum spdelta at any point in f. func funcMaxSPDelta(f funcInfo) int32 { datap := f.datap - p := datap.pclntable[f.pcsp:] + p := datap.pctab[f.pcsp:] pc := f.entry val := int32(-1) max := int32(0) @@ -903,20 +914,20 @@ func funcMaxSPDelta(f funcInfo) int32 { } } -func pcdatastart(f funcInfo, table int32) int32 { - return *(*int32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4)) +func pcdatastart(f funcInfo, table uint32) uint32 { + return *(*uint32)(add(unsafe.Pointer(&f.nfuncdata), unsafe.Sizeof(f.nfuncdata)+uintptr(table)*4)) } -func pcdatavalue(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache) int32 { - if table < 0 || table >= f.npcdata { +func pcdatavalue(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache) int32 { + if table >= f.npcdata { return -1 } r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, true) return r } -func pcdatavalue1(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 { - if table < 0 || table >= f.npcdata { +func pcdatavalue1(f funcInfo, table uint32, targetpc uintptr, cache *pcvalueCache, strict bool) int32 { + if table >= f.npcdata { return -1 } r, _ := pcvalue(f, pcdatastart(f, table), targetpc, cache, strict) @@ -925,8 +936,8 @@ func pcdatavalue1(f funcInfo, table int32, targetpc uintptr, cache *pcvalueCache // Like pcdatavalue, but also return the start PC of this PCData value. // It doesn't take a cache. -func pcdatavalue2(f funcInfo, table int32, targetpc uintptr) (int32, uintptr) { - if table < 0 || table >= f.npcdata { +func pcdatavalue2(f funcInfo, table uint32, targetpc uintptr) (int32, uintptr) { + if table >= f.npcdata { return -1, 0 } return pcvalue(f, pcdatastart(f, table), targetpc, nil, true) @@ -1008,7 +1019,7 @@ type inlinedCall struct { parent int16 // index of parent in the inltree, or < 0 funcID funcID // type of the called function _ byte - file int32 // fileno index into filetab + file int32 // perCU file index for inlined call. See cmd/link:pcln.go line int32 // line number of the call site func_ int32 // offset into pclntab for name of called function parentPc int32 // position of an instruction whose source position is the call site (offset from entry)