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