| // Copyright 2019 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 goobj | |
| import ( | |
| "bytes" | |
| "cmd/internal/objabi" | |
| "encoding/binary" | |
| ) | |
| // CUFileIndex is used to index the filenames that are stored in the | |
| // per-package/per-CU FileList. | |
| type CUFileIndex uint32 | |
| // FuncInfo is serialized as a symbol (aux symbol). The symbol data is | |
| // the binary encoding of the struct below. | |
| // | |
| // TODO: make each pcdata a separate symbol? | |
| type FuncInfo struct { | |
| Args uint32 | |
| Locals uint32 | |
| FuncID objabi.FuncID | |
| FuncFlag objabi.FuncFlag | |
| Pcsp SymRef | |
| Pcfile SymRef | |
| Pcline SymRef | |
| Pcinline SymRef | |
| Pcdata []SymRef | |
| Funcdataoff []uint32 | |
| File []CUFileIndex | |
| InlTree []InlTreeNode | |
| } | |
| func (a *FuncInfo) Write(w *bytes.Buffer) { | |
| writeUint8 := func(x uint8) { | |
| w.WriteByte(x) | |
| } | |
| var b [4]byte | |
| writeUint32 := func(x uint32) { | |
| binary.LittleEndian.PutUint32(b[:], x) | |
| w.Write(b[:]) | |
| } | |
| writeSymRef := func(s SymRef) { | |
| writeUint32(s.PkgIdx) | |
| writeUint32(s.SymIdx) | |
| } | |
| writeUint32(a.Args) | |
| writeUint32(a.Locals) | |
| writeUint8(uint8(a.FuncID)) | |
| writeUint8(uint8(a.FuncFlag)) | |
| writeUint8(0) // pad to uint32 boundary | |
| writeUint8(0) | |
| writeSymRef(a.Pcsp) | |
| writeSymRef(a.Pcfile) | |
| writeSymRef(a.Pcline) | |
| writeSymRef(a.Pcinline) | |
| writeUint32(uint32(len(a.Pcdata))) | |
| for _, sym := range a.Pcdata { | |
| writeSymRef(sym) | |
| } | |
| writeUint32(uint32(len(a.Funcdataoff))) | |
| for _, x := range a.Funcdataoff { | |
| writeUint32(x) | |
| } | |
| writeUint32(uint32(len(a.File))) | |
| for _, f := range a.File { | |
| writeUint32(uint32(f)) | |
| } | |
| writeUint32(uint32(len(a.InlTree))) | |
| for i := range a.InlTree { | |
| a.InlTree[i].Write(w) | |
| } | |
| } | |
| // FuncInfoLengths is a cache containing a roadmap of offsets and | |
| // lengths for things within a serialized FuncInfo. Each length field | |
| // stores the number of items (e.g. files, inltree nodes, etc), and the | |
| // corresponding "off" field stores the byte offset of the start of | |
| // the items in question. | |
| type FuncInfoLengths struct { | |
| NumPcdata uint32 | |
| PcdataOff uint32 | |
| NumFuncdataoff uint32 | |
| FuncdataoffOff uint32 | |
| NumFile uint32 | |
| FileOff uint32 | |
| NumInlTree uint32 | |
| InlTreeOff uint32 | |
| Initialized bool | |
| } | |
| func (*FuncInfo) ReadFuncInfoLengths(b []byte) FuncInfoLengths { | |
| var result FuncInfoLengths | |
| // 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 + 8*result.NumPcdata | |
| result.NumFuncdataoff = binary.LittleEndian.Uint32(b[numfuncdataoffOff:]) | |
| result.FuncdataoffOff = numfuncdataoffOff + 4 | |
| numfileOff := result.FuncdataoffOff + 4*result.NumFuncdataoff | |
| result.NumFile = binary.LittleEndian.Uint32(b[numfileOff:]) | |
| result.FileOff = numfileOff + 4 | |
| numinltreeOff := result.FileOff + 4*result.NumFile | |
| result.NumInlTree = binary.LittleEndian.Uint32(b[numinltreeOff:]) | |
| result.InlTreeOff = numinltreeOff + 4 | |
| result.Initialized = true | |
| return result | |
| } | |
| func (*FuncInfo) ReadArgs(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } | |
| func (*FuncInfo) ReadLocals(b []byte) uint32 { return binary.LittleEndian.Uint32(b[4:]) } | |
| func (*FuncInfo) ReadFuncID(b []byte) objabi.FuncID { return objabi.FuncID(b[8]) } | |
| func (*FuncInfo) ReadFuncFlag(b []byte) objabi.FuncFlag { return objabi.FuncFlag(b[9]) } | |
| func (*FuncInfo) ReadPcsp(b []byte) SymRef { | |
| return SymRef{binary.LittleEndian.Uint32(b[12:]), binary.LittleEndian.Uint32(b[16:])} | |
| } | |
| func (*FuncInfo) ReadPcfile(b []byte) SymRef { | |
| return SymRef{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:])} | |
| } | |
| func (*FuncInfo) ReadPcinline(b []byte) SymRef { | |
| return SymRef{binary.LittleEndian.Uint32(b[36:]), binary.LittleEndian.Uint32(b[40:])} | |
| } | |
| 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 { | |
| return int64(binary.LittleEndian.Uint32(b[funcdataofffoff+4*k:])) | |
| } | |
| func (*FuncInfo) ReadFile(b []byte, filesoff uint32, k uint32) CUFileIndex { | |
| return CUFileIndex(binary.LittleEndian.Uint32(b[filesoff+4*k:])) | |
| } | |
| func (*FuncInfo) ReadInlTree(b []byte, inltreeoff uint32, k uint32) InlTreeNode { | |
| const inlTreeNodeSize = 4 * 6 | |
| var result InlTreeNode | |
| result.Read(b[inltreeoff+k*inlTreeNodeSize:]) | |
| return result | |
| } | |
| // InlTreeNode is the serialized form of FileInfo.InlTree. | |
| type InlTreeNode struct { | |
| Parent int32 | |
| File CUFileIndex | |
| Line int32 | |
| Func SymRef | |
| ParentPC int32 | |
| } | |
| func (inl *InlTreeNode) Write(w *bytes.Buffer) { | |
| var b [4]byte | |
| writeUint32 := func(x uint32) { | |
| binary.LittleEndian.PutUint32(b[:], x) | |
| w.Write(b[:]) | |
| } | |
| writeUint32(uint32(inl.Parent)) | |
| writeUint32(uint32(inl.File)) | |
| writeUint32(uint32(inl.Line)) | |
| writeUint32(inl.Func.PkgIdx) | |
| writeUint32(inl.Func.SymIdx) | |
| writeUint32(uint32(inl.ParentPC)) | |
| } | |
| // Read an InlTreeNode from b, return the remaining bytes. | |
| func (inl *InlTreeNode) Read(b []byte) []byte { | |
| readUint32 := func() uint32 { | |
| x := binary.LittleEndian.Uint32(b) | |
| b = b[4:] | |
| return x | |
| } | |
| inl.Parent = int32(readUint32()) | |
| inl.File = CUFileIndex(readUint32()) | |
| inl.Line = int32(readUint32()) | |
| inl.Func = SymRef{readUint32(), readUint32()} | |
| inl.ParentPC = int32(readUint32()) | |
| return b | |
| } |