/** * Copyright 2023 ByteDance Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package loader import ( `encoding` `encoding/binary` `fmt` `reflect` `strings` `sync` `unsafe` ) const ( _MinLC uint8 = 1 _PtrSize uint8 = 8 ) const ( _N_FUNCDATA = 8 _INVALID_FUNCDATA_OFFSET = ^uint32(0) _FUNC_SIZE = unsafe.Sizeof(_func{}) _MINFUNC = 16 // minimum size for a function _BUCKETSIZE = 256 * _MINFUNC _SUBBUCKETS = 16 _SUB_BUCKETSIZE = _BUCKETSIZE / _SUBBUCKETS ) // Note: This list must match the list in runtime/symtab.go. const ( FuncFlag_TOPFRAME = 1 << iota FuncFlag_SPWRITE FuncFlag_ASM ) // PCDATA and FUNCDATA table indexes. // // See funcdata.h and $GROOT/src/cmd/internal/objabi/funcdata.go. const ( _FUNCDATA_ArgsPointerMaps = 0 _FUNCDATA_LocalsPointerMaps = 1 _FUNCDATA_StackObjects = 2 _FUNCDATA_InlTree = 3 _FUNCDATA_OpenCodedDeferInfo = 4 _FUNCDATA_ArgInfo = 5 _FUNCDATA_ArgLiveInfo = 6 _FUNCDATA_WrapInfo = 7 // ArgsSizeUnknown is set in Func.argsize to mark all functions // whose argument size is unknown (C vararg functions, and // assembly code without an explicit specification). // This value is generated by the compiler, assembler, or linker. ArgsSizeUnknown = -0x80000000 ) // moduledata used to cache the funcdata and findfuncbucket of one module var moduleCache = struct { m map[*moduledata][]byte sync.Mutex }{ m: make(map[*moduledata][]byte), } // Func contains information about a function. type Func struct { ID uint8 // see runtime/symtab.go Flag uint8 // see runtime/symtab.go ArgsSize int32 // args byte size EntryOff uint32 // start pc, offset to moduledata.text TextSize uint32 // size of func text DeferReturn uint32 // offset of start of a deferreturn call instruction from entry, if any. FileIndex uint32 // index into filetab Name string // name of function // PC data Pcsp *Pcdata // PC -> SP delta Pcfile *Pcdata // PC -> file index Pcline *Pcdata // PC -> line number PcUnsafePoint *Pcdata // PC -> unsafe point, must be PCDATA_UnsafePointSafe or PCDATA_UnsafePointUnsafe PcStackMapIndex *Pcdata // PC -> stack map index, relative to ArgsPointerMaps and LocalsPointerMaps PcInlTreeIndex *Pcdata // PC -> inlining tree index, relative to InlTree PcArgLiveIndex *Pcdata // PC -> arg live index, relative to ArgLiveInfo // Func data, must implement encoding.BinaryMarshaler ArgsPointerMaps encoding.BinaryMarshaler // concrete type: *StackMap LocalsPointerMaps encoding.BinaryMarshaler // concrete type: *StackMap StackObjects encoding.BinaryMarshaler InlTree encoding.BinaryMarshaler OpenCodedDeferInfo encoding.BinaryMarshaler ArgInfo encoding.BinaryMarshaler ArgLiveInfo encoding.BinaryMarshaler WrapInfo encoding.BinaryMarshaler } func getOffsetOf(data interface{}, field string) uintptr { t := reflect.TypeOf(data) fv, ok := t.FieldByName(field) if !ok { panic(fmt.Sprintf("field %s not found in struct %s", field, t.Name())) } return fv.Offset } func rnd(v int64, r int64) int64 { if r <= 0 { return v } v += r - 1 c := v % r if c < 0 { c += r } v -= c return v } var ( byteOrder binary.ByteOrder = binary.LittleEndian ) func funcNameParts(name string) (string, string, string) { i := strings.IndexByte(name, '[') if i < 0 { return name, "", "" } // TODO: use LastIndexByte once the bootstrap compiler is >= Go 1.5. j := len(name) - 1 for j > i && name[j] != ']' { j-- } if j <= i { return name, "", "" } return name[:i], "[...]", name[j+1:] } // func name table format: // nameOff[0] -> namePartA namePartB namePartC \x00 // nameOff[1] -> namePartA namePartB namePartC \x00 // ... func makeFuncnameTab(funcs []Func) (tab []byte, offs []int32) { offs = make([]int32, len(funcs)) offset := 1 tab = []byte{0} for i, f := range funcs { offs[i] = int32(offset) a, b, c := funcNameParts(f.Name) tab = append(tab, a...) tab = append(tab, b...) tab = append(tab, c...) tab = append(tab, 0) offset += len(a) + len(b) + len(c) + 1 } return } // CU table format: // cuOffsets[0] -> filetabOffset[0] filetabOffset[1] ... filetabOffset[len(CUs[0].fileNames)-1] // cuOffsets[1] -> filetabOffset[len(CUs[0].fileNames)] ... filetabOffset[len(CUs[0].fileNames) + len(CUs[1].fileNames)-1] // ... // // file name table format: // filetabOffset[0] -> CUs[0].fileNames[0] \x00 // ... // filetabOffset[len(CUs[0]-1)] -> CUs[0].fileNames[len(CUs[0].fileNames)-1] \x00 // ... // filetabOffset[SUM(CUs,fileNames)-1] -> CUs[len(CU)-1].fileNames[len(CUs[len(CU)-1].fileNames)-1] \x00 func makeFilenametab(cus []compilationUnit) (cutab []uint32, filetab []byte, cuOffsets []uint32) { cuOffsets = make([]uint32, len(cus)) cuOffset := 0 fileOffset := 0 for i, cu := range cus { cuOffsets[i] = uint32(cuOffset) for _, name := range cu.fileNames { cutab = append(cutab, uint32(fileOffset)) fileOffset += len(name) + 1 filetab = append(filetab, name...) filetab = append(filetab, 0) } cuOffset += len(cu.fileNames) } return } func writeFuncdata(out *[]byte, funcs []Func) (fstart int, funcdataOffs [][]uint32) { fstart = len(*out) *out = append(*out, byte(0)) offs := uint32(1) funcdataOffs = make([][]uint32, len(funcs)) for i, f := range funcs { var writer = func(fd encoding.BinaryMarshaler) { var ab []byte var err error if fd != nil { ab, err = fd.MarshalBinary() if err != nil { panic(err) } funcdataOffs[i] = append(funcdataOffs[i], offs) } else { ab = []byte{0} funcdataOffs[i] = append(funcdataOffs[i], _INVALID_FUNCDATA_OFFSET) } *out = append(*out, ab...) offs += uint32(len(ab)) } writer(f.ArgsPointerMaps) writer(f.LocalsPointerMaps) writer(f.StackObjects) writer(f.InlTree) writer(f.OpenCodedDeferInfo) writer(f.ArgInfo) writer(f.ArgLiveInfo) writer(f.WrapInfo) } return }