| // 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 |
| |
| Pcsp uint32 |
| Pcfile uint32 |
| Pcline uint32 |
| Pcinline uint32 |
| Pcdata []uint32 |
| PcdataEnd uint32 |
| Funcdataoff []uint32 |
| File []CUFileIndex |
| |
| InlTree []InlTreeNode |
| } |
| |
| func (a *FuncInfo) Write(w *bytes.Buffer) { |
| var b [4]byte |
| writeUint32 := func(x uint32) { |
| binary.LittleEndian.PutUint32(b[:], x) |
| w.Write(b[:]) |
| } |
| |
| writeUint32(a.Args) |
| writeUint32(a.Locals) |
| writeUint32(uint32(a.FuncID)) |
| |
| writeUint32(a.Pcsp) |
| writeUint32(a.Pcfile) |
| writeUint32(a.Pcline) |
| writeUint32(a.Pcinline) |
| writeUint32(uint32(len(a.Pcdata))) |
| for _, x := range a.Pcdata { |
| writeUint32(x) |
| } |
| writeUint32(a.PcdataEnd) |
| 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) |
| } |
| } |
| |
| func (a *FuncInfo) Read(b []byte) { |
| readUint32 := func() uint32 { |
| x := binary.LittleEndian.Uint32(b) |
| b = b[4:] |
| return x |
| } |
| |
| 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) |
| for i := range a.Pcdata { |
| a.Pcdata[i] = readUint32() |
| } |
| a.PcdataEnd = readUint32() |
| funcdataofflen := readUint32() |
| a.Funcdataoff = make([]uint32, funcdataofflen) |
| for i := range a.Funcdataoff { |
| a.Funcdataoff[i] = readUint32() |
| } |
| filelen := readUint32() |
| a.File = make([]CUFileIndex, filelen) |
| for i := range a.File { |
| a.File[i] = CUFileIndex(readUint32()) |
| } |
| inltreelen := readUint32() |
| a.InlTree = make([]InlTreeNode, inltreelen) |
| for i := range a.InlTree { |
| b = a.InlTree[i].Read(b) |
| } |
| } |
| |
| // 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 |
| |
| const numpcdataOff = 28 |
| result.NumPcdata = binary.LittleEndian.Uint32(b[numpcdataOff:]) |
| result.PcdataOff = numpcdataOff + 4 |
| |
| numfuncdataoffOff := result.PcdataOff + 4*(result.NumPcdata+1) |
| 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) 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:]) |
| } |
| |
| // return start and end offsets. |
| func (*FuncInfo) ReadPcfile(b []byte) (uint32, uint32) { |
| return binary.LittleEndian.Uint32(b[16:]), binary.LittleEndian.Uint32(b[20:]) |
| } |
| |
| // return start and end offsets. |
| func (*FuncInfo) ReadPcline(b []byte) (uint32, uint32) { |
| return binary.LittleEndian.Uint32(b[20:]), binary.LittleEndian.Uint32(b[24:]) |
| } |
| |
| // 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:]) |
| } |
| |
| // 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) 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 |
| } |