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