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