mirror of
https://github.com/ii64/sonic.git
synced 2026-06-24 02:16:45 +08:00
feat: more complete function loader (#354)
* follow complete implementation of go symtab * support go1.20
This commit is contained in:
parent
cfa4fe1736
commit
e7ac2f25fc
58 changed files with 3236 additions and 272 deletions
6
.github/workflows/push-check-go118.yml
vendored
6
.github/workflows/push-check-go118.yml
vendored
|
|
@ -25,8 +25,8 @@ jobs:
|
||||||
|
|
||||||
- name: Unit Test
|
- name: Unit Test
|
||||||
run: |
|
run: |
|
||||||
GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 -race ./...
|
GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 ./...
|
||||||
GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 -race ./external_jsonlib_test/...
|
GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 ./external_jsonlib_test/...
|
||||||
|
|
||||||
- name: Generic Test
|
- name: Generic Test
|
||||||
run: GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 -race ./generic_test
|
run: GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 ./generic_test
|
||||||
2
.github/workflows/push-check-linux-arm64.yml
vendored
2
.github/workflows/push-check-linux-arm64.yml
vendored
|
|
@ -6,7 +6,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.15.x, 1.19.x]
|
go-version: [1.15.x, 1.20.x]
|
||||||
os: [arm]
|
os: [arm]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
2
.github/workflows/push-check-linux-x64.yml
vendored
2
.github/workflows/push-check-linux-x64.yml
vendored
|
|
@ -6,7 +6,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.15.x, 1.16.x, 1.17.x, 1.19.x]
|
go-version: [1.15.x, 1.16.x, 1.17.x, 1.19.x, 1.20.x]
|
||||||
runs-on: [self-hosted, X64]
|
runs-on: [self-hosted, X64]
|
||||||
steps:
|
steps:
|
||||||
- name: Clear repository
|
- name: Clear repository
|
||||||
|
|
|
||||||
2
.github/workflows/push-check-qemu.yml
vendored
2
.github/workflows/push-check-qemu.yml
vendored
|
|
@ -6,7 +6,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.15.x, 1.19.x]
|
go-version: [1.15.x, 1.20.x]
|
||||||
os: [arm]
|
os: [arm]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
2
.github/workflows/push-check-windows.yml
vendored
2
.github/workflows/push-check-windows.yml
vendored
|
|
@ -6,7 +6,7 @@ jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.15.x, 1.19.x]
|
go-version: [1.15.x, 1.20.x]
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ import (
|
||||||
`github.com/stretchr/testify/assert`
|
`github.com/stretchr/testify/assert`
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSortNodeTwitter(t *testing.T) {root, err := NewSearcher(_TwitterJson).GetByPath()
|
func TestSortNodeTwitter(t *testing.T) {
|
||||||
|
root, err := NewSearcher(_TwitterJson).GetByPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ package ast
|
||||||
import (
|
import (
|
||||||
`sync`
|
`sync`
|
||||||
`unicode/utf8`
|
`unicode/utf8`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -163,7 +165,7 @@ func (self *Node) encodeFalse(buf *[]byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Node) encodeNumber(buf *[]byte) error {
|
func (self *Node) encodeNumber(buf *[]byte) error {
|
||||||
str := addr2str(self.p, self.v)
|
str := rt.StrFrom(self.p, self.v)
|
||||||
*buf = append(*buf, str...)
|
*buf = append(*buf, str...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -174,7 +176,7 @@ func (self *Node) encodeString(buf *[]byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
quote(buf, addr2str(self.p, self.v))
|
quote(buf, rt.StrFrom(self.p, self.v))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
31
ast/node.go
31
ast/node.go
|
|
@ -21,6 +21,7 @@ import (
|
||||||
`fmt`
|
`fmt`
|
||||||
`strconv`
|
`strconv`
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
`reflect`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/native/types`
|
`github.com/bytedance/sonic/internal/native/types`
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
|
@ -60,6 +61,10 @@ const (
|
||||||
V_ANY = int(_V_ANY)
|
V_ANY = int(_V_ANY)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
byteType = rt.UnpackType(reflect.TypeOf(byte(0)))
|
||||||
|
)
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
v int64
|
v int64
|
||||||
t types.ValueType
|
t types.ValueType
|
||||||
|
|
@ -151,7 +156,7 @@ func (self *Node) Raw() (string, error) {
|
||||||
buf, err := self.MarshalJSON()
|
buf, err := self.MarshalJSON()
|
||||||
return rt.Mem2Str(buf), err
|
return rt.Mem2Str(buf), err
|
||||||
}
|
}
|
||||||
return addr2str(self.p, self.v), nil
|
return rt.StrFrom(self.p, self.v), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Node) checkRaw() error {
|
func (self *Node) checkRaw() error {
|
||||||
|
|
@ -183,7 +188,7 @@ func (self *Node) Bool() (bool, error) {
|
||||||
} else {
|
} else {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
case types.V_STRING: return strconv.ParseBool(addr2str(self.p, self.v))
|
case types.V_STRING: return strconv.ParseBool(rt.StrFrom(self.p, self.v))
|
||||||
case _V_ANY :
|
case _V_ANY :
|
||||||
any := self.packAny()
|
any := self.packAny()
|
||||||
switch v := any.(type) {
|
switch v := any.(type) {
|
||||||
|
|
@ -389,7 +394,7 @@ func (self *Node) String() (string, error) {
|
||||||
case types.V_NULL : return "" , nil
|
case types.V_NULL : return "" , nil
|
||||||
case types.V_TRUE : return "true" , nil
|
case types.V_TRUE : return "true" , nil
|
||||||
case types.V_FALSE : return "false", nil
|
case types.V_FALSE : return "false", nil
|
||||||
case types.V_STRING, _V_NUMBER : return addr2str(self.p, self.v), nil
|
case types.V_STRING, _V_NUMBER : return rt.StrFrom(self.p, self.v), nil
|
||||||
case _V_ANY :
|
case _V_ANY :
|
||||||
any := self.packAny()
|
any := self.packAny()
|
||||||
switch v := any.(type) {
|
switch v := any.(type) {
|
||||||
|
|
@ -421,7 +426,7 @@ func (self *Node) StrictString() (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
switch self.t {
|
switch self.t {
|
||||||
case types.V_STRING : return addr2str(self.p, self.v), nil
|
case types.V_STRING : return rt.StrFrom(self.p, self.v), nil
|
||||||
case _V_ANY :
|
case _V_ANY :
|
||||||
if v, ok := self.packAny().(string); ok {
|
if v, ok := self.packAny().(string); ok {
|
||||||
return v, nil
|
return v, nil
|
||||||
|
|
@ -836,7 +841,7 @@ func (self *Node) UnsafeMap() ([]Pair, error) {
|
||||||
if err := self.skipAllKey(); err != nil {
|
if err := self.skipAllKey(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s := ptr2slice(self.p, int(self.len()), self.cap())
|
s := rt.Ptr2SlicePtr(self.p, int(self.len()), self.cap())
|
||||||
return *(*[]Pair)(s), nil
|
return *(*[]Pair)(s), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -935,7 +940,7 @@ func (self *Node) UnsafeArray() ([]Node, error) {
|
||||||
if err := self.skipAllIndex(); err != nil {
|
if err := self.skipAllIndex(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s := ptr2slice(self.p, self.len(), self.cap())
|
s := rt.Ptr2SlicePtr(self.p, self.len(), self.cap())
|
||||||
return *(*[]Node)(s), nil
|
return *(*[]Node)(s), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -953,7 +958,7 @@ func (self *Node) Interface() (interface{}, error) {
|
||||||
case types.V_FALSE : return false, nil
|
case types.V_FALSE : return false, nil
|
||||||
case types.V_ARRAY : return self.toGenericArray()
|
case types.V_ARRAY : return self.toGenericArray()
|
||||||
case types.V_OBJECT : return self.toGenericObject()
|
case types.V_OBJECT : return self.toGenericObject()
|
||||||
case types.V_STRING : return addr2str(self.p, self.v), nil
|
case types.V_STRING : return rt.StrFrom(self.p, self.v), nil
|
||||||
case _V_NUMBER :
|
case _V_NUMBER :
|
||||||
v, err := numberToFloat64(self)
|
v, err := numberToFloat64(self)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -997,7 +1002,7 @@ func (self *Node) InterfaceUseNumber() (interface{}, error) {
|
||||||
case types.V_FALSE : return false, nil
|
case types.V_FALSE : return false, nil
|
||||||
case types.V_ARRAY : return self.toGenericArrayUseNumber()
|
case types.V_ARRAY : return self.toGenericArrayUseNumber()
|
||||||
case types.V_OBJECT : return self.toGenericObjectUseNumber()
|
case types.V_OBJECT : return self.toGenericObjectUseNumber()
|
||||||
case types.V_STRING : return addr2str(self.p, self.v), nil
|
case types.V_STRING : return rt.StrFrom(self.p, self.v), nil
|
||||||
case _V_NUMBER : return toNumber(self), nil
|
case _V_NUMBER : return toNumber(self), nil
|
||||||
case _V_ARRAY_LAZY :
|
case _V_ARRAY_LAZY :
|
||||||
if err := self.loadAllIndex(); err != nil {
|
if err := self.loadAllIndex(); err != nil {
|
||||||
|
|
@ -1597,13 +1602,13 @@ func NewBool(v bool) Node {
|
||||||
func NewNumber(v string) Node {
|
func NewNumber(v string) Node {
|
||||||
return Node{
|
return Node{
|
||||||
v: int64(len(v) & _LEN_MASK),
|
v: int64(len(v) & _LEN_MASK),
|
||||||
p: str2ptr(v),
|
p: rt.StrPtr(v),
|
||||||
t: _V_NUMBER,
|
t: _V_NUMBER,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toNumber(node *Node) json.Number {
|
func toNumber(node *Node) json.Number {
|
||||||
return json.Number(addr2str(node.p, node.v))
|
return json.Number(rt.StrFrom(node.p, node.v))
|
||||||
}
|
}
|
||||||
|
|
||||||
func numberToFloat64(node *Node) (float64, error) {
|
func numberToFloat64(node *Node) (float64, error) {
|
||||||
|
|
@ -1637,7 +1642,7 @@ func newBytes(v []byte) Node {
|
||||||
func NewString(v string) Node {
|
func NewString(v string) Node {
|
||||||
return Node{
|
return Node{
|
||||||
t: types.V_STRING,
|
t: types.V_STRING,
|
||||||
p: str2ptr(v),
|
p: rt.StrPtr(v),
|
||||||
v: int64(len(v) & _LEN_MASK),
|
v: int64(len(v) & _LEN_MASK),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1727,13 +1732,13 @@ func (self *Node) setLazyObject(p *Parser, v []Pair) {
|
||||||
func newRawNode(str string, typ types.ValueType) Node {
|
func newRawNode(str string, typ types.ValueType) Node {
|
||||||
return Node{
|
return Node{
|
||||||
t: _V_RAW | typ,
|
t: _V_RAW | typ,
|
||||||
p: str2ptr(str),
|
p: rt.StrPtr(str),
|
||||||
v: int64(len(str) & _LEN_MASK),
|
v: int64(len(str) & _LEN_MASK),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Node) parseRaw(full bool) {
|
func (self *Node) parseRaw(full bool) {
|
||||||
raw := addr2str(self.p, self.v)
|
raw := rt.StrFrom(self.p, self.v)
|
||||||
parser := NewParser(raw)
|
parser := NewParser(raw)
|
||||||
if full {
|
if full {
|
||||||
parser.noLazy = true
|
parser.noLazy = true
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build go1.15,!go1.20
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
*
|
*
|
||||||
|
|
@ -18,16 +20,11 @@ package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`unsafe`
|
`unsafe`
|
||||||
`reflect`
|
|
||||||
`unicode/utf8`
|
`unicode/utf8`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
byteType = rt.UnpackType(reflect.TypeOf(byte(0)))
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:noescape
|
//go:noescape
|
||||||
//go:linkname memmove runtime.memmove
|
//go:linkname memmove runtime.memmove
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
|
|
@ -46,29 +43,6 @@ func mem2ptr(s []byte) unsafe.Pointer {
|
||||||
return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
|
return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:nosplit
|
|
||||||
func ptr2slice(s unsafe.Pointer, l int, c int) unsafe.Pointer {
|
|
||||||
slice := &rt.GoSlice{
|
|
||||||
Ptr: s,
|
|
||||||
Len: l,
|
|
||||||
Cap: c,
|
|
||||||
}
|
|
||||||
return unsafe.Pointer(slice)
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:nosplit
|
|
||||||
func str2ptr(s string) unsafe.Pointer {
|
|
||||||
return (*rt.GoString)(unsafe.Pointer(&s)).Ptr
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:nosplit
|
|
||||||
func addr2str(p unsafe.Pointer, n int64) (s string) {
|
|
||||||
(*rt.GoString)(unsafe.Pointer(&s)).Ptr = p
|
|
||||||
(*rt.GoString)(unsafe.Pointer(&s)).Len = int(n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
//go:linkname safeSet encoding/json.safeSet
|
//go:linkname safeSet encoding/json.safeSet
|
||||||
safeSet [utf8.RuneSelf]bool
|
safeSet [utf8.RuneSelf]bool
|
||||||
55
ast/stubs_go120.go
Normal file
55
ast/stubs_go120.go
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
// +build 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 ast
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
`unicode/utf8`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname unsafe_NewArray reflect.unsafe_NewArray
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func unsafe_NewArray(typ *rt.GoType, n int) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname growslice reflect.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func mem2ptr(s []byte) unsafe.Pointer {
|
||||||
|
return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:linkname safeSet encoding/json.safeSet
|
||||||
|
safeSet [utf8.RuneSelf]bool
|
||||||
|
|
||||||
|
//go:linkname hex encoding/json.hex
|
||||||
|
hex string
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname unquoteBytes encoding/json.unquoteBytes
|
||||||
|
func unquoteBytes(s []byte) (t []byte, ok bool)
|
||||||
|
|
@ -215,6 +215,7 @@ var (
|
||||||
type _Assembler struct {
|
type _Assembler struct {
|
||||||
jit.BaseAssembler
|
jit.BaseAssembler
|
||||||
p _Program
|
p _Program
|
||||||
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAssembler(p _Program) *_Assembler {
|
func newAssembler(p _Program) *_Assembler {
|
||||||
|
|
@ -224,7 +225,7 @@ func newAssembler(p _Program) *_Assembler {
|
||||||
/** Assembler Interface **/
|
/** Assembler Interface **/
|
||||||
|
|
||||||
func (self *_Assembler) Load() _Decoder {
|
func (self *_Assembler) Load() _Decoder {
|
||||||
return ptodec(self.BaseAssembler.LoadWithFaker("json_decoder", _FP_size, _FP_args, _Decoder_Shadow))
|
return ptodec(self.BaseAssembler.Load("decode_"+self.name, _FP_size, _FP_args, argPtrs, localPtrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) Init(p _Program) *_Assembler {
|
func (self *_Assembler) Init(p _Program) *_Assembler {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build go1.17,!go1.20
|
//go:build go1.17 && !go1.21
|
||||||
|
// +build go1.17,!go1.21
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
|
@ -165,10 +166,10 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_VAR_sv = _VAR_sv_p
|
_ARG_sv = _ARG_sv_p
|
||||||
_VAR_sv_p = jit.Ptr(_SP, _FP_base + 48)
|
_ARG_sv_p = jit.Ptr(_SP, _FP_base + 48)
|
||||||
_VAR_sv_n = jit.Ptr(_SP, _FP_base + 56)
|
_ARG_sv_n = jit.Ptr(_SP, _FP_base + 56)
|
||||||
_VAR_vk = jit.Ptr(_SP, _FP_base + 64)
|
_ARG_vk = jit.Ptr(_SP, _FP_base + 64)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -210,6 +211,7 @@ var (
|
||||||
type _Assembler struct {
|
type _Assembler struct {
|
||||||
jit.BaseAssembler
|
jit.BaseAssembler
|
||||||
p _Program
|
p _Program
|
||||||
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAssembler(p _Program) *_Assembler {
|
func newAssembler(p _Program) *_Assembler {
|
||||||
|
|
@ -219,7 +221,7 @@ func newAssembler(p _Program) *_Assembler {
|
||||||
/** Assembler Interface **/
|
/** Assembler Interface **/
|
||||||
|
|
||||||
func (self *_Assembler) Load() _Decoder {
|
func (self *_Assembler) Load() _Decoder {
|
||||||
return ptodec(self.BaseAssembler.LoadWithFaker("json_decoder", _FP_size, _FP_args, _Decoder_Shadow))
|
return ptodec(self.BaseAssembler.Load("decode_"+self.name, _FP_size, _FP_args, argPtrs, localPtrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) Init(p _Program) *_Assembler {
|
func (self *_Assembler) Init(p _Program) *_Assembler {
|
||||||
|
|
@ -348,8 +350,8 @@ func (self *_Assembler) epilogue() {
|
||||||
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
|
||||||
self.Emit("MOVQ", jit.Imm(0), _ARG_sp) // MOVQ $0, sv.p<>+48(FP)
|
self.Emit("MOVQ", jit.Imm(0), _ARG_sp) // MOVQ $0, sv.p<>+48(FP)
|
||||||
self.Emit("MOVQ", jit.Imm(0), _ARG_vp) // MOVQ $0, sv.p<>+48(FP)
|
self.Emit("MOVQ", jit.Imm(0), _ARG_vp) // MOVQ $0, sv.p<>+48(FP)
|
||||||
self.Emit("MOVQ", jit.Imm(0), _VAR_sv_p) // MOVQ $0, sv.p<>+48(FP)
|
self.Emit("MOVQ", jit.Imm(0), _ARG_sv_p) // MOVQ $0, sv.p<>+48(FP)
|
||||||
self.Emit("MOVQ", jit.Imm(0), _VAR_vk) // MOVQ $0, vk<>+64(FP)
|
self.Emit("MOVQ", jit.Imm(0), _ARG_vk) // MOVQ $0, vk<>+64(FP)
|
||||||
self.Emit("MOVQ", jit.Ptr(_SP, _FP_offs), _BP) // MOVQ _FP_offs(SP), BP
|
self.Emit("MOVQ", jit.Ptr(_SP, _FP_offs), _BP) // MOVQ _FP_offs(SP), BP
|
||||||
self.Emit("ADDQ", jit.Imm(_FP_size), _SP) // ADDQ $_FP_size, SP
|
self.Emit("ADDQ", jit.Imm(_FP_size), _SP) // ADDQ $_FP_size, SP
|
||||||
self.Emit("RET") // RET
|
self.Emit("RET") // RET
|
||||||
|
|
@ -370,9 +372,9 @@ func (self *_Assembler) prologue() {
|
||||||
self.Emit("MOVQ", _SI, _ARG_sb) // MOVQ SI, sb<>+32(FP)
|
self.Emit("MOVQ", _SI, _ARG_sb) // MOVQ SI, sb<>+32(FP)
|
||||||
self.Emit("MOVQ", _SI, _ST) // MOVQ SI, ST
|
self.Emit("MOVQ", _SI, _ST) // MOVQ SI, ST
|
||||||
self.Emit("MOVQ", _R8, _ARG_fv) // MOVQ R8, fv<>+40(FP)
|
self.Emit("MOVQ", _R8, _ARG_fv) // MOVQ R8, fv<>+40(FP)
|
||||||
self.Emit("MOVQ", jit.Imm(0), _VAR_sv_p) // MOVQ $0, sv.p<>+48(FP)
|
self.Emit("MOVQ", jit.Imm(0), _ARG_sv_p) // MOVQ $0, sv.p<>+48(FP)
|
||||||
self.Emit("MOVQ", jit.Imm(0), _VAR_sv_n) // MOVQ $0, sv.n<>+56(FP)
|
self.Emit("MOVQ", jit.Imm(0), _ARG_sv_n) // MOVQ $0, sv.n<>+56(FP)
|
||||||
self.Emit("MOVQ", jit.Imm(0), _VAR_vk) // MOVQ $0, vk<>+64(FP)
|
self.Emit("MOVQ", jit.Imm(0), _ARG_vk) // MOVQ $0, vk<>+64(FP)
|
||||||
self.Emit("MOVQ", jit.Imm(0), _VAR_et) // MOVQ $0, et<>+120(FP)
|
self.Emit("MOVQ", jit.Imm(0), _VAR_et) // MOVQ $0, et<>+120(FP)
|
||||||
// initialize digital buffer first
|
// initialize digital buffer first
|
||||||
self.Emit("MOVQ", jit.Imm(_MaxDigitNums), _VAR_st_Dc) // MOVQ $_MaxDigitNums, ss.Dcap
|
self.Emit("MOVQ", jit.Imm(_MaxDigitNums), _VAR_st_Dc) // MOVQ $_MaxDigitNums, ss.Dcap
|
||||||
|
|
@ -498,8 +500,8 @@ func (self *_Assembler) mismatch_error() {
|
||||||
|
|
||||||
func (self *_Assembler) field_error() {
|
func (self *_Assembler) field_error() {
|
||||||
self.Link(_LB_field_error) // _field_error:
|
self.Link(_LB_field_error) // _field_error:
|
||||||
self.Emit("MOVQ", _VAR_sv_p, _AX) // MOVQ sv.p, AX
|
self.Emit("MOVQ", _ARG_sv_p, _AX) // MOVQ sv.p, AX
|
||||||
self.Emit("MOVQ", _VAR_sv_n, _BX) // MOVQ sv.n, BX
|
self.Emit("MOVQ", _ARG_sv_n, _BX) // MOVQ sv.n, BX
|
||||||
self.call_go(_F_error_field) // CALL_GO error_field
|
self.call_go(_F_error_field) // CALL_GO error_field
|
||||||
self.Sjmp("JMP" , _LB_error) // JMP _error
|
self.Sjmp("JMP" , _LB_error) // JMP _error
|
||||||
}
|
}
|
||||||
|
|
@ -745,11 +747,11 @@ func (self *_Assembler) copy_string() {
|
||||||
self.Emit("MOVQ", _DI, _VAR_bs_p)
|
self.Emit("MOVQ", _DI, _VAR_bs_p)
|
||||||
self.Emit("MOVQ", _SI, _VAR_bs_n)
|
self.Emit("MOVQ", _SI, _VAR_bs_n)
|
||||||
self.Emit("MOVQ", _R9, _VAR_bs_LR)
|
self.Emit("MOVQ", _R9, _VAR_bs_LR)
|
||||||
self.malloc_AX(_SI, _VAR_sv_p)
|
self.malloc_AX(_SI, _ARG_sv_p)
|
||||||
self.Emit("MOVQ", _VAR_bs_p, _BX)
|
self.Emit("MOVQ", _VAR_bs_p, _BX)
|
||||||
self.Emit("MOVQ", _VAR_bs_n, _CX)
|
self.Emit("MOVQ", _VAR_bs_n, _CX)
|
||||||
self.call_go(_F_memmove)
|
self.call_go(_F_memmove)
|
||||||
self.Emit("MOVQ", _VAR_sv_p, _DI)
|
self.Emit("MOVQ", _ARG_sv_p, _DI)
|
||||||
self.Emit("MOVQ", _VAR_bs_n, _SI)
|
self.Emit("MOVQ", _VAR_bs_n, _SI)
|
||||||
self.Emit("MOVQ", _VAR_bs_LR, _R9)
|
self.Emit("MOVQ", _VAR_bs_LR, _R9)
|
||||||
self.Rjmp("JMP", _R9)
|
self.Rjmp("JMP", _R9)
|
||||||
|
|
@ -762,7 +764,7 @@ func (self *_Assembler) escape_string() {
|
||||||
self.Emit("MOVQ" , _SI, _VAR_bs_n)
|
self.Emit("MOVQ" , _SI, _VAR_bs_n)
|
||||||
self.Emit("MOVQ" , _R9, _VAR_bs_LR)
|
self.Emit("MOVQ" , _R9, _VAR_bs_LR)
|
||||||
self.malloc_AX(_SI, _DX) // MALLOC SI, DX
|
self.malloc_AX(_SI, _DX) // MALLOC SI, DX
|
||||||
self.Emit("MOVQ" , _DX, _VAR_sv_p)
|
self.Emit("MOVQ" , _DX, _ARG_sv_p)
|
||||||
self.Emit("MOVQ" , _VAR_bs_p, _DI)
|
self.Emit("MOVQ" , _VAR_bs_p, _DI)
|
||||||
self.Emit("MOVQ" , _VAR_bs_n, _SI)
|
self.Emit("MOVQ" , _VAR_bs_n, _SI)
|
||||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||||
|
|
@ -776,7 +778,7 @@ func (self *_Assembler) escape_string() {
|
||||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||||
self.Emit("MOVQ" , _AX, _SI)
|
self.Emit("MOVQ" , _AX, _SI)
|
||||||
self.Emit("MOVQ" , _VAR_sv_p, _DI)
|
self.Emit("MOVQ" , _ARG_sv_p, _DI)
|
||||||
self.Emit("MOVQ" , _VAR_bs_LR, _R9)
|
self.Emit("MOVQ" , _VAR_bs_LR, _R9)
|
||||||
self.Rjmp("JMP", _R9)
|
self.Rjmp("JMP", _R9)
|
||||||
}
|
}
|
||||||
|
|
@ -787,7 +789,7 @@ func (self *_Assembler) escape_string_twice() {
|
||||||
self.Emit("MOVQ" , _SI, _VAR_bs_n)
|
self.Emit("MOVQ" , _SI, _VAR_bs_n)
|
||||||
self.Emit("MOVQ" , _R9, _VAR_bs_LR)
|
self.Emit("MOVQ" , _R9, _VAR_bs_LR)
|
||||||
self.malloc_AX(_SI, _DX) // MALLOC SI, DX
|
self.malloc_AX(_SI, _DX) // MALLOC SI, DX
|
||||||
self.Emit("MOVQ" , _DX, _VAR_sv_p)
|
self.Emit("MOVQ" , _DX, _ARG_sv_p)
|
||||||
self.Emit("MOVQ" , _VAR_bs_p, _DI)
|
self.Emit("MOVQ" , _VAR_bs_p, _DI)
|
||||||
self.Emit("MOVQ" , _VAR_bs_n, _SI)
|
self.Emit("MOVQ" , _VAR_bs_n, _SI)
|
||||||
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
self.Emit("LEAQ" , _VAR_sr, _CX) // LEAQ sr, CX
|
||||||
|
|
@ -803,7 +805,7 @@ func (self *_Assembler) escape_string_twice() {
|
||||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
self.Sjmp("JS" , _LB_unquote_error) // JS _unquote_error
|
||||||
self.Emit("MOVQ" , _AX, _SI)
|
self.Emit("MOVQ" , _AX, _SI)
|
||||||
self.Emit("MOVQ" , _VAR_sv_p, _DI)
|
self.Emit("MOVQ" , _ARG_sv_p, _DI)
|
||||||
self.Emit("MOVQ" , _VAR_bs_LR, _R9)
|
self.Emit("MOVQ" , _VAR_bs_LR, _R9)
|
||||||
self.Rjmp("JMP", _R9)
|
self.Rjmp("JMP", _R9)
|
||||||
}
|
}
|
||||||
|
|
@ -1029,15 +1031,15 @@ func (self *_Assembler) mapassign_utext(t reflect.Type, addressable bool) {
|
||||||
/* allocate the key, and call the unmarshaler */
|
/* allocate the key, and call the unmarshaler */
|
||||||
self.valloc(vk, _BX) // VALLOC ${vk}, BX
|
self.valloc(vk, _BX) // VALLOC ${vk}, BX
|
||||||
// must spill vk pointer since next call_go may invoke GC
|
// must spill vk pointer since next call_go may invoke GC
|
||||||
self.Emit("MOVQ" , _BX, _VAR_vk)
|
self.Emit("MOVQ" , _BX, _ARG_vk)
|
||||||
self.Emit("MOVQ" , jit.Type(tk), _AX) // MOVQ ${tk}, AX
|
self.Emit("MOVQ" , jit.Type(tk), _AX) // MOVQ ${tk}, AX
|
||||||
self.Emit("MOVQ" , _VAR_sv_p, _CX) // MOVQ sv.p, CX
|
self.Emit("MOVQ" , _ARG_sv_p, _CX) // MOVQ sv.p, CX
|
||||||
self.Emit("MOVQ" , _VAR_sv_n, _DI) // MOVQ sv.n, DI
|
self.Emit("MOVQ" , _ARG_sv_n, _DI) // MOVQ sv.n, DI
|
||||||
self.call_go(_F_decodeTextUnmarshaler) // CALL_GO decodeTextUnmarshaler
|
self.call_go(_F_decodeTextUnmarshaler) // CALL_GO decodeTextUnmarshaler
|
||||||
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
||||||
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
||||||
self.Emit("MOVQ" , _VAR_vk, _AX) // MOVQ VAR.vk, AX
|
self.Emit("MOVQ" , _ARG_vk, _AX) // MOVQ VAR.vk, AX
|
||||||
self.Emit("MOVQ", jit.Imm(0), _VAR_vk)
|
self.Emit("MOVQ", jit.Imm(0), _ARG_vk)
|
||||||
|
|
||||||
/* select the correct assignment function */
|
/* select the correct assignment function */
|
||||||
if !pv {
|
if !pv {
|
||||||
|
|
@ -1061,14 +1063,14 @@ func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool) {
|
||||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
self.Sjmp("JS" , _LB_parsing_error_v) // JS _parse_error_v
|
self.Sjmp("JS" , _LB_parsing_error_v) // JS _parse_error_v
|
||||||
self.slice_from_r(_AX, 0) // SLICE_R AX, $0
|
self.slice_from_r(_AX, 0) // SLICE_R AX, $0
|
||||||
self.Emit("MOVQ" , _DI, _VAR_sv_p) // MOVQ DI, sv.p
|
self.Emit("MOVQ" , _DI, _ARG_sv_p) // MOVQ DI, sv.p
|
||||||
self.Emit("MOVQ" , _SI, _VAR_sv_n) // MOVQ SI, sv.n
|
self.Emit("MOVQ" , _SI, _ARG_sv_n) // MOVQ SI, sv.n
|
||||||
self.unmarshal_func(t, _F_decodeJsonUnmarshaler, deref) // UNMARSHAL json, ${t}, ${deref}
|
self.unmarshal_func(t, _F_decodeJsonUnmarshaler, deref) // UNMARSHAL json, ${t}, ${deref}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) unmarshal_text(t reflect.Type, deref bool) {
|
func (self *_Assembler) unmarshal_text(t reflect.Type, deref bool) {
|
||||||
self.parse_string() // PARSE STRING
|
self.parse_string() // PARSE STRING
|
||||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
self.unquote_once(_ARG_sv_p, _ARG_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||||
self.unmarshal_func(t, _F_decodeTextUnmarshaler, deref) // UNMARSHAL text, ${t}, ${deref}
|
self.unmarshal_func(t, _F_decodeTextUnmarshaler, deref) // UNMARSHAL text, ${t}, ${deref}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1094,8 +1096,8 @@ func (self *_Assembler) unmarshal_func(t reflect.Type, fn obj.Addr, deref bool)
|
||||||
self.Emit("MOVQ", jit.Type(pt), _AX) // MOVQ ${pt}, AX
|
self.Emit("MOVQ", jit.Type(pt), _AX) // MOVQ ${pt}, AX
|
||||||
|
|
||||||
/* set the source string and call the unmarshaler */
|
/* set the source string and call the unmarshaler */
|
||||||
self.Emit("MOVQ" , _VAR_sv_p, _CX) // MOVQ sv.p, CX
|
self.Emit("MOVQ" , _ARG_sv_p, _CX) // MOVQ sv.p, CX
|
||||||
self.Emit("MOVQ" , _VAR_sv_n, _DI) // MOVQ sv.n, DI
|
self.Emit("MOVQ" , _ARG_sv_n, _DI) // MOVQ sv.n, DI
|
||||||
self.call_go(fn) // CALL_GO ${fn}
|
self.call_go(fn) // CALL_GO ${fn}
|
||||||
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
|
||||||
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
self.Sjmp("JNZ" , _LB_error) // JNZ _error
|
||||||
|
|
@ -1557,26 +1559,26 @@ func (self *_Assembler) _asm_OP_map_key_f64(p *_Instr) {
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
|
func (self *_Assembler) _asm_OP_map_key_str(p *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
self.parse_string() // PARSE STRING
|
||||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
self.unquote_once(_ARG_sv_p, _ARG_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||||
if vt := p.vt(); !mapfast(vt) {
|
if vt := p.vt(); !mapfast(vt) {
|
||||||
self.valloc(vt.Key(), _DI)
|
self.valloc(vt.Key(), _DI)
|
||||||
self.Emit("MOVOU", _VAR_sv, _X0)
|
self.Emit("MOVOU", _ARG_sv, _X0)
|
||||||
self.Emit("MOVOU", _X0, jit.Ptr(_DI, 0))
|
self.Emit("MOVOU", _X0, jit.Ptr(_DI, 0))
|
||||||
self.mapassign_std(vt, jit.Ptr(_DI, 0)) // MAPASSIGN string, DI, SI
|
self.mapassign_std(vt, jit.Ptr(_DI, 0)) // MAPASSIGN string, DI, SI
|
||||||
} else {
|
} else {
|
||||||
self.mapassign_str_fast(vt, _VAR_sv_p, _VAR_sv_n) // MAPASSIGN string, DI, SI
|
self.mapassign_str_fast(vt, _ARG_sv_p, _ARG_sv_n) // MAPASSIGN string, DI, SI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_key_utext(p *_Instr) {
|
func (self *_Assembler) _asm_OP_map_key_utext(p *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
self.parse_string() // PARSE STRING
|
||||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
self.unquote_once(_ARG_sv_p, _ARG_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||||
self.mapassign_utext(p.vt(), false) // MAPASSIGN utext, ${p.vt()}, false
|
self.mapassign_utext(p.vt(), false) // MAPASSIGN utext, ${p.vt()}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) _asm_OP_map_key_utext_p(p *_Instr) {
|
func (self *_Assembler) _asm_OP_map_key_utext_p(p *_Instr) {
|
||||||
self.parse_string() // PARSE STRING
|
self.parse_string() // PARSE STRING
|
||||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
self.unquote_once(_ARG_sv_p, _ARG_sv_n, true, true) // UNQUOTE once, sv.p, sv.n
|
||||||
self.mapassign_utext(p.vt(), true) // MAPASSIGN utext, ${p.vt()}, true
|
self.mapassign_utext(p.vt(), true) // MAPASSIGN utext, ${p.vt()}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1650,8 +1652,8 @@ func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
|
||||||
self.Emit("MOVQ" , jit.Imm(-1), _AX) // MOVQ $-1, AX
|
self.Emit("MOVQ" , jit.Imm(-1), _AX) // MOVQ $-1, AX
|
||||||
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, sr
|
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, sr
|
||||||
self.parse_string() // PARSE STRING
|
self.parse_string() // PARSE STRING
|
||||||
self.unquote_once(_VAR_sv_p, _VAR_sv_n, true, false) // UNQUOTE once, sv.p, sv.n
|
self.unquote_once(_ARG_sv_p, _ARG_sv_n, true, false) // UNQUOTE once, sv.p, sv.n
|
||||||
self.Emit("LEAQ" , _VAR_sv, _AX) // LEAQ sv, AX
|
self.Emit("LEAQ" , _ARG_sv, _AX) // LEAQ sv, AX
|
||||||
self.Emit("XORL" , _BX, _BX) // XORL BX, BX
|
self.Emit("XORL" , _BX, _BX) // XORL BX, BX
|
||||||
self.call_go(_F_strhash) // CALL_GO strhash
|
self.call_go(_F_strhash) // CALL_GO strhash
|
||||||
self.Emit("MOVQ" , _AX, _R9) // MOVQ AX, R9
|
self.Emit("MOVQ" , _AX, _R9) // MOVQ AX, R9
|
||||||
|
|
@ -1672,7 +1674,7 @@ func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
|
||||||
self.Emit("CMPQ" , _R8, _R9) // CMPQ R8, R9
|
self.Emit("CMPQ" , _R8, _R9) // CMPQ R8, R9
|
||||||
self.Sjmp("JNE" , "_loop_{n}") // JNE _loop_{n}
|
self.Sjmp("JNE" , "_loop_{n}") // JNE _loop_{n}
|
||||||
self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_Name + 8), _DX) // MOVQ FieldEntry.Name+8(DI), DX
|
self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_Name + 8), _DX) // MOVQ FieldEntry.Name+8(DI), DX
|
||||||
self.Emit("CMPQ" , _DX, _VAR_sv_n) // CMPQ DX, sv.n
|
self.Emit("CMPQ" , _DX, _ARG_sv_n) // CMPQ DX, sv.n
|
||||||
self.Sjmp("JNE" , "_loop_{n}") // JNE _loop_{n}
|
self.Sjmp("JNE" , "_loop_{n}") // JNE _loop_{n}
|
||||||
self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_ID), _R8) // MOVQ FieldEntry.ID(DI), R8
|
self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_ID), _R8) // MOVQ FieldEntry.ID(DI), R8
|
||||||
self.Emit("MOVQ" , _AX, _VAR_ss_AX) // MOVQ AX, ss.AX
|
self.Emit("MOVQ" , _AX, _VAR_ss_AX) // MOVQ AX, ss.AX
|
||||||
|
|
@ -1680,7 +1682,7 @@ func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
|
||||||
self.Emit("MOVQ" , _SI, _VAR_ss_SI) // MOVQ SI, ss.SI
|
self.Emit("MOVQ" , _SI, _VAR_ss_SI) // MOVQ SI, ss.SI
|
||||||
self.Emit("MOVQ" , _R8, _VAR_ss_R8) // MOVQ R8, ss.R8
|
self.Emit("MOVQ" , _R8, _VAR_ss_R8) // MOVQ R8, ss.R8
|
||||||
self.Emit("MOVQ" , _R9, _VAR_ss_R9) // MOVQ R9, ss.R9
|
self.Emit("MOVQ" , _R9, _VAR_ss_R9) // MOVQ R9, ss.R9
|
||||||
self.Emit("MOVQ" , _VAR_sv_p, _AX) // MOVQ _VAR_sv_p, AX
|
self.Emit("MOVQ" , _ARG_sv_p, _AX) // MOVQ _VAR_sv_p, AX
|
||||||
self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_Name), _CX) // MOVQ FieldEntry.Name(DI), CX
|
self.Emit("MOVQ" , jit.Ptr(_DI, _Fe_Name), _CX) // MOVQ FieldEntry.Name(DI), CX
|
||||||
self.Emit("MOVQ" , _CX, _BX) // MOVQ CX, 8(SP)
|
self.Emit("MOVQ" , _CX, _BX) // MOVQ CX, 8(SP)
|
||||||
self.Emit("MOVQ" , _DX, _CX) // MOVQ DX, 16(SP)
|
self.Emit("MOVQ" , _DX, _CX) // MOVQ DX, 16(SP)
|
||||||
|
|
@ -1697,8 +1699,8 @@ func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
|
||||||
self.Sjmp("JMP" , "_end_{n}") // JMP _end_{n}
|
self.Sjmp("JMP" , "_end_{n}") // JMP _end_{n}
|
||||||
self.Link("_try_lowercase_{n}") // _try_lowercase_{n}:
|
self.Link("_try_lowercase_{n}") // _try_lowercase_{n}:
|
||||||
self.Emit("MOVQ" , jit.Imm(referenceFields(p.vf())), _AX) // MOVQ ${p.vf()}, AX
|
self.Emit("MOVQ" , jit.Imm(referenceFields(p.vf())), _AX) // MOVQ ${p.vf()}, AX
|
||||||
self.Emit("MOVQ", _VAR_sv_p, _BX) // MOVQ sv, BX
|
self.Emit("MOVQ", _ARG_sv_p, _BX) // MOVQ sv, BX
|
||||||
self.Emit("MOVQ", _VAR_sv_n, _CX) // MOVQ sv, CX
|
self.Emit("MOVQ", _ARG_sv_n, _CX) // MOVQ sv, CX
|
||||||
self.call_go(_F_FieldMap_GetCaseInsensitive) // CALL_GO FieldMap::GetCaseInsensitive
|
self.call_go(_F_FieldMap_GetCaseInsensitive) // CALL_GO FieldMap::GetCaseInsensitive
|
||||||
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, _VAR_sr
|
self.Emit("MOVQ" , _AX, _VAR_sr) // MOVQ AX, _VAR_sr
|
||||||
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ const (
|
||||||
const (
|
const (
|
||||||
_INT_SIZE = 32 << (^uint(0) >> 63)
|
_INT_SIZE = 32 << (^uint(0) >> 63)
|
||||||
_PTR_SIZE = 32 << (^uintptr(0) >> 63)
|
_PTR_SIZE = 32 << (^uintptr(0) >> 63)
|
||||||
|
_PTR_BYTE = unsafe.Sizeof(uintptr(0))
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,9 @@ func pretouchType(_vt reflect.Type, opts option.CompileOptions) (map[reflect.Typ
|
||||||
if pp, err := compiler.compile(_vt); err != nil {
|
if pp, err := compiler.compile(_vt); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return newAssembler(pp).Load(), nil
|
as := newAssembler(pp)
|
||||||
|
as.name = _vt.String()
|
||||||
|
return as.Load(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ type _ValueDecoder struct {
|
||||||
|
|
||||||
func (self *_ValueDecoder) build() uintptr {
|
func (self *_ValueDecoder) build() uintptr {
|
||||||
self.Init(self.compile)
|
self.Init(self.compile)
|
||||||
return *(*uintptr)(self.LoadWithFaker("decode_value", _VD_size, _VD_args, _Decoder_Generic_Shadow))
|
return *(*uintptr)(self.Load("decode_value", _VD_size, _VD_args, argPtrs_generic, localPtrs_generic))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Function Calling Helpers **/
|
/** Function Calling Helpers **/
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build go1.17,!go1.20
|
//go:build go1.17 && !go1.21
|
||||||
|
// +build go1.17,!go1.21
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
|
@ -84,7 +85,7 @@ var (
|
||||||
|
|
||||||
func (self *_ValueDecoder) build() uintptr {
|
func (self *_ValueDecoder) build() uintptr {
|
||||||
self.Init(self.compile)
|
self.Init(self.compile)
|
||||||
return *(*uintptr)(self.LoadWithFaker("decode_value", _VD_size, _VD_args, _Decoder_Generic_Shadow))
|
return *(*uintptr)(self.Load("decode_value", _VD_size, _VD_args, argPtrs_generic, localPtrs_generic))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Function Calling Helpers **/
|
/** Function Calling Helpers **/
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// +build go1.17,!go1.20
|
// +build go1.17,!go1.21
|
||||||
|
|
||||||
//
|
//
|
||||||
// Copyright 2021 ByteDance Inc.
|
// Copyright 2021 ByteDance Inc.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build !race
|
||||||
// +build !race
|
// +build !race
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -19,9 +20,10 @@
|
||||||
package decoder
|
package decoder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`testing`
|
|
||||||
`unsafe`
|
|
||||||
`runtime`
|
`runtime`
|
||||||
|
`testing`
|
||||||
|
`time`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
)
|
)
|
||||||
|
|
@ -34,7 +36,7 @@ func TestStringReferring(t *testing.T) {
|
||||||
println("malloc *byte ", sp)
|
println("malloc *byte ", sp)
|
||||||
runtime.SetFinalizer(sp, func(sp *byte){
|
runtime.SetFinalizer(sp, func(sp *byte){
|
||||||
referred = false
|
referred = false
|
||||||
println("*byte ", sp, " got free")
|
println("*byte ", sp, " got free 1")
|
||||||
})
|
})
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
println("first GC")
|
println("first GC")
|
||||||
|
|
@ -50,6 +52,7 @@ func TestStringReferring(t *testing.T) {
|
||||||
}
|
}
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
println("second GC")
|
println("second GC")
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
if referred {
|
if referred {
|
||||||
t.Fatal("*byte is being referred")
|
t.Fatal("*byte is being referred")
|
||||||
}
|
}
|
||||||
|
|
@ -73,6 +76,7 @@ func TestStringReferring(t *testing.T) {
|
||||||
}
|
}
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
println("second GC")
|
println("second GC")
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
if referred {
|
if referred {
|
||||||
t.Fatal("*byte is being referred")
|
t.Fatal("*byte is being referred")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package decoder
|
package decoder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`errors`
|
|
||||||
`sync`
|
`sync`
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
|
||||||
|
|
@ -82,37 +81,15 @@ var _KeepAlive struct {
|
||||||
frame_generic [_VD_offs]byte
|
frame_generic [_VD_offs]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
var errCallShadow = errors.New("DON'T CALL THIS!")
|
var (
|
||||||
|
argPtrs = []bool{true, false, false, true, true, false, true, false, true}
|
||||||
|
localPtrs = []bool{}
|
||||||
|
)
|
||||||
|
|
||||||
// Faker func of _Decoder, used to export its stackmap as _Decoder's
|
var (
|
||||||
func _Decoder_Shadow(s string, i int, vp unsafe.Pointer, sb *_Stack, fv uint64, sv string, vk unsafe.Pointer) (ret int, err error) {
|
argPtrs_generic = []bool{true}
|
||||||
// align to assembler_amd64.go: _FP_offs
|
localPtrs_generic = []bool{}
|
||||||
var frame [_FP_offs]byte
|
)
|
||||||
|
|
||||||
// keep all args and stacks alive
|
|
||||||
_KeepAlive.s = s
|
|
||||||
_KeepAlive.i = i
|
|
||||||
_KeepAlive.vp = vp
|
|
||||||
_KeepAlive.sb = sb
|
|
||||||
_KeepAlive.fv = fv
|
|
||||||
_KeepAlive.ret = ret
|
|
||||||
_KeepAlive.err = err
|
|
||||||
_KeepAlive.sv = sv
|
|
||||||
_KeepAlive.vk = vk
|
|
||||||
_KeepAlive.frame_decoder = frame
|
|
||||||
|
|
||||||
return 0, errCallShadow
|
|
||||||
}
|
|
||||||
|
|
||||||
// Faker func of _Decoder_Generic, used to export its stackmap
|
|
||||||
func _Decoder_Generic_Shadow(sb *_Stack) {
|
|
||||||
// align to generic_amd64.go: _VD_offs
|
|
||||||
var frame [_VD_offs]byte
|
|
||||||
|
|
||||||
// must keep sb noticeable to GC
|
|
||||||
_KeepAlive.sb = sb
|
|
||||||
_KeepAlive.frame_generic = frame
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStack() *_Stack {
|
func newStack() *_Stack {
|
||||||
if ret := stackPool.Get(); ret == nil {
|
if ret := stackPool.Get(); ret == nil {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// +build go1.15,!go1.20
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
111
decoder/stubs_go120.go
Normal file
111
decoder/stubs_go120.go
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
// +build 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 decoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
`reflect`
|
||||||
|
|
||||||
|
_ `github.com/chenzhuoyu/base64x`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname _subr__b64decode github.com/chenzhuoyu/base64x._subr__b64decode
|
||||||
|
var _subr__b64decode uintptr
|
||||||
|
|
||||||
|
// runtime.maxElementSize
|
||||||
|
const _max_map_element_size uintptr = 128
|
||||||
|
|
||||||
|
func mapfast(vt reflect.Type) bool {
|
||||||
|
return vt.Elem().Size() <= _max_map_element_size
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:linkname throw runtime.throw
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func throw(s string)
|
||||||
|
|
||||||
|
//go:linkname convT64 runtime.convT64
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func convT64(v uint64) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname convTslice runtime.convTslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func convTslice(v []byte) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname convTstring runtime.convTstring
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func convTstring(v string) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memequal runtime.memequal
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memequal(a unsafe.Pointer, b unsafe.Pointer, size uintptr) bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname mallocgc runtime.mallocgc
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mallocgc(size uintptr, typ *rt.GoType, needzero bool) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname makeslice runtime.makeslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func makeslice(et *rt.GoType, len int, cap int) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname growslice reflect.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:linkname makemap_small runtime.makemap_small
|
||||||
|
func makemap_small() unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign runtime.mapassign
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_fast32 runtime.mapassign_fast32
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_fast32(t *rt.GoType, h unsafe.Pointer, k uint32) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_fast64 runtime.mapassign_fast64
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_fast64(t *rt.GoType, h unsafe.Pointer, k uint64) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_fast64ptr runtime.mapassign_fast64ptr
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_fast64ptr(t *rt.GoType, h unsafe.Pointer, k unsafe.Pointer) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:linkname mapassign_faststr runtime.mapassign_faststr
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapassign_faststr(t *rt.GoType, h unsafe.Pointer, s string) unsafe.Pointer
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
//go:linkname memclrHasPointers runtime.memclrHasPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrHasPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
@ -19,7 +19,7 @@ package decoder
|
||||||
import (
|
import (
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/loader`
|
`github.com/bytedance/sonic/loader`
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,7 @@ type _Assembler struct {
|
||||||
jit.BaseAssembler
|
jit.BaseAssembler
|
||||||
p _Program
|
p _Program
|
||||||
x int
|
x int
|
||||||
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAssembler(p _Program) *_Assembler {
|
func newAssembler(p _Program) *_Assembler {
|
||||||
|
|
@ -184,9 +185,8 @@ func newAssembler(p _Program) *_Assembler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Assembler Interface **/
|
/** Assembler Interface **/
|
||||||
|
|
||||||
func (self *_Assembler) Load() _Encoder {
|
func (self *_Assembler) Load() _Encoder {
|
||||||
return ptoenc(self.BaseAssembler.LoadWithFaker("json_encoder", _FP_size, _FP_args, _Encoder_Shadow))
|
return ptoenc(self.BaseAssembler.Load("encode_"+self.name, _FP_size, _FP_args, argPtrs, localPtrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) Init(p _Program) *_Assembler {
|
func (self *_Assembler) Init(p _Program) *_Assembler {
|
||||||
|
|
@ -483,10 +483,6 @@ func (self *_Assembler) load_buffer() {
|
||||||
|
|
||||||
/** Function Interface Helpers **/
|
/** Function Interface Helpers **/
|
||||||
|
|
||||||
var (
|
|
||||||
_F_assertI2I = jit.Func(assertI2I)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (self *_Assembler) call(pc obj.Addr) {
|
func (self *_Assembler) call(pc obj.Addr) {
|
||||||
self.Emit("MOVQ", pc, _AX) // MOVQ $pc, AX
|
self.Emit("MOVQ", pc, _AX) // MOVQ $pc, AX
|
||||||
self.Rjmp("CALL", _AX) // CALL AX
|
self.Rjmp("CALL", _AX) // CALL AX
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build go1.17,!go1.20
|
//go:build go1.17 && !go1.21
|
||||||
|
// +build go1.17,!go1.21
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
|
@ -182,6 +183,7 @@ type _Assembler struct {
|
||||||
jit.BaseAssembler
|
jit.BaseAssembler
|
||||||
p _Program
|
p _Program
|
||||||
x int
|
x int
|
||||||
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAssembler(p _Program) *_Assembler {
|
func newAssembler(p _Program) *_Assembler {
|
||||||
|
|
@ -191,7 +193,7 @@ func newAssembler(p _Program) *_Assembler {
|
||||||
/** Assembler Interface **/
|
/** Assembler Interface **/
|
||||||
|
|
||||||
func (self *_Assembler) Load() _Encoder {
|
func (self *_Assembler) Load() _Encoder {
|
||||||
return ptoenc(self.BaseAssembler.LoadWithFaker("json_encoder", _FP_size, _FP_args, _Encoder_Shadow))
|
return ptoenc(self.BaseAssembler.Load("encode_"+self.name, _FP_size, _FP_args, argPtrs, localPtrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_Assembler) Init(p _Program) *_Assembler {
|
func (self *_Assembler) Init(p _Program) *_Assembler {
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,7 @@ const (
|
||||||
const (
|
const (
|
||||||
_INT_SIZE = 32 << (^uint(0) >> 63)
|
_INT_SIZE = 32 << (^uint(0) >> 63)
|
||||||
_PTR_SIZE = 32 << (^uintptr(0) >> 63)
|
_PTR_SIZE = 32 << (^uintptr(0) >> 63)
|
||||||
|
_PTR_BYTE = unsafe.Sizeof(uintptr(0))
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// +build go1.17,!go1.20
|
// +build go1.17,!go1.21
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,9 @@ func makeEncoder(vt *rt.GoType, ex ...interface{}) (interface{}, error) {
|
||||||
if pp, err := newCompiler().compile(vt.Pack(), ex[0].(bool)); err != nil {
|
if pp, err := newCompiler().compile(vt.Pack(), ex[0].(bool)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return newAssembler(pp).Load(), nil
|
as := newAssembler(pp)
|
||||||
|
as.name = vt.String()
|
||||||
|
return as.Load(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,7 +158,9 @@ func pretouchType(_vt reflect.Type, opts option.CompileOptions, v uint8) (map[re
|
||||||
if pp, err := compiler.compile(_vt, ex[0].(bool)); err != nil {
|
if pp, err := compiler.compile(_vt, ex[0].(bool)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
return newAssembler(pp).Load(), nil
|
as := newAssembler(pp)
|
||||||
|
as.name = vt.String()
|
||||||
|
return as.Load(), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
`encoding/json`
|
`encoding/json`
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/jit`
|
||||||
`github.com/bytedance/sonic/internal/native`
|
`github.com/bytedance/sonic/internal/native`
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
)
|
)
|
||||||
|
|
@ -111,3 +112,24 @@ func encodeTextMarshaler(buf *[]byte, val encoding.TextMarshaler, opt Options) e
|
||||||
return encodeString(buf, rt.Mem2Str(ret) )
|
return encodeString(buf, rt.Mem2Str(ret) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
argPtrs = []bool { true, true, true, false }
|
||||||
|
localPtrs = []bool{}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_F_assertI2I = jit.Func(assertI2I)
|
||||||
|
)
|
||||||
|
|
||||||
|
func asText(v unsafe.Pointer) (string, error) {
|
||||||
|
text := assertI2I(_T_encoding_TextMarshaler, *(*rt.GoIface)(v))
|
||||||
|
r, e := (*(*encoding.TextMarshaler)(unsafe.Pointer(&text))).MarshalText()
|
||||||
|
return rt.Mem2Str(r), e
|
||||||
|
}
|
||||||
|
|
||||||
|
func asJson(v unsafe.Pointer) (string, error) {
|
||||||
|
text := assertI2I(_T_json_Marshaler, *(*rt.GoIface)(v))
|
||||||
|
r, e := (*(*json.Marshaler)(unsafe.Pointer(&text))).MarshalJSON()
|
||||||
|
return rt.Mem2Str(r), e
|
||||||
|
}
|
||||||
|
|
@ -20,8 +20,6 @@ package encoder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`unsafe`
|
`unsafe`
|
||||||
`encoding`
|
|
||||||
`encoding/json`
|
|
||||||
|
|
||||||
_ `github.com/chenzhuoyu/base64x`
|
_ `github.com/chenzhuoyu/base64x`
|
||||||
|
|
||||||
|
|
@ -61,18 +59,6 @@ func isValidNumber(s string) bool
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
func asText(v unsafe.Pointer) (string, error) {
|
|
||||||
text := assertI2I(_T_encoding_TextMarshaler, *(*rt.GoIface)(v))
|
|
||||||
r, e := (*(*encoding.TextMarshaler)(unsafe.Pointer(&text))).MarshalText()
|
|
||||||
return rt.Mem2Str(r), e
|
|
||||||
}
|
|
||||||
|
|
||||||
func asJson(v unsafe.Pointer) (string, error) {
|
|
||||||
text := assertI2I(_T_json_Marshaler, *(*rt.GoIface)(v))
|
|
||||||
r, e := (*(*json.Marshaler)(unsafe.Pointer(&text))).MarshalJSON()
|
|
||||||
return rt.Mem2Str(r), e
|
|
||||||
}
|
|
||||||
|
|
||||||
var _runtime_writeBarrier uintptr = rt.GcwbAddr()
|
var _runtime_writeBarrier uintptr = rt.GcwbAddr()
|
||||||
|
|
||||||
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
|
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,10 @@
|
||||||
package encoder
|
package encoder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`encoding`
|
|
||||||
`encoding/json`
|
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
|
||||||
_ `github.com/chenzhuoyu/base64x`
|
_ `github.com/chenzhuoyu/base64x`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/jit`
|
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -41,9 +38,9 @@ func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
//go:linkname assertI2I2 runtime.assertI2I2
|
//go:linkname assertI2I runtime.assertI2I2
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func assertI2I2(inter *rt.GoType, i rt.GoIface) rt.GoIface
|
func assertI2I(inter *rt.GoType, i rt.GoIface) rt.GoIface
|
||||||
|
|
||||||
//go:linkname mapiternext runtime.mapiternext
|
//go:linkname mapiternext runtime.mapiternext
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
|
|
@ -62,22 +59,6 @@ func isValidNumber(s string) bool
|
||||||
//goland:noinspection GoUnusedParameter
|
//goland:noinspection GoUnusedParameter
|
||||||
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
var (
|
|
||||||
_F_assertI2I = jit.Func(assertI2I2)
|
|
||||||
)
|
|
||||||
|
|
||||||
func asText(v unsafe.Pointer) (string, error) {
|
|
||||||
text := assertI2I2(_T_encoding_TextMarshaler, *(*rt.GoIface)(v))
|
|
||||||
r, e := (*(*encoding.TextMarshaler)(unsafe.Pointer(&text))).MarshalText()
|
|
||||||
return rt.Mem2Str(r), e
|
|
||||||
}
|
|
||||||
|
|
||||||
func asJson(v unsafe.Pointer) (string, error) {
|
|
||||||
text := assertI2I2(_T_json_Marshaler, *(*rt.GoIface)(v))
|
|
||||||
r, e := (*(*json.Marshaler)(unsafe.Pointer(&text))).MarshalJSON()
|
|
||||||
return rt.Mem2Str(r), e
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:linkname _runtime_writeBarrier runtime.writeBarrier
|
//go:linkname _runtime_writeBarrier runtime.writeBarrier
|
||||||
var _runtime_writeBarrier uintptr
|
var _runtime_writeBarrier uintptr
|
||||||
|
|
||||||
|
|
|
||||||
66
encoder/stubs_go120.go
Normal file
66
encoder/stubs_go120.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
// +build 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 encoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
_ `github.com/chenzhuoyu/base64x`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname _subr__b64encode github.com/chenzhuoyu/base64x._subr__b64encode
|
||||||
|
var _subr__b64encode uintptr
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memmove runtime.memmove
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname growslice reflect.growslice
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func growslice(et *rt.GoType, old rt.GoSlice, cap int) rt.GoSlice
|
||||||
|
|
||||||
|
//go:linkname assertI2I runtime.assertI2I2
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func assertI2I(inter *rt.GoType, i rt.GoIface) rt.GoIface
|
||||||
|
|
||||||
|
//go:linkname mapiternext runtime.mapiternext
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapiternext(it *rt.GoMapIterator)
|
||||||
|
|
||||||
|
//go:linkname mapiterinit runtime.mapiterinit
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mapiterinit(t *rt.GoMapType, m *rt.GoMap, it *rt.GoMapIterator)
|
||||||
|
|
||||||
|
//go:linkname isValidNumber encoding/json.isValidNumber
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func isValidNumber(s string) bool
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
|
||||||
|
|
||||||
|
//go:linkname _runtime_writeBarrier runtime.writeBarrier
|
||||||
|
var _runtime_writeBarrier uintptr
|
||||||
|
|
||||||
|
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
|
||||||
|
func gcWriteBarrierAX()
|
||||||
|
|
@ -20,7 +20,7 @@ import (
|
||||||
`encoding/json`
|
`encoding/json`
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/loader`
|
`github.com/bytedance/sonic/loader`
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,37 @@ package benchmark_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`encoding/json`
|
`encoding/json`
|
||||||
|
`os`
|
||||||
|
`runtime`
|
||||||
|
`runtime/debug`
|
||||||
`testing`
|
`testing`
|
||||||
|
`time`
|
||||||
|
|
||||||
gojson `github.com/goccy/go-json`
|
gojson `github.com/goccy/go-json`
|
||||||
jsoniter `github.com/json-iterator/go`
|
jsoniter `github.com/json-iterator/go`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
debugSyncGC = os.Getenv("SONIC_SYNC_GC") != ""
|
||||||
|
debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
go func () {
|
||||||
|
if !debugAsyncGC {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println("Begin GC looping...")
|
||||||
|
for {
|
||||||
|
runtime.GC()
|
||||||
|
debug.FreeOSMemory()
|
||||||
|
}
|
||||||
|
println("stop GC looping!")
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
m.Run()
|
||||||
|
}
|
||||||
|
|
||||||
var _GenericValue interface{}
|
var _GenericValue interface{}
|
||||||
var _BindingValue TwitterStruct
|
var _BindingValue TwitterStruct
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,38 @@ package unit_test
|
||||||
import (
|
import (
|
||||||
`bytes`
|
`bytes`
|
||||||
`encoding/json`
|
`encoding/json`
|
||||||
|
`os`
|
||||||
|
`runtime`
|
||||||
|
`runtime/debug`
|
||||||
`testing`
|
`testing`
|
||||||
|
`time`
|
||||||
|
|
||||||
`github.com/bytedance/sonic`
|
`github.com/bytedance/sonic`
|
||||||
jsoniter `github.com/json-iterator/go`
|
jsoniter `github.com/json-iterator/go`
|
||||||
`github.com/stretchr/testify/require`
|
`github.com/stretchr/testify/require`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
debugSyncGC = os.Getenv("SONIC_SYNC_GC") != ""
|
||||||
|
debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
go func () {
|
||||||
|
if !debugAsyncGC {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println("Begin GC looping...")
|
||||||
|
for {
|
||||||
|
runtime.GC()
|
||||||
|
debug.FreeOSMemory()
|
||||||
|
}
|
||||||
|
println("stop GC looping!")
|
||||||
|
}()
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
m.Run()
|
||||||
|
}
|
||||||
|
|
||||||
var jt = jsoniter.Config{
|
var jt = jsoniter.Config{
|
||||||
ValidateJsonRawMessage: true,
|
ValidateJsonRawMessage: true,
|
||||||
}.Froze()
|
}.Froze()
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import (
|
||||||
`strings`
|
`strings`
|
||||||
`sync`
|
`sync`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/loader`
|
`github.com/bytedance/sonic/loader`
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
`github.com/twitchyliquid64/golang-asm/obj`
|
`github.com/twitchyliquid64/golang-asm/obj`
|
||||||
`github.com/twitchyliquid64/golang-asm/obj/x86`
|
`github.com/twitchyliquid64/golang-asm/obj/x86`
|
||||||
|
|
@ -202,14 +202,17 @@ func (self *BaseAssembler) Init(f func()) {
|
||||||
self.o = sync.Once{}
|
self.o = sync.Once{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BaseAssembler) Load(fn string, fp int, args int) loader.Function {
|
var jitLoader = loader.Loader{
|
||||||
self.build()
|
Name: "sonic.jit.",
|
||||||
return loader.Loader(self.c).Load(fn, fp, args)
|
File: "github.com/bytedance/sonic/jit.go",
|
||||||
|
Options: loader.Options{
|
||||||
|
NoPreempt: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BaseAssembler) LoadWithFaker(fn string, fp int, args int, faker interface{}) loader.Function {
|
func (self *BaseAssembler) Load(name string, frameSize int, argSize int, argStackmap []bool, localStackmap []bool) loader.Function {
|
||||||
self.build()
|
self.build()
|
||||||
return loader.Loader(self.c).LoadWithFaker(fn, fp, args, faker)
|
return jitLoader.LoadOne(self.c, name, frameSize, argSize, argStackmap, localStackmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Assembler Stages **/
|
/** Assembler Stages **/
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@
|
||||||
package loader
|
package loader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
`reflect`
|
||||||
`sync`
|
`sync`
|
||||||
`unsafe`
|
`unsafe`
|
||||||
`reflect`
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:linkname lastmoduledatap runtime.lastmoduledatap
|
//go:linkname lastmoduledatap runtime.lastmoduledatap
|
||||||
|
|
@ -92,3 +94,31 @@ func stackMap(f interface{}) (args uintptr, locals uintptr) {
|
||||||
fi := findfunc(fv.Pointer())
|
fi := findfunc(fv.Pointer())
|
||||||
return uintptr(funcdata(fi, uint8(_FUNCDATA_ArgsPointerMaps))), uintptr(funcdata(fi, uint8(_FUNCDATA_LocalsPointerMaps)))
|
return uintptr(funcdata(fi, uint8(_FUNCDATA_ArgsPointerMaps))), uintptr(funcdata(fi, uint8(_FUNCDATA_LocalsPointerMaps)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var moduleCache = struct{
|
||||||
|
m map[*_ModuleData][]byte
|
||||||
|
l sync.Mutex
|
||||||
|
}{
|
||||||
|
m : make(map[*_ModuleData][]byte),
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheStackmap(argPtrs []bool, localPtrs []bool, mod *_ModuleData) (argptrs uintptr, localptrs uintptr) {
|
||||||
|
as := rt.StackMapBuilder{}
|
||||||
|
for _, b := range argPtrs {
|
||||||
|
as.AddField(b)
|
||||||
|
}
|
||||||
|
ab, _ := as.Build().MarshalBinary()
|
||||||
|
ls := rt.StackMapBuilder{}
|
||||||
|
for _, b := range localPtrs {
|
||||||
|
ls.AddField(b)
|
||||||
|
}
|
||||||
|
lb, _ := ls.Build().MarshalBinary()
|
||||||
|
cache := make([]byte, len(ab) + len(lb))
|
||||||
|
copy(cache, ab)
|
||||||
|
copy(cache[len(ab):], lb)
|
||||||
|
moduleCache.l.Lock()
|
||||||
|
moduleCache.m[mod] = cache
|
||||||
|
moduleCache.l.Unlock()
|
||||||
|
return uintptr(rt.IndexByte(cache, 0)), uintptr(rt.IndexByte(cache, len(ab)))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -98,7 +98,8 @@ var findFuncTab = &_FindFuncBucket {
|
||||||
idx: 1,
|
idx: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {
|
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||||
|
mod := new(_ModuleData)
|
||||||
minpc := pc
|
minpc := pc
|
||||||
maxpc := pc + size
|
maxpc := pc + size
|
||||||
|
|
||||||
|
|
@ -111,6 +112,9 @@ func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args in
|
||||||
4 << (^uintptr(0) >> 63), // ptrSize : 4 << (^uintptr(0) >> 63)
|
4 << (^uintptr(0) >> 63), // ptrSize : 4 << (^uintptr(0) >> 63)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cache arg and local stackmap
|
||||||
|
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||||
|
|
||||||
/* add the function name */
|
/* add the function name */
|
||||||
noff := len(pclnt)
|
noff := len(pclnt)
|
||||||
pclnt = append(append(pclnt, name...), 0)
|
pclnt = append(append(pclnt, name...), 0)
|
||||||
|
|
@ -127,8 +131,8 @@ func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args in
|
||||||
args : int32(args),
|
args : int32(args),
|
||||||
pcsp : int32(pcsp),
|
pcsp : int32(pcsp),
|
||||||
nfuncdata : 2,
|
nfuncdata : 2,
|
||||||
argptrs : argptrs,
|
argptrs : uintptr(argptrs),
|
||||||
localptrs : localptrs,
|
localptrs : uintptr(localptrs),
|
||||||
}
|
}
|
||||||
|
|
||||||
/* align the func to 8 bytes */
|
/* align the func to 8 bytes */
|
||||||
|
|
@ -148,7 +152,7 @@ func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args in
|
||||||
}
|
}
|
||||||
|
|
||||||
/* module data */
|
/* module data */
|
||||||
mod := &_ModuleData {
|
*mod = _ModuleData {
|
||||||
pclntable : pclnt,
|
pclntable : pclnt,
|
||||||
ftab : tab,
|
ftab : tab,
|
||||||
findfunctab : findFuncTab,
|
findfunctab : findFuncTab,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build go1.16 && !go1.18
|
||||||
// +build go1.16,!go1.18
|
// +build go1.16,!go1.18
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -126,10 +127,15 @@ func makePCtab(fp int) []byte {
|
||||||
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {
|
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||||
|
mod := new(_ModuleData)
|
||||||
|
|
||||||
minpc := pc
|
minpc := pc
|
||||||
maxpc := pc + size
|
maxpc := pc + size
|
||||||
|
|
||||||
|
// cache arg and local stackmap
|
||||||
|
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||||
|
|
||||||
/* function entry */
|
/* function entry */
|
||||||
lnt := []_Func {{
|
lnt := []_Func {{
|
||||||
entry : pc,
|
entry : pc,
|
||||||
|
|
@ -137,8 +143,8 @@ func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args in
|
||||||
args : int32(args),
|
args : int32(args),
|
||||||
pcsp : 1,
|
pcsp : 1,
|
||||||
nfuncdata : 2,
|
nfuncdata : 2,
|
||||||
argptrs : argptrs,
|
argptrs : uintptr(argptrs),
|
||||||
localptrs : localptrs,
|
localptrs : uintptr(localptrs),
|
||||||
}}
|
}}
|
||||||
|
|
||||||
/* function table */
|
/* function table */
|
||||||
|
|
@ -149,7 +155,7 @@ func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args in
|
||||||
}
|
}
|
||||||
|
|
||||||
/* module data */
|
/* module data */
|
||||||
mod := &_ModuleData {
|
*mod = _ModuleData {
|
||||||
pcHeader : modHeader,
|
pcHeader : modHeader,
|
||||||
funcnametab : append(append([]byte{0}, name...), 0),
|
funcnametab : append(append([]byte{0}, name...), 0),
|
||||||
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,9 @@ func makePCtab(fp int) []byte {
|
||||||
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {
|
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||||
|
mod := new(_ModuleData)
|
||||||
|
|
||||||
minpc := pc
|
minpc := pc
|
||||||
maxpc := pc + size
|
maxpc := pc + size
|
||||||
|
|
||||||
|
|
@ -142,6 +144,9 @@ func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args in
|
||||||
textStart: minpc,
|
textStart: minpc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cache arg and local stackmap
|
||||||
|
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||||
|
|
||||||
base := argptrs
|
base := argptrs
|
||||||
if argptrs > localptrs {
|
if argptrs > localptrs {
|
||||||
base = localptrs
|
base = localptrs
|
||||||
|
|
@ -173,7 +178,7 @@ func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args in
|
||||||
pclntab = append(pclntab, rt.BytesFrom(plnt, nlnt, nlnt)...)
|
pclntab = append(pclntab, rt.BytesFrom(plnt, nlnt, nlnt)...)
|
||||||
|
|
||||||
/* module data */
|
/* module data */
|
||||||
mod := &_ModuleData {
|
*mod = _ModuleData {
|
||||||
pcHeader : modHeader,
|
pcHeader : modHeader,
|
||||||
funcnametab : append(append([]byte{0}, name...), 0),
|
funcnametab : append(append([]byte{0}, name...), 0),
|
||||||
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
||||||
|
|
|
||||||
201
internal/loader/funcdata_go120.go
Normal file
201
internal/loader/funcdata_go120.go
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
// +build 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 (
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
)
|
||||||
|
|
||||||
|
// A FuncFlag holds bits about a function.
|
||||||
|
// This list must match the list in cmd/internal/objabi/funcid.go.
|
||||||
|
type funcFlag uint8
|
||||||
|
|
||||||
|
type _Func struct {
|
||||||
|
entryOff uint32 // start pc
|
||||||
|
nameoff int32 // function name
|
||||||
|
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 funcFlag
|
||||||
|
_ [1]byte // pad
|
||||||
|
nfuncdata uint8 // must be last
|
||||||
|
argptrs uint32
|
||||||
|
localptrs 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
|
||||||
|
length uintptr // section length
|
||||||
|
baseaddr uintptr // relocated section address
|
||||||
|
}
|
||||||
|
|
||||||
|
type _ModuleData struct {
|
||||||
|
pcHeader *_PCHeader
|
||||||
|
funcnametab []byte
|
||||||
|
cutab []uint32
|
||||||
|
filetab []byte
|
||||||
|
pctab []byte
|
||||||
|
pclntable []byte
|
||||||
|
ftab []_FuncTab
|
||||||
|
findfunctab *_FindFuncBucket
|
||||||
|
minpc, maxpc uintptr
|
||||||
|
text, etext uintptr
|
||||||
|
noptrdata, enoptrdata uintptr
|
||||||
|
data, edata uintptr
|
||||||
|
bss, ebss uintptr
|
||||||
|
noptrbss, enoptrbss uintptr
|
||||||
|
end, gcdata, gcbss uintptr
|
||||||
|
types, etypes uintptr
|
||||||
|
rodata uintptr
|
||||||
|
gofunc uintptr
|
||||||
|
textsectmap []_TextSection
|
||||||
|
typelinks []int32
|
||||||
|
itablinks []unsafe.Pointer
|
||||||
|
ptab []_PtabEntry
|
||||||
|
pluginpath string
|
||||||
|
pkghashes []struct{}
|
||||||
|
modulename string
|
||||||
|
modulehashes []struct{}
|
||||||
|
hasmain uint8
|
||||||
|
gcdatamask, gcbssmask _BitVector
|
||||||
|
typemap map[int32]unsafe.Pointer
|
||||||
|
bad bool
|
||||||
|
next *_ModuleData
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type _FindFuncBucket struct {
|
||||||
|
idx uint32
|
||||||
|
subbuckets [16]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func makePCtab(fp int) []byte {
|
||||||
|
return append([]byte{0}, encodeVariant((fp + 1) << 1)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argPtrs []bool, localPtrs []bool) {
|
||||||
|
mod := new(_ModuleData)
|
||||||
|
|
||||||
|
minpc := pc
|
||||||
|
maxpc := pc + size
|
||||||
|
|
||||||
|
findFuncTab := make([]_FindFuncBucket, textSize/4096 + 1)
|
||||||
|
|
||||||
|
modHeader := &_PCHeader {
|
||||||
|
magic : 0xfffffff0,
|
||||||
|
minLC : 1,
|
||||||
|
nfunc : 1,
|
||||||
|
ptrSize : 4 << (^uintptr(0) >> 63),
|
||||||
|
textStart: minpc,
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache arg and local stackmap
|
||||||
|
argptrs, localptrs := cacheStackmap(argPtrs, localPtrs, mod)
|
||||||
|
|
||||||
|
base := argptrs
|
||||||
|
if argptrs > localptrs {
|
||||||
|
base = localptrs
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function entry */
|
||||||
|
lnt := []_Func {{
|
||||||
|
entryOff : 0,
|
||||||
|
nameoff : 1,
|
||||||
|
args : int32(args),
|
||||||
|
pcsp : 1,
|
||||||
|
nfuncdata : 2,
|
||||||
|
argptrs: uint32(argptrs - base),
|
||||||
|
localptrs: uint32(localptrs - base),
|
||||||
|
}}
|
||||||
|
nlnt := len(lnt)*int(unsafe.Sizeof(_Func{}))
|
||||||
|
plnt := unsafe.Pointer(&lnt[0])
|
||||||
|
|
||||||
|
/* function table */
|
||||||
|
ftab := []_FuncTab {
|
||||||
|
{entry : 0, funcoff : 16},
|
||||||
|
{entry : uint32(size)},
|
||||||
|
}
|
||||||
|
nftab := len(ftab)*int(unsafe.Sizeof(_FuncTab{}))
|
||||||
|
pftab := unsafe.Pointer(&ftab[0])
|
||||||
|
|
||||||
|
pclntab := make([]byte, 0, nftab + nlnt)
|
||||||
|
pclntab = append(pclntab, rt.BytesFrom(pftab, nftab, nftab)...)
|
||||||
|
pclntab = append(pclntab, rt.BytesFrom(plnt, nlnt, nlnt)...)
|
||||||
|
|
||||||
|
/* module data */
|
||||||
|
*mod = _ModuleData {
|
||||||
|
pcHeader : modHeader,
|
||||||
|
funcnametab : append(append([]byte{0}, name...), 0),
|
||||||
|
pctab : append(makePCtab(fp), encodeVariant(int(size))...),
|
||||||
|
pclntable : pclntab,
|
||||||
|
ftab : ftab,
|
||||||
|
text : minpc,
|
||||||
|
etext : pc + textSize,
|
||||||
|
findfunctab : &findFuncTab[0],
|
||||||
|
minpc : minpc,
|
||||||
|
maxpc : maxpc,
|
||||||
|
modulename : name,
|
||||||
|
gcdata: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
gcbss: uintptr(unsafe.Pointer(&emptyByte)),
|
||||||
|
gofunc: base,
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify and register the new module */
|
||||||
|
moduledataverify1(mod)
|
||||||
|
registerModule(mod)
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build linux || darwin
|
//go:build darwin || linux
|
||||||
// +build linux darwin
|
// +build darwin linux
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
|
@ -36,15 +36,15 @@ const (
|
||||||
type Loader []byte
|
type Loader []byte
|
||||||
type Function unsafe.Pointer
|
type Function unsafe.Pointer
|
||||||
|
|
||||||
func (self Loader) LoadWithFaker(fn string, fp int, args int, faker interface{}) (f Function) {
|
func (self Loader) Load(fn string, fp int, args int, argPtrs []bool, localPtrs []bool) (f Function) {
|
||||||
p := os.Getpagesize()
|
p := os.Getpagesize()
|
||||||
n := (((len(self) - 1) / p) + 1) * p
|
n := (((len(self) - 1) / p) + 1) * p
|
||||||
|
|
||||||
/* register the function */
|
/* register the function */
|
||||||
m := mmap(n)
|
m := mmap(n)
|
||||||
v := fmt.Sprintf("runtime.__%s_%x", fn, m)
|
v := fmt.Sprintf("runtime.__%s_%x", fn, m)
|
||||||
argsptr, localsptr := stackMap(faker)
|
|
||||||
registerFunction(v, m, uintptr(n), fp, args, uintptr(len(self)), argsptr, localsptr)
|
registerFunction(v, m, uintptr(n), fp, args, uintptr(len(self)), argPtrs, localPtrs)
|
||||||
|
|
||||||
/* reference as a slice */
|
/* reference as a slice */
|
||||||
s := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader {
|
s := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader {
|
||||||
|
|
@ -59,10 +59,6 @@ func (self Loader) LoadWithFaker(fn string, fp int, args int, faker interface{})
|
||||||
return Function(&m)
|
return Function(&m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Loader) Load(fn string, fp int, args int) (f Function) {
|
|
||||||
return self.LoadWithFaker(fn, fp, args, func(){})
|
|
||||||
}
|
|
||||||
|
|
||||||
func mmap(nb int) uintptr {
|
func mmap(nb int) uintptr {
|
||||||
if m, _, e := syscall.RawSyscall6(syscall.SYS_MMAP, 0, uintptr(nb), _RW, _AP, 0, 0); e != 0 {
|
if m, _, e := syscall.RawSyscall6(syscall.SYS_MMAP, 0, uintptr(nb), _RW, _AP, 0, 0); e != 0 {
|
||||||
panic(e)
|
panic(e)
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
`fmt`
|
`fmt`
|
||||||
`reflect`
|
`reflect`
|
||||||
`runtime`
|
`runtime`
|
||||||
`runtime/debug`
|
|
||||||
`testing`
|
`testing`
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
|
||||||
|
|
@ -36,7 +35,7 @@ func TestLoader_Load(t *testing.T) {
|
||||||
0xc3, // RET
|
0xc3, // RET
|
||||||
}
|
}
|
||||||
v0 := 0
|
v0 := 0
|
||||||
fn := Loader(bc).Load("test", 0, 8)
|
fn := Loader(bc).Load("test", 0, 8, nil, nil)
|
||||||
(*(*func(*int))(unsafe.Pointer(&fn)))(&v0)
|
(*(*func(*int))(unsafe.Pointer(&fn)))(&v0)
|
||||||
assert.Equal(t, 1234, v0)
|
assert.Equal(t, 1234, v0)
|
||||||
println(runtime.FuncForPC(*(*uintptr)(fn)).Name())
|
println(runtime.FuncForPC(*(*uintptr)(fn)).Name())
|
||||||
|
|
@ -90,23 +89,3 @@ func funcWrap(f func(i *int)) int {
|
||||||
ret = x
|
ret = x
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadWithStackMap(t *testing.T) {
|
|
||||||
var f = func(i *int) {
|
|
||||||
*i = 1234
|
|
||||||
}
|
|
||||||
v1 := funcWrap(f)
|
|
||||||
|
|
||||||
bc := []byte {
|
|
||||||
0x48, 0xc7, 0x00, 0xd2, 0x04, 0x00, 0x00, // MOVQ $1234, (%rax)
|
|
||||||
0xc3, // RET
|
|
||||||
}
|
|
||||||
fn := Loader(bc).LoadWithFaker("test", 0, 8, f)
|
|
||||||
f2 := (*(*func(*int))(unsafe.Pointer(&fn)))
|
|
||||||
v2 := funcWrap(f2)
|
|
||||||
|
|
||||||
runtime.GC()
|
|
||||||
debug.FreeOSMemory()
|
|
||||||
println(v1, v2)
|
|
||||||
assert.Equal(t, v1, v2)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
`fmt`
|
`fmt`
|
||||||
`reflect`
|
`reflect`
|
||||||
`runtime`
|
`runtime`
|
||||||
`runtime/debug`
|
|
||||||
`testing`
|
`testing`
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
|
||||||
|
|
@ -37,7 +36,7 @@ func TestLoader_Load(t *testing.T) {
|
||||||
0xc3, // RET
|
0xc3, // RET
|
||||||
}
|
}
|
||||||
v0 := 0
|
v0 := 0
|
||||||
fn := Loader(bc).Load("test", 0, 8)
|
fn := Loader(bc).Load("test", 0, 8, nil, nil)
|
||||||
(*(*func(*int))(unsafe.Pointer(&fn)))(&v0)
|
(*(*func(*int))(unsafe.Pointer(&fn)))(&v0)
|
||||||
assert.Equal(t, 1234, v0)
|
assert.Equal(t, 1234, v0)
|
||||||
println(runtime.FuncForPC(*(*uintptr)(fn)).Name())
|
println(runtime.FuncForPC(*(*uintptr)(fn)).Name())
|
||||||
|
|
@ -91,24 +90,3 @@ func funcWrap(f func(i *int)) int {
|
||||||
ret = x
|
ret = x
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadWithStackMap(t *testing.T) {
|
|
||||||
var f = func(i *int) {
|
|
||||||
*i = 1234
|
|
||||||
}
|
|
||||||
v1 := funcWrap(f)
|
|
||||||
|
|
||||||
bc := []byte {
|
|
||||||
0x48, 0x8b, 0x44, 0x24, 0x08, // MOVQ 8(%rsp), %rax
|
|
||||||
0x48, 0xc7, 0x00, 0xd2, 0x04, 0x00, 0x00, // MOVQ $1234, (%rax)
|
|
||||||
0xc3, // RET
|
|
||||||
}
|
|
||||||
fn := Loader(bc).LoadWithFaker("test", 0, 8, f)
|
|
||||||
f2 := (*(*func(*int))(unsafe.Pointer(&fn)))
|
|
||||||
v2 := funcWrap(f2)
|
|
||||||
|
|
||||||
runtime.GC()
|
|
||||||
debug.FreeOSMemory()
|
|
||||||
println(v1, v2)
|
|
||||||
assert.Equal(t, v1, v2)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
*
|
*
|
||||||
|
|
@ -38,15 +41,15 @@ var (
|
||||||
type Loader []byte
|
type Loader []byte
|
||||||
type Function unsafe.Pointer
|
type Function unsafe.Pointer
|
||||||
|
|
||||||
func (self Loader) LoadWithFaker(fn string, fp int, args int, faker interface{}) (f Function) {
|
func (self Loader) Load(fn string, fp int, args int, argPtrs []bool, localPtrs []bool) (f Function) {
|
||||||
p := os.Getpagesize()
|
p := os.Getpagesize()
|
||||||
n := (((len(self) - 1) / p) + 1) * p
|
n := (((len(self) - 1) / p) + 1) * p
|
||||||
|
|
||||||
/* register the function */
|
/* register the function */
|
||||||
m := mmap(n)
|
m := mmap(n)
|
||||||
v := fmt.Sprintf("runtime.__%s_%x", fn, m)
|
v := fmt.Sprintf("runtime.__%s_%x", fn, m)
|
||||||
argsptr, localsptr := stackMap(faker)
|
|
||||||
registerFunction(v, m, uintptr(n), fp, args, uintptr(len(self)), argsptr, localsptr)
|
registerFunction(v, m, uintptr(n), fp, args, uintptr(len(self)), argPtrs, localPtrs)
|
||||||
|
|
||||||
/* reference as a slice */
|
/* reference as a slice */
|
||||||
s := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader {
|
s := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader {
|
||||||
|
|
@ -61,10 +64,6 @@ func (self Loader) LoadWithFaker(fn string, fp int, args int, faker interface{})
|
||||||
return Function(&m)
|
return Function(&m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Loader) Load(fn string, fp int, args int) (f Function) {
|
|
||||||
return self.LoadWithFaker(fn, fp, args, func(){})
|
|
||||||
}
|
|
||||||
|
|
||||||
func mmap(nb int) uintptr {
|
func mmap(nb int) uintptr {
|
||||||
addr, err := winapi_VirtualAlloc(0, nb, MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE)
|
addr, err := winapi_VirtualAlloc(0, nb, MEM_COMMIT|MEM_RESERVE, syscall.PAGE_READWRITE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ func IndexByte(ptr []byte, index int) unsafe.Pointer {
|
||||||
return unsafe.Pointer(uintptr((*GoSlice)(unsafe.Pointer(&ptr)).Ptr) + uintptr(index))
|
return unsafe.Pointer(uintptr((*GoSlice)(unsafe.Pointer(&ptr)).Ptr) + uintptr(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
func GuardSlice(buf *[]byte, n int) {
|
func GuardSlice(buf *[]byte, n int) {
|
||||||
c := cap(*buf)
|
c := cap(*buf)
|
||||||
l := len(*buf)
|
l := len(*buf)
|
||||||
|
|
@ -88,3 +89,25 @@ func GuardSlice(buf *[]byte, n int) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func Ptr2SlicePtr(s unsafe.Pointer, l int, c int) unsafe.Pointer {
|
||||||
|
slice := &GoSlice{
|
||||||
|
Ptr: s,
|
||||||
|
Len: l,
|
||||||
|
Cap: c,
|
||||||
|
}
|
||||||
|
return unsafe.Pointer(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func StrPtr(s string) unsafe.Pointer {
|
||||||
|
return (*GoString)(unsafe.Pointer(&s)).Ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:nosplit
|
||||||
|
func StrFrom(p unsafe.Pointer, n int64) (s string) {
|
||||||
|
(*GoString)(unsafe.Pointer(&s)).Ptr = p
|
||||||
|
(*GoString)(unsafe.Pointer(&s)).Len = int(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
180
internal/rt/stackmap.go
Normal file
180
internal/rt/stackmap.go
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
/**
|
||||||
|
* 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 rt
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
`strings`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
type Bitmap struct {
|
||||||
|
N int
|
||||||
|
B []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Bitmap) grow() {
|
||||||
|
if self.N >= len(self.B) * 8 {
|
||||||
|
self.B = append(self.B, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Bitmap) mark(i int, bv int) {
|
||||||
|
if bv != 0 {
|
||||||
|
self.B[i / 8] |= 1 << (i % 8)
|
||||||
|
} else {
|
||||||
|
self.B[i / 8] &^= 1 << (i % 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Bitmap) Set(i int, bv int) {
|
||||||
|
if i >= self.N {
|
||||||
|
panic("bitmap: invalid bit position")
|
||||||
|
} else {
|
||||||
|
self.mark(i, bv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Bitmap) Append(bv int) {
|
||||||
|
self.grow()
|
||||||
|
self.mark(self.N, bv)
|
||||||
|
self.N++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Bitmap) AppendMany(n int, bv int) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
self.Append(bv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// var (
|
||||||
|
// _stackMapLock = sync.Mutex{}
|
||||||
|
// _stackMapCache = make(map[*StackMap]struct{})
|
||||||
|
// )
|
||||||
|
|
||||||
|
type BitVec struct {
|
||||||
|
N uintptr
|
||||||
|
B unsafe.Pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self BitVec) Bit(i uintptr) byte {
|
||||||
|
return (*(*byte)(unsafe.Pointer(uintptr(self.B) + i / 8)) >> (i % 8)) & 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self BitVec) String() string {
|
||||||
|
var i uintptr
|
||||||
|
var v []string
|
||||||
|
|
||||||
|
/* add each bit */
|
||||||
|
for i = 0; i < self.N; i++ {
|
||||||
|
v = append(v, fmt.Sprintf("%d", self.Bit(i)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* join them together */
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"BitVec { %s }",
|
||||||
|
strings.Join(v, ", "),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type StackMap struct {
|
||||||
|
N int32
|
||||||
|
L int32
|
||||||
|
B [1]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (self *StackMap) add() {
|
||||||
|
// _stackMapLock.Lock()
|
||||||
|
// _stackMapCache[self] = struct{}{}
|
||||||
|
// _stackMapLock.Unlock()
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (self *StackMap) Pin() uintptr {
|
||||||
|
// self.add()
|
||||||
|
return uintptr(unsafe.Pointer(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StackMap) Get(i int32) BitVec {
|
||||||
|
return BitVec {
|
||||||
|
N: uintptr(self.L),
|
||||||
|
B: unsafe.Pointer(uintptr(unsafe.Pointer(&self.B)) + uintptr(i * ((self.L + 7) >> 3))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StackMap) String() string {
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.WriteString("StackMap {")
|
||||||
|
|
||||||
|
/* dump every stack map */
|
||||||
|
for i := int32(0); i < self.N; i++ {
|
||||||
|
sb.WriteRune('\n')
|
||||||
|
sb.WriteString(" " + self.Get(i).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close the stackmap */
|
||||||
|
sb.WriteString("\n}")
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StackMap) MarshalBinary() ([]byte, error) {
|
||||||
|
size := int(self.N) * int(self.L) + int(unsafe.Sizeof(self.L)) + int(unsafe.Sizeof(self.N))
|
||||||
|
return BytesFrom(unsafe.Pointer(self), size, size), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
byteType = UnpackEface(byte(0)).Type
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_StackMapSize = unsafe.Sizeof(StackMap{})
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:linkname mallocgc runtime.mallocgc
|
||||||
|
//goland:noinspection GoUnusedParameter
|
||||||
|
func mallocgc(nb uintptr, vt *GoType, zero bool) unsafe.Pointer
|
||||||
|
|
||||||
|
type StackMapBuilder struct {
|
||||||
|
b Bitmap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StackMapBuilder) Build() (p *StackMap) {
|
||||||
|
nb := len(self.b.B)
|
||||||
|
bm := mallocgc(_StackMapSize + uintptr(nb) - 1, byteType, false)
|
||||||
|
|
||||||
|
/* initialize as 1 bitmap of N bits */
|
||||||
|
p = (*StackMap)(bm)
|
||||||
|
p.N, p.L = 1, int32(self.b.N)
|
||||||
|
copy(BytesFrom(unsafe.Pointer(&p.B), nb, nb), self.b.B)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StackMapBuilder) AddField(ptr bool) {
|
||||||
|
if ptr {
|
||||||
|
self.b.Append(1)
|
||||||
|
} else {
|
||||||
|
self.b.Append(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StackMapBuilder) AddFields(n int, ptr bool) {
|
||||||
|
if ptr {
|
||||||
|
self.b.AppendMany(n, 1)
|
||||||
|
} else {
|
||||||
|
self.b.AppendMany(n, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
144
loader/funcdata.go
Normal file
144
loader/funcdata.go
Normal file
|
|
@ -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:]
|
||||||
|
}
|
||||||
541
loader/funcdata_go115.go
Normal file
541
loader/funcdata_go115.go
Normal file
|
|
@ -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) {}
|
||||||
541
loader/funcdata_go118.go
Normal file
541
loader/funcdata_go118.go
Normal file
|
|
@ -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) {}
|
||||||
545
loader/funcdata_go120.go
Normal file
545
loader/funcdata_go120.go
Normal file
|
|
@ -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) {}
|
||||||
37
loader/loader.go
Normal file
37
loader/loader.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
33
loader/loader_go115.go
Normal file
33
loader/loader_go115.go
Normal file
|
|
@ -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")
|
||||||
|
}
|
||||||
104
loader/loader_go118.go
Normal file
104
loader/loader_go118.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
134
loader/loader_test.go
Normal file
134
loader/loader_test.go
Normal file
|
|
@ -0,0 +1,134 @@
|
||||||
|
//go:build go1.18
|
||||||
|
// +build 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 (
|
||||||
|
`runtime`
|
||||||
|
`runtime/debug`
|
||||||
|
`strconv`
|
||||||
|
`testing`
|
||||||
|
`unsafe`
|
||||||
|
|
||||||
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
|
`github.com/stretchr/testify/require`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoad(t *testing.T) {
|
||||||
|
// defer func() {
|
||||||
|
// if r := recover(); r != nil {
|
||||||
|
// runtime.GC()
|
||||||
|
// if r != "hook1" {
|
||||||
|
// t.Fatal("not right panic:" + r.(string))
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// t.Fatal("not panic")
|
||||||
|
// }
|
||||||
|
// }()
|
||||||
|
|
||||||
|
var hstr string
|
||||||
|
|
||||||
|
type TestFunc func(i *int, hook func(i *int)) int
|
||||||
|
var hook = func(i *int) {
|
||||||
|
runtime.GC()
|
||||||
|
debug.FreeOSMemory()
|
||||||
|
hstr = ("hook" + strconv.Itoa(*i))
|
||||||
|
runtime.GC()
|
||||||
|
debug.FreeOSMemory()
|
||||||
|
}
|
||||||
|
// var f TestFunc = func(i *int, hook func(i *int)) int {
|
||||||
|
// var t = *i
|
||||||
|
// hook(i)
|
||||||
|
// return t + *i
|
||||||
|
// }
|
||||||
|
bc := []byte {
|
||||||
|
0x48, 0x83, 0xec, 0x18, // (0x00) subq $24, %rsp
|
||||||
|
0x48, 0x89, 0x6c, 0x24, 0x10, // (0x04) movq %rbp, 16(%rsp)
|
||||||
|
0x48, 0x8d, 0x6c, 0x24, 0x10, // (0x09) leaq 16(%rsp), %rbp
|
||||||
|
0x48, 0x89, 0x44, 0x24, 0x20, // (0x0e) movq %rax, 32(%rsp)
|
||||||
|
0x48, 0x8b, 0x08, // (0x13) movq (%rax), %rcx
|
||||||
|
0x48, 0x89, 0x4c, 0x24, 0x08, // (0x16) movq %rcx, 8(%rsp)
|
||||||
|
0x48, 0x8b, 0x33, // (0x1b) movq (%rbx), %rsi
|
||||||
|
0x48, 0x89, 0xda, // (0x1e) movq %rbx, %rdx
|
||||||
|
0xff, 0xd6, // (0x21) callq %rsi
|
||||||
|
0x48, 0x8b, 0x44, 0x24, 0x08, // (0x23) movq 8(%rsp), %rax
|
||||||
|
0x48, 0x8b, 0x4c, 0x24, 0x20, // (0x28) movq 32(%rsp), %rcx
|
||||||
|
0x48, 0x03, 0x01, // (0x2d) addq (%rcx), %rax
|
||||||
|
0x48, 0x8b, 0x6c, 0x24, 0x10, // (0x30) movq 16(%rsp), %rbp
|
||||||
|
0x48, 0x83, 0xc4, 0x18, // (0x35) addq $24, %rsp
|
||||||
|
0xc3, // (0x39) ret
|
||||||
|
}
|
||||||
|
size := uint32(len(bc))
|
||||||
|
fn := Func{
|
||||||
|
ID: 0,
|
||||||
|
Flag: 0,
|
||||||
|
ArgsSize: 16,
|
||||||
|
EntryOff: 0,
|
||||||
|
TextSize: size,
|
||||||
|
DeferReturn: 0,
|
||||||
|
FileIndex: 0,
|
||||||
|
Name: "dummy",
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.Pcsp = &Pcdata{
|
||||||
|
{PC: 0x04, Val: 0},
|
||||||
|
{PC: size, Val: 24},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.Pcline = &Pcdata{
|
||||||
|
{PC: 0x00, Val: 0},
|
||||||
|
{PC: 0x0e, Val: 1},
|
||||||
|
{PC: 0x1d, Val: 2},
|
||||||
|
{PC: size, Val: 3},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.Pcfile = &Pcdata{
|
||||||
|
{PC: size, Val: 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.PcUnsafePoint = &Pcdata{
|
||||||
|
{PC: size, Val: PCDATA_UnsafePointUnsafe},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.PcStackMapIndex = &Pcdata{
|
||||||
|
{PC: size, Val: 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
args := rt.StackMapBuilder{}
|
||||||
|
args.AddField(true)
|
||||||
|
args.AddField(true)
|
||||||
|
fn.ArgsPointerMaps = args.Build()
|
||||||
|
|
||||||
|
locals := rt.StackMapBuilder{}
|
||||||
|
locals.AddField(false)
|
||||||
|
locals.AddField(false)
|
||||||
|
fn.LocalsPointerMaps = locals.Build()
|
||||||
|
|
||||||
|
rets := Load(bc, []Func{fn}, "dummy_module", []string{"github.com/bytedance/sonic/dummy.go"})
|
||||||
|
println("func address ", *(*unsafe.Pointer)(rets[0]))
|
||||||
|
// for k, _ := range moduleCache.m {
|
||||||
|
// spew.Dump(k)
|
||||||
|
// }
|
||||||
|
|
||||||
|
f := *(*TestFunc)(unsafe.Pointer(&rets[0]))
|
||||||
|
i := 1
|
||||||
|
j := f(&i, hook)
|
||||||
|
require.Equal(t, 2, j)
|
||||||
|
require.Equal(t, "hook1", hstr)
|
||||||
|
}
|
||||||
45
loader/mmap_unix.go
Normal file
45
loader/mmap_unix.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
84
loader/mmap_windows.go
Normal file
84
loader/mmap_windows.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
100
loader/pcdata.go
Normal file
100
loader/pcdata.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
// +build !go1.15 go1.20
|
/**
|
||||||
|
* Copyright 2023 ByteDance Inc.
|
||||||
/*
|
|
||||||
* Copyright 2021 ByteDance Inc.
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -18,11 +16,20 @@
|
||||||
|
|
||||||
package loader
|
package loader
|
||||||
|
|
||||||
// triggers a compilation error
|
import (
|
||||||
const (
|
_ `unsafe`
|
||||||
_ = panic("Unsupported Go version. Supported versions are: 1.15, 1.16, 1.17, 1.18, 1.19")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerFunction(_ string, _ uintptr, _ int, _ int, _ uintptr) {
|
//go:linkname lastmoduledatap runtime.lastmoduledatap
|
||||||
panic("Unsupported Go version. Supported versions are: 1.15, 1.16, 1.17, 1.18, 1.19")
|
//goland:noinspection GoUnusedGlobalVariable
|
||||||
|
var lastmoduledatap *moduledata
|
||||||
|
|
||||||
|
func registerModule(mod *moduledata) {
|
||||||
|
lastmoduledatap.next = mod
|
||||||
|
lastmoduledatap = mod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname moduledataverify1 runtime.moduledataverify1
|
||||||
|
func moduledataverify1(_ *moduledata)
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in a new issue