github.com/goshafaq/sonic@v0.0.0-20231026082336-871835fb94c6/loader/wrapper.go (about) 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/goshafaq/sonic/internal/abi" 24 "github.com/goshafaq/sonic/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 }