podman
1//go:build go1.16 && !go1.18
2// +build go1.16,!go1.18
3
4/*
5* Copyright 2021 ByteDance Inc.
6*
7* Licensed under the Apache License, Version 2.0 (the "License");
8* you may not use this file except in compliance with the License.
9* You may obtain a copy of the License at
10*
11* http://www.apache.org/licenses/LICENSE-2.0
12*
13* Unless required by applicable law or agreed to in writing, software
14* distributed under the License is distributed on an "AS IS" BASIS,
15* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16* See the License for the specific language governing permissions and
17* limitations under the License.
18*/
19
20package loader21
22import (23`os`24`unsafe`25`sort`26
27`github.com/bytedance/sonic/internal/rt`28)
29
30const (31_Magic uint32 = 0xfffffffa32)
33
34type pcHeader struct {35magic uint32 // 0xFFFFFFF036pad1, pad2 uint8 // 0,037minLC uint8 // min instruction size38ptrSize uint8 // size of a ptr in bytes39nfunc int // number of functions in the module40nfiles uint // number of entries in the file tab41funcnameOffset uintptr // offset to the funcnametab variable from pcHeader42cuOffset uintptr // offset to the cutab variable from pcHeader43filetabOffset uintptr // offset to the filetab variable from pcHeader44pctabOffset uintptr // offset to the pctab variable from pcHeader45pclnOffset uintptr // offset to the pclntab variable from pcHeader46}
47
48type moduledata struct {49pcHeader *pcHeader50funcnametab []byte51cutab []uint3252filetab []byte53pctab []byte54pclntable []byte55ftab []funcTab56findfunctab uintptr57minpc, maxpc uintptr // first func address, last func address + last func size58
59text, etext uintptr // start/end of text, (etext-text) must be greater than MIN_FUNC60noptrdata, enoptrdata uintptr61data, edata uintptr62bss, ebss uintptr63noptrbss, enoptrbss uintptr64end, gcdata, gcbss uintptr65types, etypes uintptr66
67textsectmap []textSection // see runtime/symtab.go: textAddr()68typelinks []int32 // offsets from types69itablinks []*rt.GoItab70
71ptab []ptabEntry72
73pluginpath string74pkghashes []modulehash75
76modulename string77modulehashes []modulehash78
79hasmain uint8 // 1 if module contains the main function, 0 otherwise80
81gcdatamask, gcbssmask bitVector82
83typemap map[int32]*rt.GoType // offset to *_rtype in previous module84
85bad bool // module failed to load and should be ignored86
87next *moduledata88}
89
90type _func struct {91entry uintptr // start pc, as offset from moduledata.text/pcHeader.textStart92nameOff int32 // function name, as index into moduledata.funcnametab.93
94args int32 // in/out args size95deferreturn uint32 // offset of start of a deferreturn call instruction from entry, if any.96
97pcsp uint3298pcfile uint3299pcln uint32100npcdata uint32101cuOffset uint32 // runtime.cutab offset of this function's CU102funcID uint8 // set for certain special runtime functions103_ [2]byte // pad104nfuncdata uint8 //105
106// The end of the struct is followed immediately by two variable-length107// arrays that reference the pcdata and funcdata locations for this108// function.109
110// pcdata contains the offset into moduledata.pctab for the start of111// that index's table. e.g.,112// &moduledata.pctab[_func.pcdata[_PCDATA_UnsafePoint]] is the start of113// the unsafe point table.114//115// An offset of 0 indicates that there is no table.116//117// pcdata [npcdata]uint32118
119// funcdata contains the offset past moduledata.gofunc which contains a120// pointer to that index's funcdata. e.g.,121// *(moduledata.gofunc + _func.funcdata[_FUNCDATA_ArgsPointerMaps]) is122// the argument pointer map.123//124// An offset of ^uint32(0) indicates that there is no entry.125//126// funcdata [nfuncdata]uint32127}
128
129type funcTab struct {130entry uintptr131funcoff uintptr132}
133
134type bitVector struct {135n int32 // # of bits136bytedata *uint8137}
138
139type ptabEntry struct {140name int32141typ int32142}
143
144type textSection struct {145vaddr uintptr // prelinked section vaddr146end uintptr // vaddr + section length147baseaddr uintptr // relocated section address148}
149
150type modulehash struct {151modulename string152linktimehash string153runtimehash *string154}
155
156// findfuncbucket is an array of these structures.
157// Each bucket represents 4096 bytes of the text segment.
158// Each subbucket represents 256 bytes of the text segment.
159// To find a function given a pc, locate the bucket and subbucket for
160// that pc. Add together the idx and subbucket value to obtain a
161// function index. Then scan the functab array starting at that
162// index to find the target function.
163// This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
164type findfuncbucket struct {165idx uint32166_SUBBUCKETS [16]byte167}
168
169
170type compilationUnit struct {171fileNames []string172}
173
174func makeFtab(funcs []_func, maxpc uintptr) (ftab []funcTab, pclntabSize int64, startLocations []uint32) {175// Allocate space for the pc->func table. This structure consists of a pc offset176// and an offset to the func structure. After that, we have a single pc177// value that marks the end of the last function in the binary.178pclntabSize = int64(len(funcs)*2*int(_PtrSize) + int(_PtrSize))179startLocations = make([]uint32, len(funcs))180for i, f := range funcs {181pclntabSize = rnd(pclntabSize, int64(_PtrSize))182//writePCToFunc183startLocations[i] = uint32(pclntabSize)184pclntabSize += int64(uint8(_FUNC_SIZE) + f.nfuncdata*_PtrSize + uint8(f.npcdata)*4)185}186ftab = make([]funcTab, 0, len(funcs)+1)187
188// write a map of pc->func info offsets189for i, f := range funcs {190ftab = append(ftab, funcTab{uintptr(f.entry), uintptr(startLocations[i])})191}192
193// Final entry of table is just end pc offset.194ftab = append(ftab, funcTab{maxpc, 0})195
196return197}
198
199// Pcln table format: [...]funcTab + [...]_Func
200func makePclntable(size int64, startLocations []uint32, funcs []_func, maxpc uintptr, pcdataOffs [][]uint32, funcdataAddr uintptr, funcdataOffs [][]uint32) (pclntab []byte) {201pclntab = make([]byte, size, size)202
203// write a map of pc->func info offsets204offs := 0205for i, f := range funcs {206byteOrder.PutUint64(pclntab[offs:offs+8], uint64(f.entry))207byteOrder.PutUint64(pclntab[offs+8:offs+16], uint64(startLocations[i]))208offs += 16209}210// Final entry of table is just end pc offset.211byteOrder.PutUint64(pclntab[offs:offs+8], uint64(maxpc))212offs += 8213
214// write func info table215for i, f := range funcs {216off := startLocations[i]217
218// write _func structure to pclntab219byteOrder.PutUint64(pclntab[off:off+8], uint64(f.entry))220off += 8221byteOrder.PutUint32(pclntab[off:off+4], uint32(f.nameOff))222off += 4223byteOrder.PutUint32(pclntab[off:off+4], uint32(f.args))224off += 4225byteOrder.PutUint32(pclntab[off:off+4], uint32(f.deferreturn))226off += 4227byteOrder.PutUint32(pclntab[off:off+4], uint32(f.pcsp))228off += 4229byteOrder.PutUint32(pclntab[off:off+4], uint32(f.pcfile))230off += 4231byteOrder.PutUint32(pclntab[off:off+4], uint32(f.pcln))232off += 4233byteOrder.PutUint32(pclntab[off:off+4], uint32(f.npcdata))234off += 4235byteOrder.PutUint32(pclntab[off:off+4], uint32(f.cuOffset))236off += 4237pclntab[off] = f.funcID238// NOTICE: _[2]byte alignment239off += 3240pclntab[off] = f.nfuncdata241off += 1242
243// NOTICE: _func.pcdata always starts from PcUnsafePoint, which is index 3244for j := 3; j < len(pcdataOffs[i]); j++ {245byteOrder.PutUint32(pclntab[off:off+4], uint32(pcdataOffs[i][j]))246off += 4247}248
249off = uint32(rnd(int64(off), int64(_PtrSize)))250
251// funcdata refs as offsets from gofunc252for _, funcdata := range funcdataOffs[i] {253if funcdata == _INVALID_FUNCDATA_OFFSET {254byteOrder.PutUint64(pclntab[off:off+8], 0)255} else {256byteOrder.PutUint64(pclntab[off:off+8], uint64(funcdataAddr)+uint64(funcdata))257}258off += 8259}260}261
262return263}
264
265// findfunc table used to map pc to belonging func,
266// returns the index in the func table.
267//
268// All text section are divided into buckets sized _BUCKETSIZE(4K):
269// every bucket is divided into _SUBBUCKETS sized _SUB_BUCKETSIZE(64),
270// and it has a base idx to plus the offset stored in jth subbucket.
271// see findfunc() in runtime/symtab.go
272func writeFindfunctab(out *[]byte, ftab []funcTab) (start int) {273start = len(*out)274
275max := ftab[len(ftab)-1].entry276min := ftab[0].entry277nbuckets := (max - min + _BUCKETSIZE - 1) / _BUCKETSIZE278n := (max - min + _SUB_BUCKETSIZE - 1) / _SUB_BUCKETSIZE279
280tab := make([]findfuncbucket, 0, nbuckets)281var s, e = 0, 0282for i := 0; i<int(nbuckets); i++ {283// store the start func of the bucket284var fb = findfuncbucket{idx: uint32(s)}285
286// find the last e-th func of the bucket287var pc = min + uintptr((i+1)*_BUCKETSIZE)288for ; e < len(ftab)-1 && ftab[e+1].entry <= pc; e++ {}289
290for j := 0; j<_SUBBUCKETS && (i*_SUBBUCKETS+j)<int(n); j++ {291// store the start func of the subbucket292fb._SUBBUCKETS[j] = byte(uint32(s) - fb.idx)293
294// find the s-th end func of the subbucket295pc = min + uintptr(i*_BUCKETSIZE) + uintptr((j+1)*_SUB_BUCKETSIZE)296for ; s < len(ftab)-1 && ftab[s+1].entry <= pc; s++ {}297}298
299s = e300tab = append(tab, fb)301}302
303// write findfuncbucket304if len(tab) > 0 {305size := int(unsafe.Sizeof(findfuncbucket{}))*len(tab)306*out = append(*out, rt.BytesFrom(unsafe.Pointer(&tab[0]), size, size)...)307}308return309}
310
311func makeModuledata(name string, filenames []string, funcsp *[]Func, text []byte) (mod *moduledata) {312mod = new(moduledata)313mod.modulename = name314
315// sort funcs by entry316funcs := *funcsp317sort.Slice(funcs, func(i, j int) bool {318return funcs[i].EntryOff < funcs[j].EntryOff319})320*funcsp = funcs321
322// make filename table323cu := make([]string, 0, len(filenames))324cu = append(cu, filenames...)325cutab, filetab, cuOffs := makeFilenametab([]compilationUnit{{cu}})326mod.cutab = cutab327mod.filetab = filetab328
329// make funcname table330funcnametab, nameOffs := makeFuncnameTab(funcs)331mod.funcnametab = funcnametab332
333// mmap() text and funcdata segements334p := os.Getpagesize()335size := int(rnd(int64(len(text)), int64(p)))336addr := mmap(size)337// copy the machine code338s := rt.BytesFrom(unsafe.Pointer(addr), len(text), size)339copy(s, text)340// make it executable341mprotect(addr, size)342
343// assign addresses344mod.text = addr345mod.etext = addr + uintptr(size)346mod.minpc = addr347mod.maxpc = addr + uintptr(len(text))348
349// make pcdata table350// NOTICE: _func only use offset to index pcdata, thus no need mmap() pcdata351cuOff := cuOffs[0]352pctab, pcdataOffs, _funcs := makePctab(funcs, addr, cuOff, nameOffs)353mod.pctab = pctab354
355// write func data356// NOTICE: _func use mod.gofunc+offset to directly point funcdata, thus need cache funcdata357// TODO: estimate accurate capacity358cache := make([]byte, 0, len(funcs)*int(_PtrSize))359fstart, funcdataOffs := writeFuncdata(&cache, funcs)360
361// make pc->func (binary search) func table362ftab, pclntSize, startLocations := makeFtab(_funcs, mod.maxpc)363mod.ftab = ftab364
365// write pc->func (modmap) findfunc table366ffstart := writeFindfunctab(&cache, ftab)367
368// cache funcdata and findfuncbucket369moduleCache.Lock()370moduleCache.m[mod] = cache371moduleCache.Unlock()372mod.findfunctab = uintptr(rt.IndexByte(cache, ffstart))373funcdataAddr := uintptr(rt.IndexByte(cache, fstart))374
375// make pclnt table376pclntab := makePclntable(pclntSize, startLocations, _funcs, mod.maxpc, pcdataOffs, funcdataAddr, funcdataOffs)377mod.pclntable = pclntab378
379// make pc header380mod.pcHeader = &pcHeader {381magic : _Magic,382minLC : _MinLC,383ptrSize : _PtrSize,384nfunc : len(funcs),385nfiles: uint(len(cu)),386funcnameOffset: getOffsetOf(moduledata{}, "funcnametab"),387cuOffset: getOffsetOf(moduledata{}, "cutab"),388filetabOffset: getOffsetOf(moduledata{}, "filetab"),389pctabOffset: getOffsetOf(moduledata{}, "pctab"),390pclnOffset: getOffsetOf(moduledata{}, "pclntable"),391}392
393// sepecial case: gcdata and gcbss must by non-empty394mod.gcdata = uintptr(unsafe.Pointer(&emptyByte))395mod.gcbss = uintptr(unsafe.Pointer(&emptyByte))396
397return398}
399
400// makePctab generates pcdelta->valuedelta tables for functions,
401// and returns the table and the entry offset of every kind pcdata in the table.
402func makePctab(funcs []Func, addr uintptr, cuOffset uint32, nameOffset []int32) (pctab []byte, pcdataOffs [][]uint32, _funcs []_func) {403_funcs = make([]_func, len(funcs))404
405// Pctab offsets of 0 are considered invalid in the runtime. We respect406// that by just padding a single byte at the beginning of runtime.pctab,407// that way no real offsets can be zero.408pctab = make([]byte, 1, 12*len(funcs)+1)409pcdataOffs = make([][]uint32, len(funcs))410
411for i, f := range funcs {412_f := &_funcs[i]413
414var writer = func(pc *Pcdata) {415var ab []byte416var err error417if pc != nil {418ab, err = pc.MarshalBinary()419if err != nil {420panic(err)421}422pcdataOffs[i] = append(pcdataOffs[i], uint32(len(pctab)))423} else {424ab = []byte{0}425pcdataOffs[i] = append(pcdataOffs[i], _PCDATA_INVALID_OFFSET)426}427pctab = append(pctab, ab...)428}429
430if f.Pcsp != nil {431_f.pcsp = uint32(len(pctab))432}433writer(f.Pcsp)434if f.Pcfile != nil {435_f.pcfile = uint32(len(pctab))436}437writer(f.Pcfile)438if f.Pcline != nil {439_f.pcln = uint32(len(pctab))440}441writer(f.Pcline)442writer(f.PcUnsafePoint)443writer(f.PcStackMapIndex)444writer(f.PcInlTreeIndex)445writer(f.PcArgLiveIndex)446
447_f.entry = addr + uintptr(f.EntryOff)448_f.nameOff = nameOffset[i]449_f.args = f.ArgsSize450_f.deferreturn = f.DeferReturn451// NOTICE: _func.pcdata is always as [PCDATA_UnsafePoint(0) : PCDATA_ArgLiveIndex(3)]452_f.npcdata = uint32(_N_PCDATA)453_f.cuOffset = cuOffset454_f.funcID = f.ID455_f.nfuncdata = uint8(_N_FUNCDATA)456}457
458return459}
460
461func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {}462