github.com/jwijenbergh/purego@v0.0.0-20240126093400-70ff3a61df13/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 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 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 type Arg struct { 104 Name string 105 Type string 106 } 107 108 type Symbol struct { 109 Name string 110 Args [5]Arg 111 Return string 112 } 113 114 var ( 115 libcSymbols = []Symbol{ 116 {"malloc", [5]Arg{{"size", "uintptr"}}, "unsafe.Pointer"}, 117 {"free", [5]Arg{{"ptr", "unsafe.Pointer"}}, ""}, 118 {"setenv", [5]Arg{{"name", "*byte"}, {"value", "*byte"}, {"overwrite", "int32"}}, "int32"}, 119 {"unsetenv", [5]Arg{{"name", "*byte"}}, "int32"}, 120 {"sigfillset", [5]Arg{{"set", "*sigset_t"}}, "int32"}, 121 {"nanosleep", [5]Arg{{"ts", "*syscall.Timespec"}, {"rem", "*syscall.Timespec"}}, "int32"}, 122 {"abort", [5]Arg{}, ""}, 123 } 124 pthreadSymbols = []Symbol{ 125 {"pthread_attr_init", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32"}, 126 {"pthread_create", [5]Arg{{"thread", "*pthread_t"}, {"attr", "*pthread_attr_t"}, {"start", "unsafe.Pointer"}, {"arg", "unsafe.Pointer"}}, "int32"}, 127 {"pthread_detach", [5]Arg{{"thread", "pthread_t"}}, "int32"}, 128 {"pthread_sigmask", [5]Arg{{"how", "sighow"}, {"ign", "*sigset_t"}, {"oset", "*sigset_t"}}, "int32"}, 129 {"pthread_self", [5]Arg{}, "pthread_t"}, 130 {"pthread_get_stacksize_np", [5]Arg{{"thread", "pthread_t"}}, "size_t"}, 131 {"pthread_attr_getstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"stacksize", "*size_t"}}, "int32"}, 132 {"pthread_attr_setstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"size", "size_t"}}, "int32"}, 133 {"pthread_attr_destroy", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32"}, 134 {"pthread_mutex_lock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32"}, 135 {"pthread_mutex_unlock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32"}, 136 {"pthread_cond_broadcast", [5]Arg{{"cond", "*pthread_cond_t"}}, "int32"}, 137 {"pthread_setspecific", [5]Arg{{"key", "pthread_key_t"}, {"value", "unsafe.Pointer"}}, "int32"}, 138 } 139 ) 140 141 var funcs = map[string]any{ 142 "hasPrefix": strings.HasPrefix, 143 } 144 145 func run() error { 146 t, err := template.New("symbol.go").Funcs(funcs).Parse(templateSymbols) 147 if err != nil { 148 return err 149 } 150 f, err := os.Create("symbols.go") 151 defer f.Close() 152 if err != nil { 153 return err 154 } 155 allSymbols := append(append([]Symbol{}, libcSymbols...), pthreadSymbols...) 156 buf := new(bytes.Buffer) 157 if err := t.Execute(buf, allSymbols); err != nil { 158 return err 159 } 160 source, err := format.Source(buf.Bytes()) 161 if err != nil { 162 return err 163 } 164 if _, err = f.Write(source); err != nil { 165 return err 166 } 167 t, err = template.New("trampolines_stubs.s").Funcs(funcs).Parse(templateTrampolinesStubs) 168 if err != nil { 169 return err 170 } 171 f, err = os.Create("trampolines_stubs.s") 172 defer f.Close() 173 if err != nil { 174 return err 175 } 176 if err := t.Execute(f, allSymbols); err != nil { 177 return err 178 } 179 for _, goos := range []string{"darwin", "linux", "freebsd"} { 180 f, err = os.Create(fmt.Sprintf("symbols_%s.go", goos)) 181 defer f.Close() 182 if err != nil { 183 return err 184 } 185 f.WriteString(`// Code generated by 'go generate' with gen.go. DO NOT EDIT. 186 187 // SPDX-License-Identifier: Apache-2.0 188 // SPDX-FileCopyrightText: 2022 The Ebitengine Authors 189 190 package fakecgo 191 192 `) 193 const importPragma = `//go:cgo_import_dynamic purego_%s %s "%s"` + "\n" 194 libcSO := "/usr/lib/libSystem.B.dylib" 195 pthreadSO := "/usr/lib/libSystem.B.dylib" 196 if goos == "linux" { 197 libcSO = "libc.so.6" 198 pthreadSO = "libpthread.so.0" 199 } else if goos == "freebsd" { 200 libcSO = "libc.so.7" 201 pthreadSO = "libpthread.so" 202 } 203 204 for _, sym := range libcSymbols { 205 f.WriteString(fmt.Sprintf(importPragma, sym.Name, sym.Name, libcSO)) 206 } 207 for _, sym := range pthreadSymbols { 208 f.WriteString(fmt.Sprintf(importPragma, sym.Name, sym.Name, pthreadSO)) 209 } 210 } 211 return nil 212 } 213 214 func main() { 215 if err := run(); err != nil { 216 log.Fatal(err) 217 } 218 }