github.com/ebitengine/purego@v0.8.0-alpha.2.0.20240512170805-6cd12240d332/internal/fakecgo/gen.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2022 The Ebitengine Authors 3 4 //go:build ignore 5 6 package main 7 8 import ( 9 "bytes" 10 "fmt" 11 "go/format" 12 "log" 13 "os" 14 "strings" 15 "text/template" 16 ) 17 18 const templateSymbols = `// Code generated by 'go generate' with gen.go. DO NOT EDIT. 19 20 // SPDX-License-Identifier: Apache-2.0 21 // SPDX-FileCopyrightText: 2022 The Ebitengine Authors 22 23 //go:build !cgo && (darwin || freebsd || linux) 24 25 package fakecgo 26 27 import ( 28 "syscall" 29 "unsafe" 30 ) 31 32 // setg_trampoline calls setg with the G provided 33 func setg_trampoline(setg uintptr, G uintptr) 34 35 //go:linkname memmove runtime.memmove 36 func memmove(to, from unsafe.Pointer, n uintptr) 37 38 // call5 takes fn the C function and 5 arguments and calls the function with those arguments 39 func call5(fn, a1, a2, a3, a4, a5 uintptr) uintptr 40 41 {{ range . -}} 42 func {{.Name}}( 43 {{- range .Args -}} 44 {{- if .Name -}} 45 {{.Name}} {{.Type}}, 46 {{- end -}} 47 {{- end -}}) {{.Return}} { 48 {{- if .Return -}} 49 {{- if eq .Return "unsafe.Pointer" -}} 50 ret := 51 {{- else -}} 52 return {{.Return}}( 53 {{- end -}} 54 {{- end -}} 55 call5({{.Name}}ABI0, 56 {{- range .Args}} 57 {{- if .Name -}} 58 {{- if hasPrefix .Type "*" -}} 59 uintptr(unsafe.Pointer({{.Name}})), 60 {{- else -}} 61 uintptr({{.Name}}), 62 {{- end -}} 63 {{- else -}} 64 0, 65 {{- end -}} 66 {{- end -}} 67 ) {{/* end of call5 */}} 68 {{- if .Return -}} 69 {{- if eq .Return "unsafe.Pointer"}} 70 // this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer 71 return *(*unsafe.Pointer)(unsafe.Pointer(&ret)) 72 {{- else -}} 73 ) {{/* end of cast */}} 74 {{- end -}} 75 {{- end}} 76 } 77 78 {{end}} 79 {{- range . }} 80 //go:linkname _{{.Name}} _{{.Name}} 81 var _{{.Name}} uintptr 82 var {{.Name}}ABI0 = uintptr(unsafe.Pointer(&_{{.Name}})) 83 {{ end }} 84 ` 85 86 const templateTrampolinesStubs = `// Code generated by 'go generate' with gen.go. DO NOT EDIT. 87 88 // SPDX-License-Identifier: Apache-2.0 89 // SPDX-FileCopyrightText: 2022 The Ebitengine Authors 90 91 //go:build !cgo && (darwin || freebsd || linux) 92 93 #include "textflag.h" 94 95 // these stubs are here because it is not possible to go:linkname directly the C functions on darwin arm64 96 {{ range . }} 97 TEXT _{{.Name}}(SB), NOSPLIT|NOFRAME, $0-0 98 JMP purego_{{.Name}}(SB) 99 RET 100 {{ end -}} 101 ` 102 103 const templateSymbolsGoos = `// Code generated by 'go generate' with gen.go. DO NOT EDIT. 104 105 // SPDX-License-Identifier: Apache-2.0 106 // SPDX-FileCopyrightText: 2022 The Ebitengine Authors 107 108 //go:build !cgo 109 110 package fakecgo 111 112 {{- range $location := . }} 113 {{- range .Symbols }} 114 //go:cgo_import_dynamic purego_{{ .Name }} {{ .Name }} "{{ $location.SharedObject }}" 115 {{- end }} 116 {{- end }} 117 ` 118 119 type Arg struct { 120 Name string 121 Type string 122 } 123 124 type Symbol struct { 125 Name string 126 Args [5]Arg 127 Return string 128 } 129 130 type LocatedSymbols struct { 131 SharedObject string 132 Symbols []Symbol 133 } 134 135 var ( 136 libcSymbols = []Symbol{ 137 {"malloc", [5]Arg{{"size", "uintptr"}}, "unsafe.Pointer"}, 138 {"free", [5]Arg{{"ptr", "unsafe.Pointer"}}, ""}, 139 {"setenv", [5]Arg{{"name", "*byte"}, {"value", "*byte"}, {"overwrite", "int32"}}, "int32"}, 140 {"unsetenv", [5]Arg{{"name", "*byte"}}, "int32"}, 141 {"sigfillset", [5]Arg{{"set", "*sigset_t"}}, "int32"}, 142 {"nanosleep", [5]Arg{{"ts", "*syscall.Timespec"}, {"rem", "*syscall.Timespec"}}, "int32"}, 143 {"abort", [5]Arg{}, ""}, 144 } 145 pthreadSymbols = []Symbol{ 146 {"pthread_attr_init", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32"}, 147 {"pthread_create", [5]Arg{{"thread", "*pthread_t"}, {"attr", "*pthread_attr_t"}, {"start", "unsafe.Pointer"}, {"arg", "unsafe.Pointer"}}, "int32"}, 148 {"pthread_detach", [5]Arg{{"thread", "pthread_t"}}, "int32"}, 149 {"pthread_sigmask", [5]Arg{{"how", "sighow"}, {"ign", "*sigset_t"}, {"oset", "*sigset_t"}}, "int32"}, 150 {"pthread_self", [5]Arg{}, "pthread_t"}, 151 {"pthread_get_stacksize_np", [5]Arg{{"thread", "pthread_t"}}, "size_t"}, 152 {"pthread_attr_getstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"stacksize", "*size_t"}}, "int32"}, 153 {"pthread_attr_setstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"size", "size_t"}}, "int32"}, 154 {"pthread_attr_destroy", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32"}, 155 {"pthread_mutex_lock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32"}, 156 {"pthread_mutex_unlock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32"}, 157 {"pthread_cond_broadcast", [5]Arg{{"cond", "*pthread_cond_t"}}, "int32"}, 158 {"pthread_setspecific", [5]Arg{{"key", "pthread_key_t"}, {"value", "unsafe.Pointer"}}, "int32"}, 159 } 160 ) 161 162 var funcs = map[string]any{ 163 "hasPrefix": strings.HasPrefix, 164 } 165 166 func run() error { 167 t, err := template.New("symbol.go").Funcs(funcs).Parse(templateSymbols) 168 if err != nil { 169 return err 170 } 171 f, err := os.Create("symbols.go") 172 defer f.Close() 173 if err != nil { 174 return err 175 } 176 allSymbols := append(append([]Symbol{}, libcSymbols...), pthreadSymbols...) 177 buf := new(bytes.Buffer) 178 if err := t.Execute(buf, allSymbols); err != nil { 179 return err 180 } 181 source, err := format.Source(buf.Bytes()) 182 if err != nil { 183 return err 184 } 185 if _, err = f.Write(source); err != nil { 186 return err 187 } 188 t, err = template.New("trampolines_stubs.s").Funcs(funcs).Parse(templateTrampolinesStubs) 189 if err != nil { 190 return err 191 } 192 f, err = os.Create("trampolines_stubs.s") 193 defer f.Close() 194 if err != nil { 195 return err 196 } 197 if err := t.Execute(f, allSymbols); err != nil { 198 return err 199 } 200 t, err = template.New("symbols_goos.go").Parse(templateSymbolsGoos) 201 if err != nil { 202 return err 203 } 204 for _, goos := range []string{"darwin", "linux", "freebsd"} { 205 f, err = os.Create(fmt.Sprintf("symbols_%s.go", goos)) 206 defer f.Close() 207 if err != nil { 208 return err 209 } 210 b := &bytes.Buffer{} 211 var libcSO, pthreadSO string 212 switch goos { 213 case "darwin": 214 libcSO = "/usr/lib/libSystem.B.dylib" 215 pthreadSO = "/usr/lib/libSystem.B.dylib" 216 case "freebsd": 217 libcSO = "libc.so.7" 218 pthreadSO = "libpthread.so" 219 case "linux": 220 libcSO = "libc.so.6" 221 pthreadSO = "libpthread.so.0" 222 default: 223 return fmt.Errorf("unsupported OS: %s", goos) 224 } 225 located := []LocatedSymbols{ 226 {SharedObject: libcSO, Symbols: libcSymbols}, 227 {SharedObject: pthreadSO, Symbols: pthreadSymbols}, 228 } 229 if err = t.Execute(b, located); err != nil { 230 return err 231 } 232 var src []byte 233 src, err = format.Source(b.Bytes()) 234 if err != nil { 235 return err 236 } 237 if _, err = f.Write(src); err != nil { 238 return err 239 } 240 } 241 return nil 242 } 243 244 func main() { 245 if err := run(); err != nil { 246 log.Fatal(err) 247 } 248 }