Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ This is a list of the copied files:
* `internal/fakecgo/setenv.go` from package `runtime/cgo`
* `internal/fakecgo/freebsd.go` from package `runtime/cgo`
* `internal/fakecgo/netbsd.go` from package `runtime/cgo`
* `internal/fakecgo/linux.go` from package `runtime/cgo`

The `internal/fakecgo/go_GOOS.go` files were modified from `runtime/cgo/gcc_GOOS_GOARCH.go`.

Expand Down
27 changes: 27 additions & 0 deletions internal/fakecgo/fakecgo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors

//go:build !cgo && (darwin || freebsd || linux || netbsd)

package fakecgo

import "unsafe"

// setg_trampoline calls setg with the G provided
func setg_trampoline(setg uintptr, G uintptr)

// call5 takes fn the C function and 5 arguments and calls the function with those arguments
func call5(fn, a1, a2, a3, a4, a5 uintptr) uintptr

// argset matches runtime/cgocall.go:argset.
type argset struct {
args *uintptr
retval uintptr
}

//go:nosplit
//go:norace
func (a *argset) arg(i int) unsafe.Pointer {
// this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer
return *(*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(a.args), uintptr(i)*unsafe.Sizeof(uintptr(0))))
}
272 changes: 218 additions & 54 deletions internal/fakecgo/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,7 @@ import (
"text/template"
)

const templateSymbols = `// Code generated by 'go generate' with gen.go. DO NOT EDIT.

// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors

//go:build !cgo && (darwin || freebsd || linux || netbsd)

package fakecgo

import (
"syscall"
"unsafe"
)

// setg_trampoline calls setg with the G provided
func setg_trampoline(setg uintptr, G uintptr)

// call5 takes fn the C function and 5 arguments and calls the function with those arguments
func call5(fn, a1, a2, a3, a4, a5 uintptr) uintptr

const templateSymbols = `{{- define "symbols" -}}
{{ range . -}}
//go:nosplit
//go:norace
Expand Down Expand Up @@ -80,6 +61,46 @@ call5({{.Name}}ABI0,
var _{{.Name}} uint8
var {{.Name}}ABI0 = uintptr(unsafe.Pointer(&_{{.Name}}))
{{ end }}

{{ range . }}
{{ if .HasCalledFromGoAttr }}
//go:linkname _cgo_purego_{{.Name}}_trampoline _cgo_purego_{{.Name}}_trampoline
var _cgo_purego_{{.Name}}_trampoline byte
var x_cgo_purego_{{.Name}}_call = x_cgo_purego_{{.Name}}

//go:nosplit
//go:norace
func x_cgo_purego_{{.Name}}(c *argset) {
{{- if .Return }}
ret := {{.Name}}(
{{- else }}
{{.Name}}(
{{- end }}
{{- range $i, $arg := .Args }}
{{- if $arg.Name }}
{{- if $i -}}, {{- end -}}
{{- if hasPrefix .Type "*" -}}
({{$arg.Type}})(c.arg({{$i}}))
{{- else -}}
{{$arg.Type}}(uintptr(c.arg({{$i}})))
{{- end -}}
{{- end }}
{{- end -}})
{{- if .Return }}
{{- if .HasErrnoAttr }}
if ret == -1 {
c.retval = uintptr(errno())
} else {
c.retval = uintptr(ret)
}
{{- else }}
c.retval = uintptr(ret)
{{- end }}
{{- end }}
}
{{- end -}}
{{ end -}}
{{ end -}}
`

const templateTrampolinesStubs = `// Code generated by 'go generate' with gen.go. DO NOT EDIT.
Expand All @@ -99,6 +120,23 @@ TEXT _{{.Name}}(SB), NOSPLIT|NOFRAME, $0-0
{{ end -}}
`

const templateSymbolsAllGOOS = `// Code generated by 'go generate' with gen.go. DO NOT EDIT.

// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors

//go:build !cgo && (darwin || freebsd || linux || netbsd)

package fakecgo

import (
"syscall"
"unsafe"
)

{{ template "symbols" . }}
`

const templateSymbolsGoos = `// Code generated by 'go generate' with gen.go. DO NOT EDIT.

// SPDX-License-Identifier: Apache-2.0
Expand All @@ -108,63 +146,153 @@ const templateSymbolsGoos = `// Code generated by 'go generate' with gen.go. DO

package fakecgo

{{- range $location := . }}
{{- range .Symbols }}
//go:cgo_import_dynamic purego_{{ .Name }} {{ .Name }} "{{ $location.SharedObject }}"
{{- if .Symbols }}
import "unsafe"
{{- end }}

{{- range $so := .SharedObject }}
{{- range $sym := $so.Symbols }}
//go:cgo_import_dynamic purego_{{ $sym.Name }} {{ $sym.Name }} "{{ $so.Name }}"
{{- end }}
{{- end }}

{{ template "symbols" .Symbols }}
`

const templateTrampolinesGoarch = `// Code generated by 'go generate' with gen.go. DO NOT EDIT.

// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors

//go:build !cgo

#include "textflag.h"

{{ $goarch := .Goarch -}}
{{ range .Symbols -}}
{{ if .HasCalledFromGoAttr -}}
{{ if eq $goarch "amd64" -}}
TEXT _cgo_purego_{{.Name}}_trampoline(SB), NOSPLIT, $0
MOVQ DI, AX
MOVQ ·x_cgo_purego_{{.Name}}_call(SB), DX
MOVQ (DX), CX
CALL CX
RET
{{ else if eq $goarch "arm64" -}}
TEXT _cgo_purego_{{.Name}}_trampoline(SB), NOSPLIT, $0
MOVD ·x_cgo_purego_{{.Name}}_call(SB), R26
MOVD (R26), R2
CALL (R2)
RET
{{ else if eq $goarch "loong64" -}}
TEXT _cgo_purego_{{.Name}}_trampoline(SB), NOSPLIT, $0
MOVV ·x_cgo_purego_{{.Name}}_call(SB), R5
MOVV (R5), R6
CALL (R6)
RET
{{ end -}}
{{ end }}
TEXT _{{.Name}}(SB), NOSPLIT, $0-0
JMP purego_{{.Name}}(SB)

{{ end -}}
`

type Arg struct {
Name string
Type string
}

type SymbolAttrs int

const AttrNone SymbolAttrs = 0
const (
AttrCalledFromGo SymbolAttrs = 1 << iota
AttrErrno
AttrLinux
)

type Symbol struct {
Name string
Args [5]Arg
Return string
Attrs SymbolAttrs
Goos []string // empty means all GOOSes
}

func (s Symbol) HasCalledFromGoAttr() bool {
return (s.Attrs & AttrCalledFromGo) != 0
}

type LocatedSymbols struct {
SharedObject string
Symbols []Symbol
func (s Symbol) HasErrnoAttr() bool {
return (s.Attrs & AttrErrno) != 0
}

var (
libcSymbols = []Symbol{
{"malloc", [5]Arg{{"size", "uintptr"}}, "unsafe.Pointer"},
{"free", [5]Arg{{"ptr", "unsafe.Pointer"}}, ""},
{"setenv", [5]Arg{{"name", "*byte"}, {"value", "*byte"}, {"overwrite", "int32"}}, "int32"},
{"unsetenv", [5]Arg{{"name", "*byte"}}, "int32"},
{"sigfillset", [5]Arg{{"set", "*sigset_t"}}, "int32"},
{"nanosleep", [5]Arg{{"ts", "*syscall.Timespec"}, {"rem", "*syscall.Timespec"}}, "int32"},
{"abort", [5]Arg{}, ""},
{"sigaltstack", [5]Arg{{"ss", "*stack_t"}, {"old_ss", "*stack_t"}}, "int32"},
{"malloc", [5]Arg{{"size", "uintptr"}}, "unsafe.Pointer", AttrNone, nil},
{"free", [5]Arg{{"ptr", "unsafe.Pointer"}}, "", AttrNone, nil},
{"setenv", [5]Arg{{"name", "*byte"}, {"value", "*byte"}, {"overwrite", "int32"}}, "int32", AttrNone, nil},
{"unsetenv", [5]Arg{{"name", "*byte"}}, "int32", AttrNone, nil},
{"sigfillset", [5]Arg{{"set", "*sigset_t"}}, "int32", AttrNone, nil},
{"nanosleep", [5]Arg{{"ts", "*syscall.Timespec"}, {"rem", "*syscall.Timespec"}}, "int32", AttrNone, nil},
{"abort", [5]Arg{}, "", AttrNone, nil},
{"sigaltstack", [5]Arg{{"ss", "*stack_t"}, {"old_ss", "*stack_t"}}, "int32", AttrNone, nil},
{"__errno_location", [5]Arg{}, "uintptr", AttrNone, []string{"linux"}},
{"setegid", [5]Arg{{"egid", "uint32"}}, "int32", AttrCalledFromGo | AttrErrno, []string{"linux"}},
{"seteuid", [5]Arg{{"euid", "uint32"}}, "int32", AttrCalledFromGo | AttrErrno, []string{"linux"}},
{"setgid", [5]Arg{{"gid", "uint32"}}, "int32", AttrCalledFromGo | AttrErrno, []string{"linux"}},
{"setregid", [5]Arg{{"rgid", "uint32"}, {"egid", "uint32"}}, "int32", AttrCalledFromGo | AttrErrno, []string{"linux"}},
{"setresgid", [5]Arg{{"rgid", "uint32"}, {"egid", "uint32"}, {"sgid", "uint32"}}, "int32", AttrCalledFromGo | AttrErrno, []string{"linux"}},
{"setresuid", [5]Arg{{"ruid", "uint32"}, {"euid", "uint32"}, {"suid", "uint32"}}, "int32", AttrCalledFromGo | AttrErrno, []string{"linux"}},
{"setreuid", [5]Arg{{"ruid", "uint32"}, {"euid", "uint32"}}, "int32", AttrCalledFromGo | AttrErrno, []string{"linux"}},
{"setuid", [5]Arg{{"uid", "uint32"}}, "int32", AttrCalledFromGo | AttrErrno, []string{"linux"}},
{"setgroups", [5]Arg{{"ngid", "uint32"}, {"gidset", "*uint32"}}, "int32", AttrCalledFromGo | AttrErrno, []string{"linux"}},
}
pthreadSymbols = []Symbol{
{"pthread_attr_init", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32"},
{"pthread_create", [5]Arg{{"thread", "*pthread_t"}, {"attr", "*pthread_attr_t"}, {"start", "unsafe.Pointer"}, {"arg", "unsafe.Pointer"}}, "int32"},
{"pthread_detach", [5]Arg{{"thread", "pthread_t"}}, "int32"},
{"pthread_sigmask", [5]Arg{{"how", "sighow"}, {"ign", "*sigset_t"}, {"oset", "*sigset_t"}}, "int32"},
{"pthread_self", [5]Arg{}, "pthread_t"},
{"pthread_get_stacksize_np", [5]Arg{{"thread", "pthread_t"}}, "size_t"},
{"pthread_attr_getstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"stacksize", "*size_t"}}, "int32"},
{"pthread_attr_setstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"size", "size_t"}}, "int32"},
{"pthread_attr_destroy", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32"},
{"pthread_mutex_lock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32"},
{"pthread_mutex_unlock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32"},
{"pthread_cond_broadcast", [5]Arg{{"cond", "*pthread_cond_t"}}, "int32"},
{"pthread_setspecific", [5]Arg{{"key", "pthread_key_t"}, {"value", "unsafe.Pointer"}}, "int32"},
{"pthread_attr_init", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32", AttrNone, nil},
{"pthread_create", [5]Arg{{"thread", "*pthread_t"}, {"attr", "*pthread_attr_t"}, {"start", "unsafe.Pointer"}, {"arg", "unsafe.Pointer"}}, "int32", AttrNone, nil},
{"pthread_detach", [5]Arg{{"thread", "pthread_t"}}, "int32", AttrNone, nil},
{"pthread_sigmask", [5]Arg{{"how", "sighow"}, {"ign", "*sigset_t"}, {"oset", "*sigset_t"}}, "int32", AttrNone, nil},
{"pthread_self", [5]Arg{}, "pthread_t", AttrNone, nil},
{"pthread_get_stacksize_np", [5]Arg{{"thread", "pthread_t"}}, "size_t", AttrNone, nil},
{"pthread_attr_getstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"stacksize", "*size_t"}}, "int32", AttrNone, nil},
{"pthread_attr_setstacksize", [5]Arg{{"attr", "*pthread_attr_t"}, {"size", "size_t"}}, "int32", AttrNone, nil},
{"pthread_attr_destroy", [5]Arg{{"attr", "*pthread_attr_t"}}, "int32", AttrNone, nil},
{"pthread_mutex_lock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32", AttrNone, nil},
{"pthread_mutex_unlock", [5]Arg{{"mutex", "*pthread_mutex_t"}}, "int32", AttrNone, nil},
{"pthread_cond_broadcast", [5]Arg{{"cond", "*pthread_cond_t"}}, "int32", AttrNone, nil},
{"pthread_setspecific", [5]Arg{{"key", "pthread_key_t"}, {"value", "unsafe.Pointer"}}, "int32", AttrNone, nil},
}
)

var funcs = map[string]any{
"hasPrefix": strings.HasPrefix,
}

func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}

// filterSymbols filters the symbols by GOOS.
// If goos is empty, it returns symbols that are not specific to any GOOS.
func filterSymbols(symbols []Symbol, goos string) []Symbol {
filtered := make([]Symbol, 0, len(symbols))
for _, s := range symbols {
if (s.Goos == nil && goos == "") || contains(s.Goos, goos) {
filtered = append(filtered, s)
}
}
return filtered
}

func run() error {
t, err := template.New("symbol.go").Funcs(funcs).Parse(templateSymbols)
t, err := template.New("symbol.go").Funcs(funcs).Parse(templateSymbols + templateSymbolsAllGOOS)
if err != nil {
return err
}
Expand All @@ -175,7 +303,7 @@ func run() error {
}
allSymbols := append(append([]Symbol{}, libcSymbols...), pthreadSymbols...)
buf := new(bytes.Buffer)
if err := t.Execute(buf, allSymbols); err != nil {
if err := t.Execute(buf, filterSymbols(allSymbols, "")); err != nil {
return err
}
source, err := format.Source(buf.Bytes())
Expand All @@ -194,14 +322,15 @@ func run() error {
if err != nil {
return err
}
if err := t.Execute(f, allSymbols); err != nil {
if err := t.Execute(f, filterSymbols(allSymbols, "")); err != nil {
return err
}
t, err = template.New("symbols_goos.go").Parse(templateSymbolsGoos)
t, err = template.New("symbols_goos.go").Funcs(funcs).Parse(templateSymbols + templateSymbolsGoos)
if err != nil {
return err
}
for _, goos := range []string{"darwin", "freebsd", "linux", "netbsd"} {
goosSymbols := filterSymbols(allSymbols, goos)
f, err = os.Create(fmt.Sprintf("symbols_%s.go", goos))
defer f.Close()
if err != nil {
Expand All @@ -225,9 +354,20 @@ func run() error {
default:
return fmt.Errorf("unsupported OS: %s", goos)
}
located := []LocatedSymbols{
{SharedObject: libcSO, Symbols: libcSymbols},
{SharedObject: pthreadSO, Symbols: pthreadSymbols},
type SharedObject struct {
Name string
Symbols []Symbol
}
type args struct {
SharedObject []SharedObject
Symbols []Symbol
}
located := args{
SharedObject: []SharedObject{
{libcSO, append(filterSymbols(libcSymbols, ""), filterSymbols(libcSymbols, goos)...)},
{pthreadSO, append(filterSymbols(pthreadSymbols, ""), filterSymbols(pthreadSymbols, goos)...)},
},
Symbols: goosSymbols,
}
if err = t.Execute(b, located); err != nil {
return err
Expand All @@ -240,6 +380,30 @@ func run() error {
if _, err = f.Write(src); err != nil {
return err
}

if len(goosSymbols) == 0 {
continue
}
for _, goarch := range []string{"amd64", "arm64", "loong64"} {
t, err := template.New("ztrampolines_goos_goarch.s").Funcs(funcs).Parse(templateTrampolinesGoarch)
if err != nil {
return err
}
f, err = os.Create(fmt.Sprintf("ztrampolines_%s_%s.s", goos, goarch))
if err != nil {
return err
}
if err := t.Execute(f, struct {
Symbols []Symbol
Goarch string
}{goosSymbols, goarch}); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
}
}
return nil
}
Expand Down
Loading
Loading