2023-02-25 13:12:40 +01:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
)
|
|
|
|
|
2023-11-27 14:15:03 +01:00
|
|
|
// Note: This list must match the list in runtime/symtab.go.
|
|
|
|
const (
|
|
|
|
FuncFlag_TOPFRAME = 1 << iota
|
|
|
|
FuncFlag_SPWRITE
|
|
|
|
FuncFlag_ASM
|
|
|
|
)
|
|
|
|
|
2023-02-25 13:12:40 +01:00
|
|
|
// 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:]
|
2023-11-27 14:15:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|