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