wrapper.go raw
1 /**
2 * Copyright 2023 ByteDance Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package loader
18
19 import (
20 `reflect`
21 `unsafe`
22
23 `github.com/bytedance/sonic/loader/internal/abi`
24 `github.com/bytedance/sonic/loader/internal/rt`
25 )
26
27 var _C_Redzone = []bool{false, false, false, false}
28
29 // CFunc is a function information for C func
30 type CFunc struct {
31 // C function name
32 Name string
33
34 // entry pc relative to entire text segment
35 EntryOff uint32
36
37 // function text size in bytes
38 TextSize uint32
39
40 // maximum stack depth of the function
41 MaxStack uintptr
42
43 // PC->SP delta lists of the function
44 Pcsp [][2]uint32
45 }
46
47 // GoC is the wrapper for Go calls to C
48 type GoC struct {
49 // CName is the name of corresponding C function
50 CName string
51
52 // CEntry points out where to store the entry address of corresponding C function.
53 // It won't be set if nil
54 CEntry *uintptr
55
56 // GoFunc is the POINTER of corresponding go stub function.
57 // It is used to generate Go-C ABI conversion wrapper and receive the wrapper's address
58 // eg. &func(a int, b int) int
59 // FOR
60 // int add(int a, int b)
61 // It won't be set if nil
62 GoFunc interface{}
63 }
64
65 // WrapGoC wraps C functions and loader it into Go stubs
66 func WrapGoC(text []byte, natives []CFunc, stubs []GoC, modulename string, filename string) {
67 funcs := make([]Func, len(natives))
68
69 // register C funcs
70 for i, f := range natives {
71 fn := Func{
72 Flag: FuncFlag_ASM,
73 EntryOff: f.EntryOff,
74 TextSize: f.TextSize,
75 Name: f.Name,
76 }
77 if len(f.Pcsp) != 0 {
78 fn.Pcsp = (*Pcdata)(unsafe.Pointer(&natives[i].Pcsp))
79 }
80 // NOTICE: always forbid async preempt
81 fn.PcUnsafePoint = &Pcdata{
82 {PC: f.TextSize, Val: PCDATA_UnsafePointUnsafe},
83 }
84 // NOTICE: always refer to first file
85 fn.Pcfile = &Pcdata{
86 {PC: f.TextSize, Val: 0},
87 }
88 // NOTICE: always refer to first line
89 fn.Pcline = &Pcdata{
90 {PC: f.TextSize, Val: 1},
91 }
92 // NOTICE: copystack need locals stackmap
93 fn.PcStackMapIndex = &Pcdata{
94 {PC: f.TextSize, Val: 0},
95 }
96 sm := rt.StackMapBuilder{}
97 sm.AddField(false)
98 fn.ArgsPointerMaps = sm.Build()
99 fn.LocalsPointerMaps = sm.Build()
100 funcs[i] = fn
101 }
102 rets := Load(text, funcs, modulename, []string{filename})
103
104 // got absolute entry address
105 native_entry := **(**uintptr)(unsafe.Pointer(&rets[0]))
106 // println("native_entry: ", native_entry)
107
108 wraps := make([]Func, 0, len(stubs))
109 wrapIds := make([]int, 0, len(stubs))
110 code := make([]byte, 0, len(wraps))
111 entryOff := uint32(0)
112
113 // register go wrappers
114 for i := range stubs {
115 for j := range natives {
116 if stubs[i].CName != natives[j].Name {
117 continue
118 }
119
120 // calculate corresponding C entry
121 pc := uintptr(native_entry + uintptr(natives[j].EntryOff))
122 if stubs[i].CEntry != nil {
123 *stubs[i].CEntry = pc
124 }
125
126 // no need to generate wrapper, continue next
127 if stubs[i].GoFunc == nil {
128 continue
129 }
130
131 // assemble wrapper codes
132 layout := abi.NewFunctionLayout(reflect.TypeOf(stubs[i].GoFunc).Elem())
133 frame := abi.NewFrame(&layout, _C_Redzone, true)
134 tcode := abi.CallC(pc, frame, natives[j].MaxStack)
135 code = append(code, tcode...)
136 size := uint32(len(tcode))
137
138 fn := Func{
139 Flag: FuncFlag_ASM,
140 ArgsSize: int32(layout.ArgSize()),
141 EntryOff: entryOff,
142 TextSize: size,
143 Name: stubs[i].CName + "_go",
144 }
145
146 // add check-stack and grow-stack texts' pcsp
147 fn.Pcsp = &Pcdata{
148 {PC: uint32(frame.StackCheckTextSize()), Val: 0},
149 {PC: size - uint32(frame.GrowStackTextSize()), Val: int32(frame.Size())},
150 {PC: size, Val: 0},
151 }
152 // NOTICE: always refer to first file
153 fn.Pcfile = &Pcdata{
154 {PC: size, Val: 0},
155 }
156 // NOTICE: always refer to first line
157 fn.Pcline = &Pcdata{
158 {PC: size, Val: 1},
159 }
160 // NOTICE: always forbid async preempt
161 fn.PcUnsafePoint = &Pcdata{
162 {PC: size, Val: PCDATA_UnsafePointUnsafe},
163 }
164
165 // register pointer stackmaps
166 fn.PcStackMapIndex = &Pcdata{
167 {PC: size, Val: 0},
168 }
169 fn.ArgsPointerMaps = frame.ArgPtrs()
170 fn.LocalsPointerMaps = frame.LocalPtrs()
171
172 entryOff += size
173 wraps = append(wraps, fn)
174 wrapIds = append(wrapIds, i)
175 }
176 }
177 gofuncs := Load(code, wraps, modulename+"/go", []string{filename+".go"})
178
179 // set go func value
180 for i := range gofuncs {
181 idx := wrapIds[i]
182 w := rt.UnpackEface(stubs[idx].GoFunc)
183 *(*Function)(w.Value) = gofuncs[i]
184 }
185 }
186