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  }