diff options
Diffstat (limited to 'vendor/github.com/bytedance/sonic/loader')
11 files changed, 2209 insertions, 0 deletions
diff --git a/vendor/github.com/bytedance/sonic/loader/funcdata.go b/vendor/github.com/bytedance/sonic/loader/funcdata.go new file mode 100644 index 000000000..9b760f615 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/funcdata.go @@ -0,0 +1,144 @@ +/** + * 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 +) + +// 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:] +}
\ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/loader/funcdata_go115.go b/vendor/github.com/bytedance/sonic/loader/funcdata_go115.go new file mode 100644 index 000000000..f35f03d56 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/funcdata_go115.go @@ -0,0 +1,541 @@ +// go:build go1.15 && !go1.18 +// +build go1.15,!go1.18 + +/* + * Copyright 2021 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` + `os` + `unsafe` + + `github.com/bytedance/sonic/internal/rt` +) + +const ( + _Magic uint32 = 0xfffffff0 +) + +type pcHeader struct { + magic uint32 // 0xFFFFFFF0 + 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 + textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text + 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 variable from pcHeader + pclnOffset uintptr // offset to the pclntab variable from pcHeader +} + +type moduledata struct { + pcHeader *pcHeader + funcnametab []byte + cutab []uint32 + filetab []byte + pctab []byte + pclntable []byte + ftab []funcTab + findfunctab uintptr + minpc, maxpc uintptr // first func address, last func address + last func size + + text, etext uintptr // start/end of text, (etext-text) must be greater than MIN_FUNC + noptrdata, enoptrdata uintptr + data, edata uintptr + bss, ebss uintptr + noptrbss, enoptrbss uintptr + end, gcdata, gcbss uintptr + types, etypes uintptr + rodata uintptr + gofunc uintptr // go.func.* is actual funcinfo object in image + + textsectmap []textSection // see runtime/symtab.go: textAddr() + typelinks []int32 // offsets from types + itablinks []*rt.GoItab + + ptab []ptabEntry + + pluginpath string + pkghashes []modulehash + + modulename string + modulehashes []modulehash + + hasmain uint8 // 1 if module contains the main function, 0 otherwise + + gcdatamask, gcbssmask bitVector + + typemap map[int32]*rt.GoType // offset to *_rtype in previous module + + bad bool // module failed to load and should be ignored + + next *moduledata +} + +type _func struct { + entryOff uint32 // start pc, as offset from moduledata.text/pcHeader.textStart + nameOff int32 // function name, as index into moduledata.funcnametab. + + args int32 // in/out args size + deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any. + + pcsp uint32 + pcfile uint32 + pcln uint32 + npcdata uint32 + cuOffset uint32 // runtime.cutab offset of this function's CU + funcID uint8 // set for certain special runtime functions + flag uint8 + _ [1]byte // pad + nfuncdata uint8 // + + // The end of the struct is followed immediately by two variable-length + // arrays that reference the pcdata and funcdata locations for this + // function. + + // pcdata contains the offset into moduledata.pctab for the start of + // that index's table. e.g., + // &moduledata.pctab[_func.pcdata[_PCDATA_UnsafePoint]] is the start of + // the unsafe point table. + // + // An offset of 0 indicates that there is no table. + // + // pcdata [npcdata]uint32 + + // funcdata contains the offset past moduledata.gofunc which contains a + // pointer to that index's funcdata. e.g., + // *(moduledata.gofunc + _func.funcdata[_FUNCDATA_ArgsPointerMaps]) is + // the argument pointer map. + // + // An offset of ^uint32(0) indicates that there is no entry. + // + // funcdata [nfuncdata]uint32 +} + +type funcTab struct { + entry uint32 + funcoff uint32 +} + +type bitVector struct { + n int32 // # of bits + bytedata *uint8 +} + +type ptabEntry struct { + name int32 + typ int32 +} + +type textSection struct { + vaddr uintptr // prelinked section vaddr + end uintptr // vaddr + section length + baseaddr uintptr // relocated section address +} + +type modulehash struct { + modulename string + linktimehash string + runtimehash *string +} + +// findfuncbucket is an array of these structures. +// Each bucket represents 4096 bytes of the text segment. +// Each subbucket represents 256 bytes of the text segment. +// To find a function given a pc, locate the bucket and subbucket for +// that pc. Add together the idx and subbucket value to obtain a +// function index. Then scan the functab array starting at that +// index to find the target function. +// This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead. +type findfuncbucket struct { + idx uint32 + _SUBBUCKETS [16]byte +} + +// 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 := 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 +} + +type compilationUnit struct { + fileNames []string +} + +// 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 +} + +func makeFtab(funcs []_func, lastFuncSize uint32) (ftab []funcTab) { + // Allocate space for the pc->func table. This structure consists of a pc offset + // 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. + var size int64 = int64(len(funcs)*2*4 + 4) + var startLocations = make([]uint32, len(funcs)) + for i, f := range funcs { + size = rnd(size, int64(_PtrSize)) + //writePCToFunc + startLocations[i] = uint32(size) + size += int64(uint8(_FUNC_SIZE)+f.nfuncdata*4+uint8(f.npcdata)*4) + } + + ftab = make([]funcTab, 0, len(funcs)+1) + + // write a map of pc->func info offsets + for i, f := range funcs { + ftab = append(ftab, funcTab{uint32(f.entryOff), uint32(startLocations[i])}) + } + + // Final entry of table is just end pc offset. + lastFunc := funcs[len(funcs)-1] + ftab = append(ftab, funcTab{uint32(lastFunc.entryOff + lastFuncSize), 0}) + + return +} + +// Pcln table format: [...]funcTab + [...]_Func +func makePclntable(funcs []_func, lastFuncSize uint32, pcdataOffs [][]uint32, funcdataOffs [][]uint32) (pclntab []byte) { + // Allocate space for the pc->func table. This structure consists of a pc offset + // 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. + var size int64 = int64(len(funcs)*2*4 + 4) + var startLocations = make([]uint32, len(funcs)) + for i := range funcs { + size = rnd(size, int64(_PtrSize)) + //writePCToFunc + startLocations[i] = uint32(size) + size += int64(int(_FUNC_SIZE)+len(funcdataOffs[i])*4+len(pcdataOffs[i])*4) + } + + pclntab = make([]byte, size, size) + + // write a map of pc->func info offsets + offs := 0 + for i, f := range funcs { + byteOrder.PutUint32(pclntab[offs:offs+4], uint32(f.entryOff)) + byteOrder.PutUint32(pclntab[offs+4:offs+8], uint32(startLocations[i])) + offs += 8 + } + // Final entry of table is just end pc offset. + lastFunc := funcs[len(funcs)-1] + byteOrder.PutUint32(pclntab[offs:offs+4], uint32(lastFunc.entryOff+lastFuncSize)) + + // write func info table + for i, f := range funcs { + off := startLocations[i] + + // write _func structure to pclntab + fb := rt.BytesFrom(unsafe.Pointer(&f), int(_FUNC_SIZE), int(_FUNC_SIZE)) + copy(pclntab[off:off+uint32(_FUNC_SIZE)], fb) + off += uint32(_FUNC_SIZE) + + // NOTICE: _func.pcdata always starts from PcUnsafePoint, which is index 3 + for j := 3; j < len(pcdataOffs[i]); j++ { + byteOrder.PutUint32(pclntab[off:off+4], uint32(pcdataOffs[i][j])) + off += 4 + } + + // funcdata refs as offsets from gofunc + for _, funcdata := range funcdataOffs[i] { + byteOrder.PutUint32(pclntab[off:off+4], uint32(funcdata)) + off += 4 + } + + } + + return +} + +// findfunc table used to map pc to belonging func, +// returns the index in the func table. +// +// All text section are divided into buckets sized _BUCKETSIZE(4K): +// every bucket is divided into _SUBBUCKETS sized _SUB_BUCKETSIZE(64), +// and it has a base idx to plus the offset stored in jth subbucket. +// see findfunc() in runtime/symtab.go +func writeFindfunctab(out *[]byte, ftab []funcTab) (start int) { + start = len(*out) + + max := ftab[len(ftab)-1].entry + min := ftab[0].entry + nbuckets := (max - min + _BUCKETSIZE - 1) / _BUCKETSIZE + n := (max - min + _SUB_BUCKETSIZE - 1) / _SUB_BUCKETSIZE + + tab := make([]findfuncbucket, 0, nbuckets) + var s, e = 0, 0 + for i := 0; i<int(nbuckets); i++ { + var pc = min + uint32((i+1)*_BUCKETSIZE) + // find the end func of the bucket + for ; e < len(ftab)-1 && ftab[e+1].entry <= pc; e++ {} + // store the start func of the bucket + var fb = findfuncbucket{idx: uint32(s)} + + for j := 0; j<_SUBBUCKETS && (i*_SUBBUCKETS+j)<int(n); j++ { + pc = min + uint32(i*_BUCKETSIZE) + uint32((j+1)*_SUB_BUCKETSIZE) + var ss = s + // find the end func of the subbucket + for ; ss < len(ftab)-1 && ftab[ss+1].entry <= pc; ss++ {} + // store the start func of the subbucket + fb._SUBBUCKETS[j] = byte(uint32(s) - fb.idx) + s = ss + } + s = e + tab = append(tab, fb) + } + + // write findfuncbucket + if len(tab) > 0 { + size := int(unsafe.Sizeof(findfuncbucket{}))*len(tab) + *out = append(*out, rt.BytesFrom(unsafe.Pointer(&tab[0]), size, size)...) + } + return +} + +func makeModuledata(name string, filenames []string, funcs []Func, text []byte) (mod *moduledata) { + mod = new(moduledata) + mod.modulename = name + + // make filename table + cu := make([]string, 0, len(filenames)) + for _, f := range filenames { + cu = append(cu, f) + } + cutab, filetab, cuOffs := makeFilenametab([]compilationUnit{{cu}}) + mod.cutab = cutab + mod.filetab = filetab + + // make funcname table + funcnametab, nameOffs := makeFuncnameTab(funcs) + mod.funcnametab = funcnametab + + // make pcdata table + // NOTICE: _func only use offset to index pcdata, thus no need mmap() pcdata + pctab, pcdataOffs, _funcs := makePctab(funcs, cuOffs, nameOffs) + mod.pctab = pctab + + // write func data + // NOTICE: _func use mod.gofunc+offset to directly point funcdata, thus need cache funcdata + // TODO: estimate accurate capacity + cache := make([]byte, 0, len(funcs)*int(_PtrSize)) + fstart, funcdataOffs := writeFuncdata(&cache, funcs) + + // make pc->func (binary search) func table + lastFuncsize := funcs[len(funcs)-1].TextSize + ftab := makeFtab(_funcs, lastFuncsize) + mod.ftab = ftab + + // write pc->func (modmap) findfunc table + ffstart := writeFindfunctab(&cache, ftab) + + // make pclnt table + pclntab := makePclntable(_funcs, lastFuncsize, pcdataOffs, funcdataOffs) + mod.pclntable = pclntab + + // mmap() text and funcdata segements + p := os.Getpagesize() + size := int(rnd(int64(len(text)), int64(p))) + addr := mmap(size) + // copy the machine code + s := rt.BytesFrom(unsafe.Pointer(addr), len(text), size) + copy(s, text) + // make it executable + mprotect(addr, size) + + // assign addresses + mod.text = addr + mod.etext = addr + uintptr(size) + mod.minpc = addr + mod.maxpc = addr + uintptr(len(text)) + + // cache funcdata and findfuncbucket + moduleCache.Lock() + moduleCache.m[mod] = cache + moduleCache.Unlock() + mod.gofunc = uintptr(unsafe.Pointer(&cache[fstart])) + mod.findfunctab = uintptr(unsafe.Pointer(&cache[ffstart])) + + // make pc header + mod.pcHeader = &pcHeader { + magic : _Magic, + minLC : _MinLC, + ptrSize : _PtrSize, + nfunc : len(funcs), + nfiles: uint(len(cu)), + textStart: mod.text, + funcnameOffset: getOffsetOf(moduledata{}, "funcnametab"), + cuOffset: getOffsetOf(moduledata{}, "cutab"), + filetabOffset: getOffsetOf(moduledata{}, "filetab"), + pctabOffset: getOffsetOf(moduledata{}, "pctab"), + pclnOffset: getOffsetOf(moduledata{}, "pclntable"), + } + + // sepecial case: gcdata and gcbss must by non-empty + mod.gcdata = uintptr(unsafe.Pointer(&emptyByte)) + mod.gcbss = uintptr(unsafe.Pointer(&emptyByte)) + + return +} + +// makePctab generates pcdelta->valuedelta tables for functions, +// and returns the table and the entry offset of every kind pcdata in the table. +func makePctab(funcs []Func, cuOffset []uint32, nameOffset []int32) (pctab []byte, pcdataOffs [][]uint32, _funcs []_func) { + _funcs = make([]_func, len(funcs)) + + // 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. + pctab = make([]byte, 1, 12*len(funcs)+1) + pcdataOffs = make([][]uint32, len(funcs)) + + for i, f := range funcs { + _f := &_funcs[i] + + var writer = func(pc *Pcdata) { + var ab []byte + var err error + if pc != nil { + ab, err = pc.MarshalBinary() + if err != nil { + panic(err) + } + pcdataOffs[i] = append(pcdataOffs[i], uint32(len(pctab))) + } else { + ab = []byte{0} + pcdataOffs[i] = append(pcdataOffs[i], _PCDATA_INVALID_OFFSET) + } + pctab = append(pctab, ab...) + } + + if f.Pcsp != nil { + _f.pcsp = uint32(len(pctab)) + } + writer(f.Pcsp) + if f.Pcfile != nil { + _f.pcfile = uint32(len(pctab)) + } + writer(f.Pcfile) + if f.Pcline != nil { + _f.pcln = uint32(len(pctab)) + } + writer(f.Pcline) + writer(f.PcUnsafePoint) + writer(f.PcStackMapIndex) + writer(f.PcInlTreeIndex) + writer(f.PcArgLiveIndex) + + _f.entryOff = f.EntryOff + _f.nameOff = nameOffset[i] + _f.args = f.ArgsSize + _f.deferreturn = f.DeferReturn + // NOTICE: _func.pcdata is always as [PCDATA_UnsafePoint(0) : PCDATA_ArgLiveIndex(3)] + _f.npcdata = uint32(_N_PCDATA) + _f.cuOffset = cuOffset[i] + _f.funcID = f.ID + _f.flag = f.Flag + _f.nfuncdata = uint8(_N_FUNCDATA) + } + + return +} + +func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {}
\ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/loader/funcdata_go118.go b/vendor/github.com/bytedance/sonic/loader/funcdata_go118.go new file mode 100644 index 000000000..a2bac857a --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/funcdata_go118.go @@ -0,0 +1,541 @@ +// go:build go1.18 && !go1.20 +// +build go1.18,!go1.20 + +/* + * Copyright 2021 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` + `os` + `unsafe` + + `github.com/bytedance/sonic/internal/rt` +) + +const ( + _Magic uint32 = 0xfffffff0 +) + +type pcHeader struct { + magic uint32 // 0xFFFFFFF0 + 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 + textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text + 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 variable from pcHeader + pclnOffset uintptr // offset to the pclntab variable from pcHeader +} + +type moduledata struct { + pcHeader *pcHeader + funcnametab []byte + cutab []uint32 + filetab []byte + pctab []byte + pclntable []byte + ftab []funcTab + findfunctab uintptr + minpc, maxpc uintptr // first func address, last func address + last func size + + text, etext uintptr // start/end of text, (etext-text) must be greater than MIN_FUNC + noptrdata, enoptrdata uintptr + data, edata uintptr + bss, ebss uintptr + noptrbss, enoptrbss uintptr + end, gcdata, gcbss uintptr + types, etypes uintptr + rodata uintptr + gofunc uintptr // go.func.* is actual funcinfo object in image + + textsectmap []textSection // see runtime/symtab.go: textAddr() + typelinks []int32 // offsets from types + itablinks []*rt.GoItab + + ptab []ptabEntry + + pluginpath string + pkghashes []modulehash + + modulename string + modulehashes []modulehash + + hasmain uint8 // 1 if module contains the main function, 0 otherwise + + gcdatamask, gcbssmask bitVector + + typemap map[int32]*rt.GoType // offset to *_rtype in previous module + + bad bool // module failed to load and should be ignored + + next *moduledata +} + +type _func struct { + entryOff uint32 // start pc, as offset from moduledata.text/pcHeader.textStart + nameOff int32 // function name, as index into moduledata.funcnametab. + + args int32 // in/out args size + deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any. + + pcsp uint32 + pcfile uint32 + pcln uint32 + npcdata uint32 + cuOffset uint32 // runtime.cutab offset of this function's CU + funcID uint8 // set for certain special runtime functions + flag uint8 + _ [1]byte // pad + nfuncdata uint8 // + + // The end of the struct is followed immediately by two variable-length + // arrays that reference the pcdata and funcdata locations for this + // function. + + // pcdata contains the offset into moduledata.pctab for the start of + // that index's table. e.g., + // &moduledata.pctab[_func.pcdata[_PCDATA_UnsafePoint]] is the start of + // the unsafe point table. + // + // An offset of 0 indicates that there is no table. + // + // pcdata [npcdata]uint32 + + // funcdata contains the offset past moduledata.gofunc which contains a + // pointer to that index's funcdata. e.g., + // *(moduledata.gofunc + _func.funcdata[_FUNCDATA_ArgsPointerMaps]) is + // the argument pointer map. + // + // An offset of ^uint32(0) indicates that there is no entry. + // + // funcdata [nfuncdata]uint32 +} + +type funcTab struct { + entry uint32 + funcoff uint32 +} + +type bitVector struct { + n int32 // # of bits + bytedata *uint8 +} + +type ptabEntry struct { + name int32 + typ int32 +} + +type textSection struct { + vaddr uintptr // prelinked section vaddr + end uintptr // vaddr + section length + baseaddr uintptr // relocated section address +} + +type modulehash struct { + modulename string + linktimehash string + runtimehash *string +} + +// findfuncbucket is an array of these structures. +// Each bucket represents 4096 bytes of the text segment. +// Each subbucket represents 256 bytes of the text segment. +// To find a function given a pc, locate the bucket and subbucket for +// that pc. Add together the idx and subbucket value to obtain a +// function index. Then scan the functab array starting at that +// index to find the target function. +// This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead. +type findfuncbucket struct { + idx uint32 + _SUBBUCKETS [16]byte +} + +// 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 := 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 +} + +type compilationUnit struct { + fileNames []string +} + +// 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 +} + +func makeFtab(funcs []_func, lastFuncSize uint32) (ftab []funcTab) { + // Allocate space for the pc->func table. This structure consists of a pc offset + // 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. + var size int64 = int64(len(funcs)*2*4 + 4) + var startLocations = make([]uint32, len(funcs)) + for i, f := range funcs { + size = rnd(size, int64(_PtrSize)) + //writePCToFunc + startLocations[i] = uint32(size) + size += int64(uint8(_FUNC_SIZE)+f.nfuncdata*4+uint8(f.npcdata)*4) + } + + ftab = make([]funcTab, 0, len(funcs)+1) + + // write a map of pc->func info offsets + for i, f := range funcs { + ftab = append(ftab, funcTab{uint32(f.entryOff), uint32(startLocations[i])}) + } + + // Final entry of table is just end pc offset. + lastFunc := funcs[len(funcs)-1] + ftab = append(ftab, funcTab{uint32(lastFunc.entryOff + lastFuncSize), 0}) + + return +} + +// Pcln table format: [...]funcTab + [...]_Func +func makePclntable(funcs []_func, lastFuncSize uint32, pcdataOffs [][]uint32, funcdataOffs [][]uint32) (pclntab []byte) { + // Allocate space for the pc->func table. This structure consists of a pc offset + // 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. + var size int64 = int64(len(funcs)*2*4 + 4) + var startLocations = make([]uint32, len(funcs)) + for i := range funcs { + size = rnd(size, int64(_PtrSize)) + //writePCToFunc + startLocations[i] = uint32(size) + size += int64(int(_FUNC_SIZE)+len(funcdataOffs[i])*4+len(pcdataOffs[i])*4) + } + + pclntab = make([]byte, size, size) + + // write a map of pc->func info offsets + offs := 0 + for i, f := range funcs { + byteOrder.PutUint32(pclntab[offs:offs+4], uint32(f.entryOff)) + byteOrder.PutUint32(pclntab[offs+4:offs+8], uint32(startLocations[i])) + offs += 8 + } + // Final entry of table is just end pc offset. + lastFunc := funcs[len(funcs)-1] + byteOrder.PutUint32(pclntab[offs:offs+4], uint32(lastFunc.entryOff+lastFuncSize)) + + // write func info table + for i, f := range funcs { + off := startLocations[i] + + // write _func structure to pclntab + fb := rt.BytesFrom(unsafe.Pointer(&f), int(_FUNC_SIZE), int(_FUNC_SIZE)) + copy(pclntab[off:off+uint32(_FUNC_SIZE)], fb) + off += uint32(_FUNC_SIZE) + + // NOTICE: _func.pcdata always starts from PcUnsafePoint, which is index 3 + for j := 3; j < len(pcdataOffs[i]); j++ { + byteOrder.PutUint32(pclntab[off:off+4], uint32(pcdataOffs[i][j])) + off += 4 + } + + // funcdata refs as offsets from gofunc + for _, funcdata := range funcdataOffs[i] { + byteOrder.PutUint32(pclntab[off:off+4], uint32(funcdata)) + off += 4 + } + + } + + return +} + +// findfunc table used to map pc to belonging func, +// returns the index in the func table. +// +// All text section are divided into buckets sized _BUCKETSIZE(4K): +// every bucket is divided into _SUBBUCKETS sized _SUB_BUCKETSIZE(64), +// and it has a base idx to plus the offset stored in jth subbucket. +// see findfunc() in runtime/symtab.go +func writeFindfunctab(out *[]byte, ftab []funcTab) (start int) { + start = len(*out) + + max := ftab[len(ftab)-1].entry + min := ftab[0].entry + nbuckets := (max - min + _BUCKETSIZE - 1) / _BUCKETSIZE + n := (max - min + _SUB_BUCKETSIZE - 1) / _SUB_BUCKETSIZE + + tab := make([]findfuncbucket, 0, nbuckets) + var s, e = 0, 0 + for i := 0; i<int(nbuckets); i++ { + var pc = min + uint32((i+1)*_BUCKETSIZE) + // find the end func of the bucket + for ; e < len(ftab)-1 && ftab[e+1].entry <= pc; e++ {} + // store the start func of the bucket + var fb = findfuncbucket{idx: uint32(s)} + + for j := 0; j<_SUBBUCKETS && (i*_SUBBUCKETS+j)<int(n); j++ { + pc = min + uint32(i*_BUCKETSIZE) + uint32((j+1)*_SUB_BUCKETSIZE) + var ss = s + // find the end func of the subbucket + for ; ss < len(ftab)-1 && ftab[ss+1].entry <= pc; ss++ {} + // store the start func of the subbucket + fb._SUBBUCKETS[j] = byte(uint32(s) - fb.idx) + s = ss + } + s = e + tab = append(tab, fb) + } + + // write findfuncbucket + if len(tab) > 0 { + size := int(unsafe.Sizeof(findfuncbucket{}))*len(tab) + *out = append(*out, rt.BytesFrom(unsafe.Pointer(&tab[0]), size, size)...) + } + return +} + +func makeModuledata(name string, filenames []string, funcs []Func, text []byte) (mod *moduledata) { + mod = new(moduledata) + mod.modulename = name + + // make filename table + cu := make([]string, 0, len(filenames)) + for _, f := range filenames { + cu = append(cu, f) + } + cutab, filetab, cuOffs := makeFilenametab([]compilationUnit{{cu}}) + mod.cutab = cutab + mod.filetab = filetab + + // make funcname table + funcnametab, nameOffs := makeFuncnameTab(funcs) + mod.funcnametab = funcnametab + + // make pcdata table + // NOTICE: _func only use offset to index pcdata, thus no need mmap() pcdata + pctab, pcdataOffs, _funcs := makePctab(funcs, cuOffs, nameOffs) + mod.pctab = pctab + + // write func data + // NOTICE: _func use mod.gofunc+offset to directly point funcdata, thus need cache funcdata + // TODO: estimate accurate capacity + cache := make([]byte, 0, len(funcs)*int(_PtrSize)) + fstart, funcdataOffs := writeFuncdata(&cache, funcs) + + // make pc->func (binary search) func table + lastFuncsize := funcs[len(funcs)-1].TextSize + ftab := makeFtab(_funcs, lastFuncsize) + mod.ftab = ftab + + // write pc->func (modmap) findfunc table + ffstart := writeFindfunctab(&cache, ftab) + + // make pclnt table + pclntab := makePclntable(_funcs, lastFuncsize, pcdataOffs, funcdataOffs) + mod.pclntable = pclntab + + // mmap() text and funcdata segements + p := os.Getpagesize() + size := int(rnd(int64(len(text)), int64(p))) + addr := mmap(size) + // copy the machine code + s := rt.BytesFrom(unsafe.Pointer(addr), len(text), size) + copy(s, text) + // make it executable + mprotect(addr, size) + + // assign addresses + mod.text = addr + mod.etext = addr + uintptr(size) + mod.minpc = addr + mod.maxpc = addr + uintptr(len(text)) + + // cache funcdata and findfuncbucket + moduleCache.Lock() + moduleCache.m[mod] = cache + moduleCache.Unlock() + mod.gofunc = uintptr(unsafe.Pointer(&cache[fstart])) + mod.findfunctab = uintptr(unsafe.Pointer(&cache[ffstart])) + + // make pc header + mod.pcHeader = &pcHeader { + magic : _Magic, + minLC : _MinLC, + ptrSize : _PtrSize, + nfunc : len(funcs), + nfiles: uint(len(cu)), + textStart: mod.text, + funcnameOffset: getOffsetOf(moduledata{}, "funcnametab"), + cuOffset: getOffsetOf(moduledata{}, "cutab"), + filetabOffset: getOffsetOf(moduledata{}, "filetab"), + pctabOffset: getOffsetOf(moduledata{}, "pctab"), + pclnOffset: getOffsetOf(moduledata{}, "pclntable"), + } + + // sepecial case: gcdata and gcbss must by non-empty + mod.gcdata = uintptr(unsafe.Pointer(&emptyByte)) + mod.gcbss = uintptr(unsafe.Pointer(&emptyByte)) + + return +} + +// makePctab generates pcdelta->valuedelta tables for functions, +// and returns the table and the entry offset of every kind pcdata in the table. +func makePctab(funcs []Func, cuOffset []uint32, nameOffset []int32) (pctab []byte, pcdataOffs [][]uint32, _funcs []_func) { + _funcs = make([]_func, len(funcs)) + + // 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. + pctab = make([]byte, 1, 12*len(funcs)+1) + pcdataOffs = make([][]uint32, len(funcs)) + + for i, f := range funcs { + _f := &_funcs[i] + + var writer = func(pc *Pcdata) { + var ab []byte + var err error + if pc != nil { + ab, err = pc.MarshalBinary() + if err != nil { + panic(err) + } + pcdataOffs[i] = append(pcdataOffs[i], uint32(len(pctab))) + } else { + ab = []byte{0} + pcdataOffs[i] = append(pcdataOffs[i], _PCDATA_INVALID_OFFSET) + } + pctab = append(pctab, ab...) + } + + if f.Pcsp != nil { + _f.pcsp = uint32(len(pctab)) + } + writer(f.Pcsp) + if f.Pcfile != nil { + _f.pcfile = uint32(len(pctab)) + } + writer(f.Pcfile) + if f.Pcline != nil { + _f.pcln = uint32(len(pctab)) + } + writer(f.Pcline) + writer(f.PcUnsafePoint) + writer(f.PcStackMapIndex) + writer(f.PcInlTreeIndex) + writer(f.PcArgLiveIndex) + + _f.entryOff = f.EntryOff + _f.nameOff = nameOffset[i] + _f.args = f.ArgsSize + _f.deferreturn = f.DeferReturn + // NOTICE: _func.pcdata is always as [PCDATA_UnsafePoint(0) : PCDATA_ArgLiveIndex(3)] + _f.npcdata = uint32(_N_PCDATA) + _f.cuOffset = cuOffset[i] + _f.funcID = f.ID + _f.flag = f.Flag + _f.nfuncdata = uint8(_N_FUNCDATA) + } + + return +} + +func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {}
\ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/loader/funcdata_go120.go b/vendor/github.com/bytedance/sonic/loader/funcdata_go120.go new file mode 100644 index 000000000..906fe375d --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/funcdata_go120.go @@ -0,0 +1,545 @@ +//go:build go1.20 && !go1.21 +// +build go1.20,!go1.21 + +/* + * Copyright 2021 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` + `os` + `unsafe` + + `github.com/bytedance/sonic/internal/rt` +) + +const ( + _Magic uint32 = 0xFFFFFFF1 +) + +type moduledata struct { + pcHeader *pcHeader + funcnametab []byte + cutab []uint32 + filetab []byte + pctab []byte + pclntable []byte + ftab []funcTab + findfunctab uintptr + minpc, maxpc uintptr // first func address, last func address + last func size + + text, etext uintptr // start/end of text, (etext-text) must be greater than MIN_FUNC + noptrdata, enoptrdata uintptr + data, edata uintptr + bss, ebss uintptr + noptrbss, enoptrbss uintptr + covctrs, ecovctrs uintptr + end, gcdata, gcbss uintptr + types, etypes uintptr + rodata uintptr + + // TODO: generate funcinfo object to memory + gofunc uintptr // go.func.* is actual funcinfo object in image + + textsectmap []textSection // see runtime/symtab.go: textAddr() + typelinks []int32 // offsets from types + itablinks []*rt.GoItab + + ptab []ptabEntry + + pluginpath string + pkghashes []modulehash + + modulename string + modulehashes []modulehash + + hasmain uint8 // 1 if module contains the main function, 0 otherwise + + gcdatamask, gcbssmask bitVector + + typemap map[int32]*rt.GoType // offset to *_rtype in previous module + + bad bool // module failed to load and should be ignored + + next *moduledata +} + +type _func struct { + entryOff uint32 // start pc, as offset from moduledata.text/pcHeader.textStart + nameOff int32 // function name, as index into moduledata.funcnametab. + + args int32 // in/out args size + deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any. + + pcsp uint32 + pcfile uint32 + pcln uint32 + npcdata uint32 + cuOffset uint32 // runtime.cutab offset of this function's CU + startLine int32 // line number of start of function (func keyword/TEXT directive) + funcID uint8 // set for certain special runtime functions + flag uint8 + _ [1]byte // pad + nfuncdata uint8 // + + // The end of the struct is followed immediately by two variable-length + // arrays that reference the pcdata and funcdata locations for this + // function. + + // pcdata contains the offset into moduledata.pctab for the start of + // that index's table. e.g., + // &moduledata.pctab[_func.pcdata[_PCDATA_UnsafePoint]] is the start of + // the unsafe point table. + // + // An offset of 0 indicates that there is no table. + // + // pcdata [npcdata]uint32 + + // funcdata contains the offset past moduledata.gofunc which contains a + // pointer to that index's funcdata. e.g., + // *(moduledata.gofunc + _func.funcdata[_FUNCDATA_ArgsPointerMaps]) is + // the argument pointer map. + // + // An offset of ^uint32(0) indicates that there is no entry. + // + // funcdata [nfuncdata]uint32 +} + +type funcTab struct { + entry uint32 + funcoff uint32 +} + +type pcHeader struct { + magic uint32 // 0xFFFFFFF0 + 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 + textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text + 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 variable from pcHeader + pclnOffset uintptr // offset to the pclntab variable from pcHeader +} + +type bitVector struct { + n int32 // # of bits + bytedata *uint8 +} + +type ptabEntry struct { + name int32 + typ int32 +} + +type textSection struct { + vaddr uintptr // prelinked section vaddr + end uintptr // vaddr + section length + baseaddr uintptr // relocated section address +} + +type modulehash struct { + modulename string + linktimehash string + runtimehash *string +} + +// findfuncbucket is an array of these structures. +// Each bucket represents 4096 bytes of the text segment. +// Each subbucket represents 256 bytes of the text segment. +// To find a function given a pc, locate the bucket and subbucket for +// that pc. Add together the idx and subbucket value to obtain a +// function index. Then scan the functab array starting at that +// index to find the target function. +// This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead. +type findfuncbucket struct { + idx uint32 + _SUBBUCKETS [16]byte +} + +// 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 := 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 +} + +type compilationUnit struct { + fileNames []string +} + +// 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 +} + +func makeFtab(funcs []_func, lastFuncSize uint32) (ftab []funcTab) { + // Allocate space for the pc->func table. This structure consists of a pc offset + // 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. + var size int64 = int64(len(funcs)*2*4 + 4) + var startLocations = make([]uint32, len(funcs)) + for i, f := range funcs { + size = rnd(size, int64(_PtrSize)) + //writePCToFunc + startLocations[i] = uint32(size) + size += int64(uint8(_FUNC_SIZE)+f.nfuncdata*4+uint8(f.npcdata)*4) + } + + ftab = make([]funcTab, 0, len(funcs)+1) + + // write a map of pc->func info offsets + for i, f := range funcs { + ftab = append(ftab, funcTab{uint32(f.entryOff), uint32(startLocations[i])}) + } + + // Final entry of table is just end pc offset. + lastFunc := funcs[len(funcs)-1] + ftab = append(ftab, funcTab{uint32(lastFunc.entryOff + lastFuncSize), 0}) + + return +} + +// Pcln table format: [...]funcTab + [...]_Func +func makePclntable(funcs []_func, lastFuncSize uint32, pcdataOffs [][]uint32, funcdataOffs [][]uint32) (pclntab []byte) { + // Allocate space for the pc->func table. This structure consists of a pc offset + // 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. + var size int64 = int64(len(funcs)*2*4 + 4) + var startLocations = make([]uint32, len(funcs)) + for i := range funcs { + size = rnd(size, int64(_PtrSize)) + //writePCToFunc + startLocations[i] = uint32(size) + size += int64(int(_FUNC_SIZE)+len(funcdataOffs[i])*4+len(pcdataOffs[i])*4) + } + + pclntab = make([]byte, size, size) + + // write a map of pc->func info offsets + offs := 0 + for i, f := range funcs { + byteOrder.PutUint32(pclntab[offs:offs+4], uint32(f.entryOff)) + byteOrder.PutUint32(pclntab[offs+4:offs+8], uint32(startLocations[i])) + offs += 8 + } + // Final entry of table is just end pc offset. + lastFunc := funcs[len(funcs)-1] + byteOrder.PutUint32(pclntab[offs:offs+4], uint32(lastFunc.entryOff+lastFuncSize)) + + // write func info table + for i, f := range funcs { + off := startLocations[i] + + // write _func structure to pclntab + fb := rt.BytesFrom(unsafe.Pointer(&f), int(_FUNC_SIZE), int(_FUNC_SIZE)) + copy(pclntab[off:off+uint32(_FUNC_SIZE)], fb) + off += uint32(_FUNC_SIZE) + + // NOTICE: _func.pcdata always starts from PcUnsafePoint, which is index 3 + for j := 3; j < len(pcdataOffs[i]); j++ { + byteOrder.PutUint32(pclntab[off:off+4], uint32(pcdataOffs[i][j])) + off += 4 + } + + // funcdata refs as offsets from gofunc + for _, funcdata := range funcdataOffs[i] { + byteOrder.PutUint32(pclntab[off:off+4], uint32(funcdata)) + off += 4 + } + + } + + return +} + +// findfunc table used to map pc to belonging func, +// returns the index in the func table. +// +// All text section are divided into buckets sized _BUCKETSIZE(4K): +// every bucket is divided into _SUBBUCKETS sized _SUB_BUCKETSIZE(64), +// and it has a base idx to plus the offset stored in jth subbucket. +// see findfunc() in runtime/symtab.go +func writeFindfunctab(out *[]byte, ftab []funcTab) (start int) { + start = len(*out) + + max := ftab[len(ftab)-1].entry + min := ftab[0].entry + nbuckets := (max - min + _BUCKETSIZE - 1) / _BUCKETSIZE + n := (max - min + _SUB_BUCKETSIZE - 1) / _SUB_BUCKETSIZE + + tab := make([]findfuncbucket, 0, nbuckets) + var s, e = 0, 0 + for i := 0; i<int(nbuckets); i++ { + var pc = min + uint32((i+1)*_BUCKETSIZE) + // find the end func of the bucket + for ; e < len(ftab)-1 && ftab[e+1].entry <= pc; e++ {} + // store the start func of the bucket + var fb = findfuncbucket{idx: uint32(s)} + + for j := 0; j<_SUBBUCKETS && (i*_SUBBUCKETS+j)<int(n); j++ { + pc = min + uint32(i*_BUCKETSIZE) + uint32((j+1)*_SUB_BUCKETSIZE) + var ss = s + // find the end func of the subbucket + for ; ss < len(ftab)-1 && ftab[ss+1].entry <= pc; ss++ {} + // store the start func of the subbucket + fb._SUBBUCKETS[j] = byte(uint32(s) - fb.idx) + s = ss + } + s = e + tab = append(tab, fb) + } + + // write findfuncbucket + if len(tab) > 0 { + size := int(unsafe.Sizeof(findfuncbucket{}))*len(tab) + *out = append(*out, rt.BytesFrom(unsafe.Pointer(&tab[0]), size, size)...) + } + return +} + +func makeModuledata(name string, filenames []string, funcs []Func, text []byte) (mod *moduledata) { + mod = new(moduledata) + mod.modulename = name + + // make filename table + cu := make([]string, 0, len(filenames)) + for _, f := range filenames { + cu = append(cu, f) + } + cutab, filetab, cuOffs := makeFilenametab([]compilationUnit{{cu}}) + mod.cutab = cutab + mod.filetab = filetab + + // make funcname table + funcnametab, nameOffs := makeFuncnameTab(funcs) + mod.funcnametab = funcnametab + + // make pcdata table + // NOTICE: _func only use offset to index pcdata, thus no need mmap() pcdata + pctab, pcdataOffs, _funcs := makePctab(funcs, cuOffs, nameOffs) + mod.pctab = pctab + + // write func data + // NOTICE: _func use mod.gofunc+offset to directly point funcdata, thus need cache funcdata + // TODO: estimate accurate capacity + cache := make([]byte, 0, len(funcs)*int(_PtrSize)) + fstart, funcdataOffs := writeFuncdata(&cache, funcs) + + // make pc->func (binary search) func table + lastFuncsize := funcs[len(funcs)-1].TextSize + ftab := makeFtab(_funcs, lastFuncsize) + mod.ftab = ftab + + // write pc->func (modmap) findfunc table + ffstart := writeFindfunctab(&cache, ftab) + + // make pclnt table + pclntab := makePclntable(_funcs, lastFuncsize, pcdataOffs, funcdataOffs) + mod.pclntable = pclntab + + // mmap() text and funcdata segements + p := os.Getpagesize() + size := int(rnd(int64(len(text)), int64(p))) + addr := mmap(size) + // copy the machine code + s := rt.BytesFrom(unsafe.Pointer(addr), len(text), size) + copy(s, text) + // make it executable + mprotect(addr, size) + + // assign addresses + mod.text = addr + mod.etext = addr + uintptr(size) + mod.minpc = addr + mod.maxpc = addr + uintptr(len(text)) + + // cache funcdata and findfuncbucket + moduleCache.Lock() + moduleCache.m[mod] = cache + moduleCache.Unlock() + mod.gofunc = uintptr(unsafe.Pointer(&cache[fstart])) + mod.findfunctab = uintptr(unsafe.Pointer(&cache[ffstart])) + + // make pc header + mod.pcHeader = &pcHeader { + magic : _Magic, + minLC : _MinLC, + ptrSize : _PtrSize, + nfunc : len(funcs), + nfiles: uint(len(cu)), + textStart: mod.text, + funcnameOffset: getOffsetOf(moduledata{}, "funcnametab"), + cuOffset: getOffsetOf(moduledata{}, "cutab"), + filetabOffset: getOffsetOf(moduledata{}, "filetab"), + pctabOffset: getOffsetOf(moduledata{}, "pctab"), + pclnOffset: getOffsetOf(moduledata{}, "pclntable"), + } + + // sepecial case: gcdata and gcbss must by non-empty + mod.gcdata = uintptr(unsafe.Pointer(&emptyByte)) + mod.gcbss = uintptr(unsafe.Pointer(&emptyByte)) + + return +} + +// makePctab generates pcdelta->valuedelta tables for functions, +// and returns the table and the entry offset of every kind pcdata in the table. +func makePctab(funcs []Func, cuOffset []uint32, nameOffset []int32) (pctab []byte, pcdataOffs [][]uint32, _funcs []_func) { + _funcs = make([]_func, len(funcs)) + + // 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. + pctab = make([]byte, 1, 12*len(funcs)+1) + pcdataOffs = make([][]uint32, len(funcs)) + + for i, f := range funcs { + _f := &_funcs[i] + + var writer = func(pc *Pcdata) { + var ab []byte + var err error + if pc != nil { + ab, err = pc.MarshalBinary() + if err != nil { + panic(err) + } + pcdataOffs[i] = append(pcdataOffs[i], uint32(len(pctab))) + } else { + ab = []byte{0} + pcdataOffs[i] = append(pcdataOffs[i], _PCDATA_INVALID_OFFSET) + } + pctab = append(pctab, ab...) + } + + if f.Pcsp != nil { + _f.pcsp = uint32(len(pctab)) + } + writer(f.Pcsp) + if f.Pcfile != nil { + _f.pcfile = uint32(len(pctab)) + } + writer(f.Pcfile) + if f.Pcline != nil { + _f.pcln = uint32(len(pctab)) + } + writer(f.Pcline) + writer(f.PcUnsafePoint) + writer(f.PcStackMapIndex) + writer(f.PcInlTreeIndex) + writer(f.PcArgLiveIndex) + + _f.entryOff = f.EntryOff + _f.nameOff = nameOffset[i] + _f.args = f.ArgsSize + _f.deferreturn = f.DeferReturn + // NOTICE: _func.pcdata is always as [PCDATA_UnsafePoint(0) : PCDATA_ArgLiveIndex(3)] + _f.npcdata = uint32(_N_PCDATA) + _f.cuOffset = cuOffset[i] + _f.funcID = f.ID + _f.flag = f.Flag + _f.nfuncdata = uint8(_N_FUNCDATA) + } + + return +} + +func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {}
\ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/loader/loader.go b/vendor/github.com/bytedance/sonic/loader/loader.go new file mode 100644 index 000000000..929d8c23d --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/loader.go @@ -0,0 +1,37 @@ +/** + * 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 ( + `unsafe` +) + +// Function is a function pointer +type Function unsafe.Pointer + +// Options used to load a module +type Options struct { + // NoPreempt is used to disable async preemption for this module + NoPreempt bool +} + +// Loader is a helper used to load a module simply +type Loader struct { + Name string // module name + File string // file name + Options +}
\ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/loader/loader_go115.go b/vendor/github.com/bytedance/sonic/loader/loader_go115.go new file mode 100644 index 000000000..1e44b36a6 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/loader_go115.go @@ -0,0 +1,33 @@ +//go:build go1.15 && !go1.18 +// +build go1.15,!go1.18 + +/* + * Copyright 2021 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 ( + + `github.com/bytedance/sonic/internal/loader` +) + +func (self Loader) LoadOne(text []byte, funcName string, frameSize int, argSize int, argStackmap []bool, localStackmap []bool) Function { + return Function(loader.Loader(text).Load(funcName, frameSize, argSize, argStackmap, localStackmap)) +} + +func Load(modulename string, filenames []string, funcs []Func, text []byte) (out []Function) { + panic("not implemented") +}
\ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/loader/loader_go118.go b/vendor/github.com/bytedance/sonic/loader/loader_go118.go new file mode 100644 index 000000000..9a0fe4843 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/loader_go118.go @@ -0,0 +1,104 @@ +//go:build go1.18 && !go1.21 +// +build go1.18,!go1.21 + +/* + * Copyright 2021 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 ( + `github.com/bytedance/sonic/internal/rt` +) + +// LoadFuncs loads only one function as module, and returns the function pointer +// - text: machine code +// - funcName: function name +// - frameSize: stack frame size. +// - argSize: argument total size (in bytes) +// - argPtrs: indicates if a slot (8 Bytes) of arguments memory stores pointer, from low to high +// - localPtrs: indicates if a slot (8 Bytes) of local variants memory stores pointer, from low to high +// +// WARN: +// - the function MUST has fixed SP offset equaling to this, otherwise it go.gentraceback will fail +// - the function MUST has only one stack map for all arguments and local variants +func (self Loader) LoadOne(text []byte, funcName string, frameSize int, argSize int, argPtrs []bool, localPtrs []bool) Function { + size := uint32(len(text)) + + fn := Func{ + Name: funcName, + TextSize: size, + ArgsSize: int32(argSize), + } + + // NOTICE: suppose the function has fixed SP offset equaling to frameSize, thus make only one pcsp pair + fn.Pcsp = &Pcdata{ + {PC: size, Val: int32(frameSize)}, + } + + if self.NoPreempt { + fn.PcUnsafePoint = &Pcdata{ + {PC: size, Val: PCDATA_UnsafePointUnsafe}, + } + } else { + fn.PcUnsafePoint = &Pcdata{ + {PC: size, Val: PCDATA_UnsafePointSafe}, + } + } + + // NOTICE: suppose the function has only one stack map at index 0 + fn.PcStackMapIndex = &Pcdata{ + {PC: size, Val: 0}, + } + + if argPtrs != nil { + args := rt.StackMapBuilder{} + for _, b := range argPtrs { + args.AddField(b) + } + fn.ArgsPointerMaps = args.Build() + } + + if localPtrs != nil { + locals := rt .StackMapBuilder{} + for _, b := range localPtrs { + locals.AddField(b) + } + fn.LocalsPointerMaps = locals.Build() + } + + out := Load(text, []Func{fn}, self.Name + funcName, []string{self.File}) + return out[0] +} + +// Load loads given machine codes and corresponding function information into go moduledata +// and returns runnable function pointer +// WARN: this API is experimental, use it carefully +func Load(text []byte, funcs []Func, modulename string, filenames []string) (out []Function) { + // generate module data and allocate memory address + mod := makeModuledata(modulename, filenames, funcs, text) + + // verify and register the new module + moduledataverify1(mod) + registerModule(mod) + + // encapsulate function address + out = make([]Function, len(funcs)) + for i, f := range funcs { + m := uintptr(mod.text + uintptr(f.EntryOff)) + out[i] = Function(&m) + } + return +}
\ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/loader/mmap_unix.go b/vendor/github.com/bytedance/sonic/loader/mmap_unix.go new file mode 100644 index 000000000..50b80bf20 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/mmap_unix.go @@ -0,0 +1,45 @@ +//go:build darwin || linux +// +build darwin linux + +/** + * 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 ( + `syscall` +) + +const ( + _AP = syscall.MAP_ANON | syscall.MAP_PRIVATE + _RX = syscall.PROT_READ | syscall.PROT_EXEC + _RW = syscall.PROT_READ | syscall.PROT_WRITE +) + + +func mmap(nb int) uintptr { + if m, _, e := syscall.RawSyscall6(syscall.SYS_MMAP, 0, uintptr(nb), _RW, _AP, 0, 0); e != 0 { + panic(e) + } else { + return m + } +} + +func mprotect(p uintptr, nb int) { + if _, _, err := syscall.RawSyscall(syscall.SYS_MPROTECT, p, uintptr(nb), _RX); err != 0 { + panic(err) + } +}
\ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/loader/mmap_windows.go b/vendor/github.com/bytedance/sonic/loader/mmap_windows.go new file mode 100644 index 000000000..1760a7117 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/mmap_windows.go @@ -0,0 +1,84 @@ +//go:build windows +// +build windows + +// build + +/* + * Copyright 2021 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 ( + `syscall` + `unsafe` +) + +const ( + MEM_COMMIT = 0x00001000 + MEM_RESERVE = 0x00002000 +) + +var ( + libKernel32 = syscall.NewLazyDLL("KERNEL32.DLL") + libKernel32_VirtualAlloc = libKernel32.NewProc("VirtualAlloc") + libKernel32_VirtualProtect = libKernel32.NewProc("VirtualProtect") +) + +func mmap(nb int) uintptr { + addr, err := winapi_VirtualAlloc(0, nb, MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE) + if err != nil { + panic(err) + } + return addr +} + +func mprotect(p uintptr, nb int) (oldProtect int) { + err := winapi_VirtualProtect(p, nb, syscall.PAGE_EXECUTE_READ, &oldProtect) + if err != nil { + panic(err) + } + return +} + +// winapi_VirtualAlloc allocate memory +// Doc: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc +func winapi_VirtualAlloc(lpAddr uintptr, dwSize int, flAllocationType int, flProtect int) (uintptr, error) { + r1, _, err := libKernel32_VirtualAlloc.Call( + lpAddr, + uintptr(dwSize), + uintptr(flAllocationType), + uintptr(flProtect), + ) + if r1 == 0 { + return 0, err + } + return r1, nil +} + +// winapi_VirtualProtect change memory protection +// Doc: https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect +func winapi_VirtualProtect(lpAddr uintptr, dwSize int, flNewProtect int, lpflOldProtect *int) error { + r1, _, err := libKernel32_VirtualProtect.Call( + lpAddr, + uintptr(dwSize), + uintptr(flNewProtect), + uintptr(unsafe.Pointer(lpflOldProtect)), + ) + if r1 == 0 { + return err + } + return nil +} diff --git a/vendor/github.com/bytedance/sonic/loader/pcdata.go b/vendor/github.com/bytedance/sonic/loader/pcdata.go new file mode 100644 index 000000000..b5c62d17b --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/pcdata.go @@ -0,0 +1,100 @@ +/** + * 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 + +const ( + _N_PCDATA = 4 + + _PCDATA_UnsafePoint = 0 + _PCDATA_StackMapIndex = 1 + _PCDATA_InlTreeIndex = 2 + _PCDATA_ArgLiveIndex = 3 + + _PCDATA_INVALID_OFFSET = 0 +) + +const ( + // PCDATA_UnsafePoint values. + PCDATA_UnsafePointSafe = -1 // Safe for async preemption + PCDATA_UnsafePointUnsafe = -2 // Unsafe for async preemption + + // PCDATA_Restart1(2) apply on a sequence of instructions, within + // which if an async preemption happens, we should back off the PC + // to the start of the sequence when resume. + // We need two so we can distinguish the start/end of the sequence + // in case that two sequences are next to each other. + PCDATA_Restart1 = -3 + PCDATA_Restart2 = -4 + + // Like PCDATA_RestartAtEntry, but back to function entry if async + // preempted. + PCDATA_RestartAtEntry = -5 + + _PCDATA_START_VAL = -1 +) + +var emptyByte byte + +func encodeValue(v int) []byte { + return encodeVariant(toZigzag(v)) +} + +func toZigzag(v int) int { + return (v << 1) ^ (v >> 31) +} + +func encodeVariant(v int) []byte { + var u int + var r []byte + + /* split every 7 bits */ + for v > 127 { + u = v & 0x7f + v = v >> 7 + r = append(r, byte(u) | 0x80) + } + + /* check for last one */ + if v == 0 { + return r + } + + /* add the last one */ + r = append(r, byte(v)) + return r +} + +type Pcvalue struct { + PC uint32 // PC offset from func entry + Val int32 +} + +type Pcdata []Pcvalue + +// see https://docs.google.com/document/d/1lyPIbmsYbXnpNj57a261hgOYVpNRcgydurVQIyZOz_o/pub +func (self Pcdata) MarshalBinary() (data []byte, err error) { + // delta value always starts from -1 + sv := int32(_PCDATA_START_VAL) + sp := uint32(0) + for _, v := range self { + data = append(data, encodeVariant(toZigzag(int(v.Val - sv)))...) + data = append(data, encodeVariant(int(v.PC - sp))...) + sp = v.PC + sv = v.Val + } + return +}
\ No newline at end of file diff --git a/vendor/github.com/bytedance/sonic/loader/stubs.go b/vendor/github.com/bytedance/sonic/loader/stubs.go new file mode 100644 index 000000000..71439c4b7 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/loader/stubs.go @@ -0,0 +1,35 @@ +/** + * 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 ( + _ `unsafe` +) + +//go:linkname lastmoduledatap runtime.lastmoduledatap +//goland:noinspection GoUnusedGlobalVariable +var lastmoduledatap *moduledata + +func registerModule(mod *moduledata) { + lastmoduledatap.next = mod + lastmoduledatap = mod +} + +//go:linkname moduledataverify1 runtime.moduledataverify1 +func moduledataverify1(_ *moduledata) + + |