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
40//go:norace
41func {{.Name}}(
42{{- range .Args -}}
43 {{- if .Name -}}
44 {{.Name}} {{.Type}},
45 {{- end -}}
46{{- end -}}) {{.Return}} {
47 {{- if .Return -}}
48 {{- if eq .Return "unsafe.Pointer" -}}
49 ret :=
50 {{- else -}}
51 return {{.Return}}(
52 {{- end -}}
53 {{- end -}}
54call5({{.Name}}ABI0,
55{{- range .Args}}
56 {{- if .Name -}}
57 {{- if hasPrefix .Type "*" -}}
58 uintptr(unsafe.Pointer({{.Name}})),
59 {{- else -}}
60 uintptr({{.Name}}),
61 {{- end -}}
62 {{- else -}}
63 0,
64 {{- end -}}
65{{- end -}}
66 ) {{/* end of call5 */}}
67{{- if .Return -}}
68 {{- if eq .Return "unsafe.Pointer"}}
69 // this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer
70 return *(*unsafe.Pointer)(unsafe.Pointer(&ret))
71 {{- else -}}
72 ) {{/* end of cast */}}
73 {{- end -}}
74{{- end}}
75}
76
77{{end}}
78{{- range . }}
79//go:linkname _{{.Name}} _{{.Name}}
80var _{{.Name}} uint8
81var {{.Name}}ABI0 = uintptr(unsafe.Pointer(&_{{.Name}}))
82{{ end }}
83`
84
85const templateTrampolinesStubs = `// Code generated by 'go generate' with gen.go. DO NOT EDIT.
86
87// SPDX-License-Identifier: Apache-2.0
88// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
89
90//go:build !cgo && (darwin || freebsd || linux || netbsd)
91
92#include "textflag.h"
93
94// these stubs are here because it is not possible to go:linkname directly the C functions on darwin arm64
95{{ range . }}
96TEXT _{{.Name}}(SB), NOSPLIT|NOFRAME, $0-0
97 JMP purego_{{.Name}}(SB)
98 RET
99{{ end -}}
100`
101
102const templateSymbolsGoos = `// Code generated by 'go generate' with gen.go. DO NOT EDIT.
103
104// SPDX-License-Identifier: Apache-2.0
105// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
106
107//go:build !cgo
108
109package fakecgo
110
111{{- range $location := . }}
112{{- range .Symbols }}
113//go:cgo_import_dynamic purego_{{ .Name }} {{ .Name }} "{{ $location.SharedObject }}"
114{{- end }}
115{{- end }}
116`
117
118type Arg struct {
119 Name string
120 Type string
121}
122
123type Symbol struct {
124 Name string
125 Args [5]Arg
126 Return string
127}
128
129type LocatedSymbols struct {
130 SharedObject string
131 Symbols []Symbol
132}
133
134var (
135 libcSymbols = []Symbol{
136 {"malloc", [5]Arg{{"size", "uintptr"}}, "unsafe.Pointer"},
137 {"free", [5]Arg{{"ptr", "unsafe.Pointer"}}, ""},
138 {"setenv", [5]Arg{{"name", "*byte"}, {"value", "*byte"}, {"overwrite", "int32"}}, "int32"},
139 {"unsetenv", [5]Arg{{"name", "*byte"}}, "int32"},
140 {"sigfillset", [5]Arg{{"set", "*sigset_t"}}, "int32"},
141 {"nanosleep", [5]Arg{{"ts", "*syscall.Timespec"}, {"rem", "*syscall.Timespec"}}, "int32"},
142 {"abort", [5]Arg{}, ""},
143 {"sigaltstack", [5]Arg{{"ss", "*stack_t"}, {"old_ss", "*stack_t"}}, "int32"},
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
162var funcs = map[string]any{
163 "hasPrefix": strings.HasPrefix,
164}
165
166func 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", "freebsd", "linux", "netbsd"} {
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 case "netbsd":
223 libcSO = "libc.so"
224 pthreadSO = "libpthread.so"
225 default:
226 return fmt.Errorf("unsupported OS: %s", goos)
227 }
228 located := []LocatedSymbols{
229 {SharedObject: libcSO, Symbols: libcSymbols},
230 {SharedObject: pthreadSO, Symbols: pthreadSymbols},
231 }
232 if err = t.Execute(b, located); err != nil {
233 return err
234 }
235 var src []byte
236 src, err = format.Source(b.Bytes())
237 if err != nil {
238 return err
239 }
240 if _, err = f.Write(src); err != nil {
241 return err
242 }
243 }
244 return nil
245}
246
247func main() {
248 if err := run(); err != nil {
249 log.Fatal(err)
250 }
251}