1 // go:build go1.18 && !go1.25
2 // +build go1.18,!go1.25
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 20 package loader
21 22 import (
23 `os`
24 `sort`
25 `unsafe`
26 27 `github.com/bytedance/sonic/loader/internal/rt`
28 )
29 30 type funcTab struct {
31 entry uint32
32 funcoff uint32
33 }
34 35 type pcHeader struct {
36 magic uint32 // 0xFFFFFFF0
37 pad1, pad2 uint8 // 0,0
38 minLC uint8 // min instruction size
39 ptrSize uint8 // size of a ptr in bytes
40 nfunc int // number of functions in the module
41 nfiles uint // number of entries in the file tab
42 textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text
43 funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
44 cuOffset uintptr // offset to the cutab variable from pcHeader
45 filetabOffset uintptr // offset to the filetab variable from pcHeader
46 pctabOffset uintptr // offset to the pctab variable from pcHeader
47 pclnOffset uintptr // offset to the pclntab variable from pcHeader
48 }
49 50 type bitVector struct {
51 n int32 // # of bits
52 bytedata *uint8
53 }
54 55 type ptabEntry struct {
56 name int32
57 typ int32
58 }
59 60 type textSection struct {
61 vaddr uintptr // prelinked section vaddr
62 end uintptr // vaddr + section length
63 baseaddr uintptr // relocated section address
64 }
65 66 type modulehash struct {
67 modulename string
68 linktimehash string
69 runtimehash *string
70 }
71 72 // findfuncbucket is an array of these structures.
73 // Each bucket represents 4096 bytes of the text segment.
74 // Each subbucket represents 256 bytes of the text segment.
75 // To find a function given a pc, locate the bucket and subbucket for
76 // that pc. Add together the idx and subbucket value to obtain a
77 // function index. Then scan the functab array starting at that
78 // index to find the target function.
79 // This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
80 type findfuncbucket struct {
81 idx uint32
82 _SUBBUCKETS [16]byte
83 }
84 85 type compilationUnit struct {
86 fileNames []string
87 }
88 89 func makeFtab(funcs []_func, maxpc uint32) (ftab []funcTab, pclntabSize int64, startLocations []uint32) {
90 // Allocate space for the pc->func table. This structure consists of a pc offset
91 // and an offset to the func structure. After that, we have a single pc
92 // value that marks the end of the last function in the binary.
93 pclntabSize = int64(len(funcs)*2*int(_PtrSize) + int(_PtrSize))
94 startLocations = make([]uint32, len(funcs))
95 for i, f := range funcs {
96 pclntabSize = rnd(pclntabSize, int64(_PtrSize))
97 //writePCToFunc
98 startLocations[i] = uint32(pclntabSize)
99 pclntabSize += int64(uint8(_FUNC_SIZE)+f.nfuncdata*4+uint8(f.npcdata)*4)
100 }
101 102 ftab = make([]funcTab, 0, len(funcs)+1)
103 104 // write a map of pc->func info offsets
105 for i, f := range funcs {
106 ftab = append(ftab, funcTab{uint32(f.entryOff), uint32(startLocations[i])})
107 }
108 109 // Final entry of table is just end pc offset.
110 ftab = append(ftab, funcTab{maxpc, 0})
111 return
112 }
113 114 // Pcln table format: [...]funcTab + [...]_Func
115 func makePclntable(size int64, startLocations []uint32, funcs []_func, maxpc uint32, pcdataOffs [][]uint32, funcdataOffs [][]uint32) (pclntab []byte) {
116 // Allocate space for the pc->func table. This structure consists of a pc offset
117 // and an offset to the func structure. After that, we have a single pc
118 // value that marks the end of the last function in the binary.
119 pclntab = make([]byte, size, size)
120 121 // write a map of pc->func info offsets
122 offs := 0
123 for i, f := range funcs {
124 byteOrder.PutUint32(pclntab[offs:offs+4], uint32(f.entryOff))
125 byteOrder.PutUint32(pclntab[offs+4:offs+8], uint32(startLocations[i]))
126 offs += 8
127 }
128 // Final entry of table is just end pc offset.
129 byteOrder.PutUint32(pclntab[offs:offs+4], maxpc)
130 131 // write func info table
132 for i := range funcs {
133 off := startLocations[i]
134 135 // write _func structure to pclntab
136 fb := rt.BytesFrom(unsafe.Pointer(&funcs[i]), int(_FUNC_SIZE), int(_FUNC_SIZE))
137 copy(pclntab[off:off+uint32(_FUNC_SIZE)], fb)
138 off += uint32(_FUNC_SIZE)
139 140 // NOTICE: _func.pcdata always starts from PcUnsafePoint, which is index 3
141 for j := 3; j < len(pcdataOffs[i]); j++ {
142 byteOrder.PutUint32(pclntab[off:off+4], uint32(pcdataOffs[i][j]))
143 off += 4
144 }
145 146 // funcdata refs as offsets from gofunc
147 for _, funcdata := range funcdataOffs[i] {
148 byteOrder.PutUint32(pclntab[off:off+4], uint32(funcdata))
149 off += 4
150 }
151 152 }
153 154 return
155 }
156 157 // findfunc table used to map pc to belonging func,
158 // returns the index in the func table.
159 //
160 // All text section are divided into buckets sized _BUCKETSIZE(4K):
161 // every bucket is divided into _SUBBUCKETS sized _SUB_BUCKETSIZE(64),
162 // and it has a base idx to plus the offset stored in jth subbucket.
163 // see findfunc() in runtime/symtab.go
164 func writeFindfunctab(out *[]byte, ftab []funcTab) (start int) {
165 start = len(*out)
166 167 max := ftab[len(ftab)-1].entry
168 min := ftab[0].entry
169 nbuckets := (max - min + _BUCKETSIZE - 1) / _BUCKETSIZE
170 n := (max - min + _SUB_BUCKETSIZE - 1) / _SUB_BUCKETSIZE
171 172 tab := make([]findfuncbucket, 0, nbuckets)
173 var s, e = 0, 0
174 for i := 0; i<int(nbuckets); i++ {
175 // store the start s-th func of the bucket
176 var fb = findfuncbucket{idx: uint32(s)}
177 178 // find the last e-th func of the bucket
179 var pc = min + uint32((i+1)*_BUCKETSIZE)
180 for ; e < len(ftab)-1 && ftab[e+1].entry <= pc; e++ {}
181 182 for j := 0; j<_SUBBUCKETS && (i*_SUBBUCKETS+j)<int(n); j++ {
183 // store the start func of the subbucket
184 fb._SUBBUCKETS[j] = byte(uint32(s) - fb.idx)
185 186 // find the s-th end func of the subbucket
187 pc = min + uint32(i*_BUCKETSIZE) + uint32((j+1)*_SUB_BUCKETSIZE)
188 for ; s < len(ftab)-1 && ftab[s+1].entry <= pc; s++ {}
189 }
190 191 s = e
192 tab = append(tab, fb)
193 }
194 195 // write findfuncbucket
196 if len(tab) > 0 {
197 size := int(unsafe.Sizeof(findfuncbucket{}))*len(tab)
198 *out = append(*out, rt.BytesFrom(unsafe.Pointer(&tab[0]), size, size)...)
199 }
200 return
201 }
202 203 func makeModuledata(name string, filenames []string, funcsp *[]Func, text []byte) (mod *moduledata) {
204 mod = new(moduledata)
205 mod.modulename = name
206 207 // sort funcs by entry
208 funcs := *funcsp
209 sort.Slice(funcs, func(i, j int) bool {
210 return funcs[i].EntryOff < funcs[j].EntryOff
211 })
212 *funcsp = funcs
213 214 // make filename table
215 cu := make([]string, 0, len(filenames))
216 cu = append(cu, filenames...)
217 cutab, filetab, cuOffs := makeFilenametab([]compilationUnit{{cu}})
218 mod.cutab = cutab
219 mod.filetab = filetab
220 221 // make funcname table
222 funcnametab, nameOffs := makeFuncnameTab(funcs)
223 mod.funcnametab = funcnametab
224 225 // mmap() text and funcdata segments
226 p := os.Getpagesize()
227 size := int(rnd(int64(len(text)), int64(p)))
228 addr := mmap(size)
229 // copy the machine code
230 s := rt.BytesFrom(unsafe.Pointer(addr), len(text), size)
231 copy(s, text)
232 // make it executable
233 mprotect(addr, size)
234 235 // assign addresses
236 mod.text = addr
237 mod.etext = addr + uintptr(size)
238 mod.minpc = addr
239 mod.maxpc = addr + uintptr(len(text))
240 241 // make pcdata table
242 // NOTICE: _func only use offset to index pcdata, thus no need mmap() pcdata
243 cuOff := cuOffs[0]
244 pctab, pcdataOffs, _funcs := makePctab(funcs, cuOff, nameOffs)
245 mod.pctab = pctab
246 247 // write func data
248 // NOTICE: _func use mod.gofunc+offset to directly point funcdata, thus need cache funcdata
249 // TODO: estimate accurate capacity
250 cache := make([]byte, 0, len(funcs)*int(_PtrSize))
251 fstart, funcdataOffs := writeFuncdata(&cache, funcs)
252 253 // make pc->func (binary search) func table
254 ftab, pclntSize, startLocations := makeFtab(_funcs, uint32(len(text)))
255 mod.ftab = ftab
256 257 // write pc->func (modmap) findfunc table
258 ffstart := writeFindfunctab(&cache, ftab)
259 260 // cache funcdata and findfuncbucket
261 moduleCache.Lock()
262 moduleCache.m[mod] = cache
263 moduleCache.Unlock()
264 mod.gofunc = uintptr(unsafe.Pointer(&cache[fstart]))
265 mod.findfunctab = uintptr(unsafe.Pointer(&cache[ffstart]))
266 267 // make pclnt table
268 pclntab := makePclntable(pclntSize, startLocations, _funcs, uint32(len(text)), pcdataOffs, funcdataOffs)
269 mod.pclntable = pclntab
270 271 // make pc header
272 mod.pcHeader = &pcHeader {
273 magic : _Magic,
274 minLC : _MinLC,
275 ptrSize : _PtrSize,
276 nfunc : len(funcs),
277 nfiles: uint(len(cu)),
278 textStart: mod.text,
279 funcnameOffset: getOffsetOf(moduledata{}, "funcnametab"),
280 cuOffset: getOffsetOf(moduledata{}, "cutab"),
281 filetabOffset: getOffsetOf(moduledata{}, "filetab"),
282 pctabOffset: getOffsetOf(moduledata{}, "pctab"),
283 pclnOffset: getOffsetOf(moduledata{}, "pclntable"),
284 }
285 286 // special case: gcdata and gcbss must by non-empty
287 mod.gcdata = uintptr(unsafe.Pointer(&emptyByte))
288 mod.gcbss = uintptr(unsafe.Pointer(&emptyByte))
289 290 return
291 }
292 293 // makePctab generates pcdelta->valuedelta tables for functions,
294 // and returns the table and the entry offset of every kind pcdata in the table.
295 func makePctab(funcs []Func, cuOffset uint32, nameOffset []int32) (pctab []byte, pcdataOffs [][]uint32, _funcs []_func) {
296 _funcs = make([]_func, len(funcs))
297 298 // Pctab offsets of 0 are considered invalid in the runtime. We respect
299 // that by just padding a single byte at the beginning of runtime.pctab,
300 // that way no real offsets can be zero.
301 pctab = make([]byte, 1, 12*len(funcs)+1)
302 pcdataOffs = make([][]uint32, len(funcs))
303 304 for i, f := range funcs {
305 _f := &_funcs[i]
306 307 var writer = func(pc *Pcdata) {
308 var ab []byte
309 var err error
310 if pc != nil {
311 ab, err = pc.MarshalBinary()
312 if err != nil {
313 panic(err)
314 }
315 pcdataOffs[i] = append(pcdataOffs[i], uint32(len(pctab)))
316 } else {
317 ab = []byte{0}
318 pcdataOffs[i] = append(pcdataOffs[i], _PCDATA_INVALID_OFFSET)
319 }
320 pctab = append(pctab, ab...)
321 }
322 323 if f.Pcsp != nil {
324 _f.pcsp = uint32(len(pctab))
325 }
326 writer(f.Pcsp)
327 if f.Pcfile != nil {
328 _f.pcfile = uint32(len(pctab))
329 }
330 writer(f.Pcfile)
331 if f.Pcline != nil {
332 _f.pcln = uint32(len(pctab))
333 }
334 writer(f.Pcline)
335 writer(f.PcUnsafePoint)
336 writer(f.PcStackMapIndex)
337 writer(f.PcInlTreeIndex)
338 writer(f.PcArgLiveIndex)
339 340 _f.entryOff = f.EntryOff
341 _f.nameOff = nameOffset[i]
342 _f.args = f.ArgsSize
343 _f.deferreturn = f.DeferReturn
344 // NOTICE: _func.pcdata is always as [PCDATA_UnsafePoint(0) : PCDATA_ArgLiveIndex(3)]
345 _f.npcdata = uint32(_N_PCDATA)
346 _f.cuOffset = cuOffset
347 _f.funcID = f.ID
348 _f.flag = f.Flag
349 _f.nfuncdata = uint8(_N_FUNCDATA)
350 }
351 352 return
353 }
354 355 func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {}
356