diff options
Diffstat (limited to 'misc')
413 files changed, 25882 insertions, 0 deletions
diff --git a/misc/android/README b/misc/android/README new file mode 100644 index 0000000..13b59d9 --- /dev/null +++ b/misc/android/README @@ -0,0 +1,25 @@ +Android +======= + +For details on developing Go for Android, see the documentation in the +mobile subrepository: + + https://github.com/golang/mobile + +To run the standard library tests, enable Cgo and use an appropriate +C compiler from the Android NDK. For example, + + CGO_ENABLED=1 \ + GOOS=android \ + GOARCH=arm64 \ + CC_FOR_TARGET=$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang \ + ./all.bash + +To run tests on the Android device, add the bin directory to PATH so the +go tool can find the go_android_$GOARCH_exec wrapper generated by +make.bash. For example, to run the go1 benchmarks + + export PATH=$GOROOT/bin:$PATH + cd $GOROOT/test/bench/go1/ + GOOS=android GOARCH=arm64 go test -bench=. -count=N -timeout=T + diff --git a/misc/android/go_android_exec.go b/misc/android/go_android_exec.go new file mode 100644 index 0000000..308daca --- /dev/null +++ b/misc/android/go_android_exec.go @@ -0,0 +1,340 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore +// +build ignore + +// This program can be used as go_android_GOARCH_exec by the Go tool. +// It executes binaries on an android device using adb. +package main + +import ( + "errors" + "fmt" + "go/build" + "io" + "log" + "os" + "os/exec" + "os/signal" + "path/filepath" + "runtime" + "strconv" + "strings" + "syscall" +) + +func run(args ...string) (string, error) { + cmd := adbCmd(args...) + buf := new(strings.Builder) + cmd.Stdout = io.MultiWriter(os.Stdout, buf) + // If the adb subprocess somehow hangs, go test will kill this wrapper + // and wait for our os.Stderr (and os.Stdout) to close as a result. + // However, if the os.Stderr (or os.Stdout) file descriptors are + // passed on, the hanging adb subprocess will hold them open and + // go test will hang forever. + // + // Avoid that by wrapping stderr, breaking the short circuit and + // forcing cmd.Run to use another pipe and goroutine to pass + // along stderr from adb. + cmd.Stderr = struct{ io.Writer }{os.Stderr} + err := cmd.Run() + if err != nil { + return "", fmt.Errorf("adb %s: %v", strings.Join(args, " "), err) + } + return buf.String(), nil +} + +func adb(args ...string) error { + if out, err := adbCmd(args...).CombinedOutput(); err != nil { + fmt.Fprintf(os.Stderr, "adb %s\n%s", strings.Join(args, " "), out) + return err + } + return nil +} + +func adbCmd(args ...string) *exec.Cmd { + if flags := os.Getenv("GOANDROID_ADB_FLAGS"); flags != "" { + args = append(strings.Split(flags, " "), args...) + } + return exec.Command("adb", args...) +} + +const ( + deviceRoot = "/data/local/tmp/go_android_exec" + deviceGoroot = deviceRoot + "/goroot" +) + +func main() { + log.SetFlags(0) + log.SetPrefix("go_android_exec: ") + exitCode, err := runMain() + if err != nil { + log.Fatal(err) + } + os.Exit(exitCode) +} + +func runMain() (int, error) { + // Concurrent use of adb is flaky, so serialize adb commands. + // See https://github.com/golang/go/issues/23795 or + // https://issuetracker.google.com/issues/73230216. + lockPath := filepath.Join(os.TempDir(), "go_android_exec-adb-lock") + lock, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0666) + if err != nil { + return 0, err + } + defer lock.Close() + if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil { + return 0, err + } + + // In case we're booting a device or emulator alongside all.bash, wait for + // it to be ready. adb wait-for-device is not enough, we have to + // wait for sys.boot_completed. + if err := adb("wait-for-device", "exec-out", "while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;"); err != nil { + return 0, err + } + + // Done once per make.bash. + if err := adbCopyGoroot(); err != nil { + return 0, err + } + + // Prepare a temporary directory that will be cleaned up at the end. + // Binary names can conflict. + // E.g. template.test from the {html,text}/template packages. + binName := filepath.Base(os.Args[1]) + deviceGotmp := fmt.Sprintf(deviceRoot+"/%s-%d", binName, os.Getpid()) + deviceGopath := deviceGotmp + "/gopath" + defer adb("exec-out", "rm", "-rf", deviceGotmp) // Clean up. + + // Determine the package by examining the current working + // directory, which will look something like + // "$GOROOT/src/mime/multipart" or "$GOPATH/src/golang.org/x/mobile". + // We extract everything after the $GOROOT or $GOPATH to run on the + // same relative directory on the target device. + subdir, inGoRoot, err := subdir() + if err != nil { + return 0, err + } + deviceCwd := filepath.Join(deviceGopath, subdir) + if inGoRoot { + deviceCwd = filepath.Join(deviceGoroot, subdir) + } else { + if err := adb("exec-out", "mkdir", "-p", deviceCwd); err != nil { + return 0, err + } + if err := adbCopyTree(deviceCwd, subdir); err != nil { + return 0, err + } + + // Copy .go files from the package. + goFiles, err := filepath.Glob("*.go") + if err != nil { + return 0, err + } + if len(goFiles) > 0 { + args := append(append([]string{"push"}, goFiles...), deviceCwd) + if err := adb(args...); err != nil { + return 0, err + } + } + } + + deviceBin := fmt.Sprintf("%s/%s", deviceGotmp, binName) + if err := adb("push", os.Args[1], deviceBin); err != nil { + return 0, err + } + + // Forward SIGQUIT from the go command to show backtraces from + // the binary instead of from this wrapper. + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGQUIT) + go func() { + for range quit { + // We don't have the PID of the running process; use the + // binary name instead. + adb("exec-out", "killall -QUIT "+binName) + } + }() + // In light of + // https://code.google.com/p/android/issues/detail?id=3254 + // dont trust the exitcode of adb. Instead, append the exitcode to + // the output and parse it from there. + const exitstr = "exitcode=" + cmd := `export TMPDIR="` + deviceGotmp + `"` + + `; export GOROOT="` + deviceGoroot + `"` + + `; export GOPATH="` + deviceGopath + `"` + + `; export CGO_ENABLED=0` + + `; export GOPROXY=` + os.Getenv("GOPROXY") + + `; export GOCACHE="` + deviceRoot + `/gocache"` + + `; export PATH=$PATH:"` + deviceGoroot + `/bin"` + + `; cd "` + deviceCwd + `"` + + "; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") + + "; echo -n " + exitstr + "$?" + output, err := run("exec-out", cmd) + signal.Reset(syscall.SIGQUIT) + close(quit) + if err != nil { + return 0, err + } + + exitIdx := strings.LastIndex(output, exitstr) + if exitIdx == -1 { + return 0, fmt.Errorf("no exit code: %q", output) + } + code, err := strconv.Atoi(output[exitIdx+len(exitstr):]) + if err != nil { + return 0, fmt.Errorf("bad exit code: %v", err) + } + return code, nil +} + +// subdir determines the package based on the current working directory, +// and returns the path to the package source relative to $GOROOT (or $GOPATH). +func subdir() (pkgpath string, underGoRoot bool, err error) { + cwd, err := os.Getwd() + if err != nil { + return "", false, err + } + cwd, err = filepath.EvalSymlinks(cwd) + if err != nil { + return "", false, err + } + goroot, err := filepath.EvalSymlinks(runtime.GOROOT()) + if err != nil { + return "", false, err + } + if subdir, err := filepath.Rel(goroot, cwd); err == nil { + if !strings.Contains(subdir, "..") { + return subdir, true, nil + } + } + + for _, p := range filepath.SplitList(build.Default.GOPATH) { + pabs, err := filepath.EvalSymlinks(p) + if err != nil { + return "", false, err + } + if subdir, err := filepath.Rel(pabs, cwd); err == nil { + if !strings.Contains(subdir, "..") { + return subdir, false, nil + } + } + } + return "", false, fmt.Errorf("the current path %q is not in either GOROOT(%q) or GOPATH(%q)", + cwd, runtime.GOROOT(), build.Default.GOPATH) +} + +// adbCopyTree copies testdata, go.mod, go.sum files from subdir +// and from parent directories all the way up to the root of subdir. +// go.mod and go.sum files are needed for the go tool modules queries, +// and the testdata directories for tests. It is common for tests to +// reach out into testdata from parent packages. +func adbCopyTree(deviceCwd, subdir string) error { + dir := "" + for { + for _, path := range []string{"testdata", "go.mod", "go.sum"} { + path := filepath.Join(dir, path) + if _, err := os.Stat(path); err != nil { + continue + } + devicePath := filepath.Join(deviceCwd, dir) + if err := adb("exec-out", "mkdir", "-p", devicePath); err != nil { + return err + } + if err := adb("push", path, devicePath); err != nil { + return err + } + } + if subdir == "." { + break + } + subdir = filepath.Dir(subdir) + dir = filepath.Join(dir, "..") + } + return nil +} + +// adbCopyGoroot clears deviceRoot for previous versions of GOROOT, GOPATH +// and temporary data. Then, it copies relevant parts of GOROOT to the device, +// including the go tool built for android. +// A lock file ensures this only happens once, even with concurrent exec +// wrappers. +func adbCopyGoroot() error { + // Also known by cmd/dist. The bootstrap command deletes the file. + statPath := filepath.Join(os.TempDir(), "go_android_exec-adb-sync-status") + stat, err := os.OpenFile(statPath, os.O_CREATE|os.O_RDWR, 0666) + if err != nil { + return err + } + defer stat.Close() + // Serialize check and copying. + if err := syscall.Flock(int(stat.Fd()), syscall.LOCK_EX); err != nil { + return err + } + s, err := io.ReadAll(stat) + if err != nil { + return err + } + if string(s) == "done" { + return nil + } + // Delete GOROOT, GOPATH and any leftover test data. + if err := adb("exec-out", "rm", "-rf", deviceRoot); err != nil { + return err + } + deviceBin := filepath.Join(deviceGoroot, "bin") + if err := adb("exec-out", "mkdir", "-p", deviceBin); err != nil { + return err + } + goroot := runtime.GOROOT() + // Build go for android. + goCmd := filepath.Join(goroot, "bin", "go") + tmpGo, err := os.CreateTemp("", "go_android_exec-cmd-go-*") + if err != nil { + return err + } + tmpGo.Close() + defer os.Remove(tmpGo.Name()) + + if out, err := exec.Command(goCmd, "build", "-o", tmpGo.Name(), "cmd/go").CombinedOutput(); err != nil { + return fmt.Errorf("failed to build go tool for device: %s\n%v", out, err) + } + deviceGo := filepath.Join(deviceBin, "go") + if err := adb("push", tmpGo.Name(), deviceGo); err != nil { + return err + } + for _, dir := range []string{"src", "test", "lib", "api"} { + if err := adb("push", filepath.Join(goroot, dir), filepath.Join(deviceGoroot)); err != nil { + return err + } + } + + // Copy only the relevant from pkg. + if err := adb("exec-out", "mkdir", "-p", filepath.Join(deviceGoroot, "pkg", "tool")); err != nil { + return err + } + if err := adb("push", filepath.Join(goroot, "pkg", "include"), filepath.Join(deviceGoroot, "pkg")); err != nil { + return err + } + runtimea, err := exec.Command(goCmd, "list", "-f", "{{.Target}}", "runtime").Output() + pkgdir := filepath.Dir(string(runtimea)) + if pkgdir == "" { + return errors.New("could not find android pkg dir") + } + if err := adb("push", pkgdir, filepath.Join(deviceGoroot, "pkg")); err != nil { + return err + } + tooldir := filepath.Join(goroot, "pkg", "tool", filepath.Base(pkgdir)) + if err := adb("push", tooldir, filepath.Join(deviceGoroot, "pkg", "tool")); err != nil { + return err + } + + if _, err := stat.Write([]byte("done")); err != nil { + return err + } + return nil +} diff --git a/misc/arm/a b/misc/arm/a new file mode 100755 index 0000000..644e775 --- /dev/null +++ b/misc/arm/a @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +# Copyright 2010 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# This is a small script for executing go binaries on the android platform. +# +# example: +# ./a 5.out foo bar baz +# +# The script exports the local values of GOARCH, GOTRACEBACK and GOGC +# to the android environment. +# +# Known issues: +# The script fails unless the last character output by the program is "\n" +# +# TODO(kaib): add gdb bridge support + +exp () +{ + if [ ${!1} ]; then + echo "export $1=\"${!1}\"; " + fi +} + +# adb does not correctly return the exit value of the executed program. use this +# wrapper to manually extract the exit value +rloc=/data/local/tmp/retval +rsize=$(adb shell "ls -l $rloc"|tr -s ' '|cut -d' ' -f4) +rcheck=38 +if [ "$rsize" != "$rcheck" ]; then +# echo "debug: retval size incorrect want $rcheck, got $rsize. uploading" + echo >/tmp/adb.retval '#!/system/bin/sh +"$@" +echo RETVAL: $?' + adb push /tmp/adb.retval $rloc >/dev/null 2>&1 + adb shell chmod 755 $rloc +fi + +# run the main binary +if [ "-g" == "$1" ]; then + adb forward tcp:$2 tcp:$2 + args=$(echo $*| cut -d' ' -f4-) + adb push $3 /data/local/tmp/$3 >/dev/null 2>&1 + adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \ + gdbserver :$2 /data/local/tmp/retval /data/local/tmp/$3 $args" \ + 2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL +else + if [ "$*" != "$1" ]; then + args=$(echo $*| cut -d' ' -f2-) + fi + adb push $1 /data/local/tmp/$1 >/dev/null 2>&1 + adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \ + /data/local/tmp/retval /data/local/tmp/$1 $args" \ + 2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL +fi +exit $(grep RETVAL /tmp/adb.out|tr -d '\n\r'| cut -d' ' -f2) diff --git a/misc/cgo/errors/argposition_test.go b/misc/cgo/errors/argposition_test.go new file mode 100644 index 0000000..dd26663 --- /dev/null +++ b/misc/cgo/errors/argposition_test.go @@ -0,0 +1,133 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 42580: cmd/cgo: shifting identifier position in ast + +package errorstest + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +type ShortPosition struct { + Line int + Column int + Visited bool +} + +type IdentPositionInfo map[string][]ShortPosition + +type Visitor struct { + identPosInfo IdentPositionInfo + fset *token.FileSet + t *testing.T +} + +func (v *Visitor) Visit(node ast.Node) ast.Visitor { + if ident, ok := node.(*ast.Ident); ok { + if expectedPositions, ok := v.identPosInfo[ident.Name]; ok { + gotMatch := false + var errorMessage strings.Builder + for caseIndex, expectedPos := range expectedPositions { + actualPosition := v.fset.PositionFor(ident.Pos(), true) + errorOccured := false + if expectedPos.Line != actualPosition.Line { + fmt.Fprintf(&errorMessage, "wrong line number for ident %s: expected: %d got: %d\n", ident.Name, expectedPos.Line, actualPosition.Line) + errorOccured = true + } + if expectedPos.Column != actualPosition.Column { + fmt.Fprintf(&errorMessage, "wrong column number for ident %s: expected: %d got: %d\n", ident.Name, expectedPos.Column, actualPosition.Column) + errorOccured = true + } + if errorOccured { + continue + } + gotMatch = true + expectedPositions[caseIndex].Visited = true + } + + if !gotMatch { + v.t.Errorf(errorMessage.String()) + } + } + } + return v +} + +func TestArgumentsPositions(t *testing.T) { + testdata, err := filepath.Abs("testdata") + if err != nil { + t.Fatal(err) + } + + tmpPath := t.TempDir() + + dir := filepath.Join(tmpPath, "src", "testpositions") + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatal(err) + } + + cmd := exec.Command("go", "tool", "cgo", + "-srcdir", testdata, + "-objdir", dir, + "issue42580.go") + cmd.Stderr = new(bytes.Buffer) + + err = cmd.Run() + if err != nil { + t.Fatalf("%s: %v\n%s", cmd, err, cmd.Stderr) + } + mainProcessed, err := os.ReadFile(filepath.Join(dir, "issue42580.cgo1.go")) + if err != nil { + t.Fatal(err) + } + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "", mainProcessed, parser.AllErrors) + if err != nil { + fmt.Println(err) + return + } + + expectation := IdentPositionInfo{ + "checkedPointer": []ShortPosition{ + ShortPosition{ + Line: 32, + Column: 56, + }, + }, + "singleInnerPointerChecked": []ShortPosition{ + ShortPosition{ + Line: 37, + Column: 91, + }, + }, + "doublePointerChecked": []ShortPosition{ + ShortPosition{ + Line: 42, + Column: 91, + }, + }, + } + for _, decl := range f.Decls { + if fdecl, ok := decl.(*ast.FuncDecl); ok { + ast.Walk(&Visitor{expectation, fset, t}, fdecl.Body) + } + } + for ident, positions := range expectation { + for _, position := range positions { + if !position.Visited { + t.Errorf("Position %d:%d missed for %s ident", position.Line, position.Column, ident) + } + } + } +} diff --git a/misc/cgo/errors/badsym_test.go b/misc/cgo/errors/badsym_test.go new file mode 100644 index 0000000..bc3ba2b --- /dev/null +++ b/misc/cgo/errors/badsym_test.go @@ -0,0 +1,219 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package errorstest + +import ( + "bytes" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "unicode" +) + +// A manually modified object file could pass unexpected characters +// into the files generated by cgo. + +const magicInput = "abcdefghijklmnopqrstuvwxyz0123" +const magicReplace = "\n//go:cgo_ldflag \"-badflag\"\n//" + +const cSymbol = "BadSymbol" + magicInput + "Name" +const cDefSource = "int " + cSymbol + " = 1;" +const cRefSource = "extern int " + cSymbol + "; int F() { return " + cSymbol + "; }" + +// goSource is the source code for the trivial Go file we use. +// We will replace TMPDIR with the temporary directory name. +const goSource = ` +package main + +// #cgo LDFLAGS: TMPDIR/cbad.o TMPDIR/cbad.so +// extern int F(); +import "C" + +func main() { + println(C.F()) +} +` + +func TestBadSymbol(t *testing.T) { + dir := t.TempDir() + + mkdir := func(base string) string { + ret := filepath.Join(dir, base) + if err := os.Mkdir(ret, 0755); err != nil { + t.Fatal(err) + } + return ret + } + + cdir := mkdir("c") + godir := mkdir("go") + + makeFile := func(mdir, base, source string) string { + ret := filepath.Join(mdir, base) + if err := os.WriteFile(ret, []byte(source), 0644); err != nil { + t.Fatal(err) + } + return ret + } + + cDefFile := makeFile(cdir, "cdef.c", cDefSource) + cRefFile := makeFile(cdir, "cref.c", cRefSource) + + ccCmd := cCompilerCmd(t) + + cCompile := func(arg, base, src string) string { + out := filepath.Join(cdir, base) + run := append(ccCmd, arg, "-o", out, src) + output, err := exec.Command(run[0], run[1:]...).CombinedOutput() + if err != nil { + t.Log(run) + t.Logf("%s", output) + t.Fatal(err) + } + if err := os.Remove(src); err != nil { + t.Fatal(err) + } + return out + } + + // Build a shared library that defines a symbol whose name + // contains magicInput. + + cShared := cCompile("-shared", "c.so", cDefFile) + + // Build an object file that refers to the symbol whose name + // contains magicInput. + + cObj := cCompile("-c", "c.o", cRefFile) + + // Rewrite the shared library and the object file, replacing + // magicInput with magicReplace. This will have the effect of + // introducing a symbol whose name looks like a cgo command. + // The cgo tool will use that name when it generates the + // _cgo_import.go file, thus smuggling a magic //go:cgo_ldflag + // pragma into a Go file. We used to not check the pragmas in + // _cgo_import.go. + + rewrite := func(from, to string) { + obj, err := os.ReadFile(from) + if err != nil { + t.Fatal(err) + } + + if bytes.Count(obj, []byte(magicInput)) == 0 { + t.Fatalf("%s: did not find magic string", from) + } + + if len(magicInput) != len(magicReplace) { + t.Fatalf("internal test error: different magic lengths: %d != %d", len(magicInput), len(magicReplace)) + } + + obj = bytes.ReplaceAll(obj, []byte(magicInput), []byte(magicReplace)) + + if err := os.WriteFile(to, obj, 0644); err != nil { + t.Fatal(err) + } + } + + cBadShared := filepath.Join(godir, "cbad.so") + rewrite(cShared, cBadShared) + + cBadObj := filepath.Join(godir, "cbad.o") + rewrite(cObj, cBadObj) + + goSourceBadObject := strings.ReplaceAll(goSource, "TMPDIR", godir) + makeFile(godir, "go.go", goSourceBadObject) + + makeFile(godir, "go.mod", "module badsym") + + // Try to build our little package. + cmd := exec.Command("go", "build", "-ldflags=-v") + cmd.Dir = godir + output, err := cmd.CombinedOutput() + + // The build should fail, but we want it to fail because we + // detected the error, not because we passed a bad flag to the + // C linker. + + if err == nil { + t.Errorf("go build succeeded unexpectedly") + } + + t.Logf("%s", output) + + for _, line := range bytes.Split(output, []byte("\n")) { + if bytes.Contains(line, []byte("dynamic symbol")) && bytes.Contains(line, []byte("contains unsupported character")) { + // This is the error from cgo. + continue + } + + // We passed -ldflags=-v to see the external linker invocation, + // which should not include -badflag. + if bytes.Contains(line, []byte("-badflag")) { + t.Error("output should not mention -badflag") + } + + // Also check for compiler errors, just in case. + // GCC says "unrecognized command line option". + // clang says "unknown argument". + if bytes.Contains(line, []byte("unrecognized")) || bytes.Contains(output, []byte("unknown")) { + t.Error("problem should have been caught before invoking C linker") + } + } +} + +func cCompilerCmd(t *testing.T) []string { + cc := []string{goEnv(t, "CC")} + + out := goEnv(t, "GOGCCFLAGS") + quote := '\000' + start := 0 + lastSpace := true + backslash := false + s := string(out) + for i, c := range s { + if quote == '\000' && unicode.IsSpace(c) { + if !lastSpace { + cc = append(cc, s[start:i]) + lastSpace = true + } + } else { + if lastSpace { + start = i + lastSpace = false + } + if quote == '\000' && !backslash && (c == '"' || c == '\'') { + quote = c + backslash = false + } else if !backslash && quote == c { + quote = '\000' + } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { + backslash = true + } else { + backslash = false + } + } + } + if !lastSpace { + cc = append(cc, s[start:]) + } + + // Force reallocation (and avoid aliasing bugs) for tests that append to cc. + cc = cc[:len(cc):len(cc)] + + return cc +} + +func goEnv(t *testing.T, key string) string { + out, err := exec.Command("go", "env", key).CombinedOutput() + if err != nil { + t.Logf("go env %s\n", key) + t.Logf("%s", out) + t.Fatal(err) + } + return strings.TrimSpace(string(out)) +} diff --git a/misc/cgo/errors/errors_test.go b/misc/cgo/errors/errors_test.go new file mode 100644 index 0000000..8168032 --- /dev/null +++ b/misc/cgo/errors/errors_test.go @@ -0,0 +1,166 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package errorstest + +import ( + "bytes" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" + "testing" +) + +func path(file string) string { + return filepath.Join("testdata", file) +} + +func check(t *testing.T, file string) { + t.Run(file, func(t *testing.T) { + t.Parallel() + + contents, err := os.ReadFile(path(file)) + if err != nil { + t.Fatal(err) + } + var errors []*regexp.Regexp + for i, line := range bytes.Split(contents, []byte("\n")) { + if bytes.HasSuffix(line, []byte("ERROR HERE")) { + re := regexp.MustCompile(regexp.QuoteMeta(fmt.Sprintf("%s:%d:", file, i+1))) + errors = append(errors, re) + continue + } + + if _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: ")); ok { + re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag)) + if err != nil { + t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag) + continue + } + errors = append(errors, re) + } + + if _, frag, ok := bytes.Cut(line, []byte("ERROR MESSAGE: ")); ok { + re, err := regexp.Compile(string(frag)) + if err != nil { + t.Errorf("Invalid regexp after `ERROR MESSAGE: `: %#q", frag) + continue + } + errors = append(errors, re) + } + } + if len(errors) == 0 { + t.Fatalf("cannot find ERROR HERE") + } + expect(t, file, errors) + }) +} + +func expect(t *testing.T, file string, errors []*regexp.Regexp) { + dir, err := os.MkdirTemp("", filepath.Base(t.Name())) + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + dst := filepath.Join(dir, strings.TrimSuffix(file, ".go")) + cmd := exec.Command("go", "build", "-gcflags=-L -e", "-o="+dst, path(file)) // TODO(gri) no need for -gcflags=-L if go tool is adjusted + out, err := cmd.CombinedOutput() + if err == nil { + t.Errorf("expected cgo to fail but it succeeded") + } + + lines := bytes.Split(out, []byte("\n")) + for _, re := range errors { + found := false + for _, line := range lines { + if re.Match(line) { + t.Logf("found match for %#q: %q", re, line) + found = true + break + } + } + if !found { + t.Errorf("expected error output to contain %#q", re) + } + } + + if t.Failed() { + t.Logf("actual output:\n%s", out) + } +} + +func sizeofLongDouble(t *testing.T) int { + cmd := exec.Command("go", "run", path("long_double_size.go")) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out) + } + + i, err := strconv.Atoi(strings.TrimSpace(string(out))) + if err != nil { + t.Fatalf("long_double_size.go printed invalid size: %s", out) + } + return i +} + +func TestReportsTypeErrors(t *testing.T) { + for _, file := range []string{ + "err1.go", + "err2.go", + "err5.go", + "issue11097a.go", + "issue11097b.go", + "issue18452.go", + "issue18889.go", + "issue28721.go", + "issue33061.go", + "issue50710.go", + } { + check(t, file) + } + + if sizeofLongDouble(t) > 8 { + for _, file := range []string{ + "err4.go", + "issue28069.go", + } { + check(t, file) + } + } +} + +func TestToleratesOptimizationFlag(t *testing.T) { + for _, cflags := range []string{ + "", + "-O", + } { + cflags := cflags + t.Run(cflags, func(t *testing.T) { + t.Parallel() + + cmd := exec.Command("go", "build", path("issue14669.go")) + cmd.Env = append(os.Environ(), "CGO_CFLAGS="+cflags) + out, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out) + } + }) + } +} + +func TestMallocCrashesOnNil(t *testing.T) { + t.Parallel() + + cmd := exec.Command("go", "run", path("malloc.go")) + out, err := cmd.CombinedOutput() + if err == nil { + t.Logf("%#q:\n%s", strings.Join(cmd.Args, " "), out) + t.Fatalf("succeeded unexpectedly") + } +} diff --git a/misc/cgo/errors/ptr_test.go b/misc/cgo/errors/ptr_test.go new file mode 100644 index 0000000..0f39dc8 --- /dev/null +++ b/misc/cgo/errors/ptr_test.go @@ -0,0 +1,632 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tests that cgo detects invalid pointer passing at runtime. + +package errorstest + +import ( + "bytes" + "flag" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "sync/atomic" + "testing" +) + +var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up") + +// ptrTest is the tests without the boilerplate. +type ptrTest struct { + name string // for reporting + c string // the cgo comment + c1 string // cgo comment forced into non-export cgo file + imports []string // a list of imports + support string // supporting functions + body string // the body of the main function + extra []extra // extra files + fail bool // whether the test should fail + expensive bool // whether the test requires the expensive check +} + +type extra struct { + name string + contents string +} + +var ptrTests = []ptrTest{ + { + // Passing a pointer to a struct that contains a Go pointer. + name: "ptr1", + c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`, + body: `C.f1(&C.s1{new(C.int)})`, + fail: true, + }, + { + // Passing a pointer to a struct that contains a Go pointer. + name: "ptr2", + c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`, + body: `p := &C.s2{new(C.int)}; C.f2(p)`, + fail: true, + }, + { + // Passing a pointer to an int field of a Go struct + // that (irrelevantly) contains a Go pointer. + name: "ok1", + c: `struct s3 { int i; int *p; }; void f3(int *p) {}`, + body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`, + fail: false, + }, + { + // Passing a pointer to a pointer field of a Go struct. + name: "ptrfield", + c: `struct s4 { int i; int *p; }; void f4(int **p) {}`, + body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`, + fail: true, + }, + { + // Passing a pointer to a pointer field of a Go + // struct, where the field does not contain a Go + // pointer, but another field (irrelevantly) does. + name: "ptrfieldok", + c: `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`, + body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`, + fail: false, + }, + { + // Passing the address of a slice with no Go pointers. + name: "sliceok1", + c: `void f6(void **p) {}`, + imports: []string{"unsafe"}, + body: `s := []unsafe.Pointer{nil}; C.f6(&s[0])`, + fail: false, + }, + { + // Passing the address of a slice with a Go pointer. + name: "sliceptr1", + c: `void f7(void **p) {}`, + imports: []string{"unsafe"}, + body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`, + fail: true, + }, + { + // Passing the address of a slice with a Go pointer, + // where we are passing the address of an element that + // is not a Go pointer. + name: "sliceptr2", + c: `void f8(void **p) {}`, + imports: []string{"unsafe"}, + body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`, + fail: true, + }, + { + // Passing the address of a slice that is an element + // in a struct only looks at the slice. + name: "sliceok2", + c: `void f9(void **p) {}`, + imports: []string{"unsafe"}, + support: `type S9 struct { p *int; s []unsafe.Pointer }`, + body: `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`, + fail: false, + }, + { + // Passing the address of a slice of an array that is + // an element in a struct, with a type conversion. + name: "sliceok3", + c: `void f10(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S10 struct { p *int; a [4]byte }`, + body: `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`, + fail: false, + }, + { + // Passing the address of a slice of an array that is + // an element in a struct, with a type conversion. + name: "sliceok4", + c: `typedef void* PV11; void f11(PV11 p) {}`, + imports: []string{"unsafe"}, + support: `type S11 struct { p *int; a [4]byte }`, + body: `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`, + fail: false, + }, + { + // Passing the address of a static variable with no + // pointers doesn't matter. + name: "varok", + c: `void f12(char** parg) {}`, + support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`, + body: `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`, + fail: false, + }, + { + // Passing the address of a static variable with + // pointers does matter. + name: "var1", + c: `void f13(char*** parg) {}`, + support: `var hello13 = [...]*C.char{new(C.char)}`, + body: `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`, + fail: true, + }, + { + // Storing a Go pointer into C memory should fail. + name: "barrier", + c: `#include <stdlib.h> + char **f14a() { return malloc(sizeof(char*)); } + void f14b(char **p) {}`, + body: `p := C.f14a(); *p = new(C.char); C.f14b(p)`, + fail: true, + expensive: true, + }, + { + // Storing a Go pointer into C memory by assigning a + // large value should fail. + name: "barrierstruct", + c: `#include <stdlib.h> + struct s15 { char *a[10]; }; + struct s15 *f15() { return malloc(sizeof(struct s15)); } + void f15b(struct s15 *p) {}`, + body: `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`, + fail: true, + expensive: true, + }, + { + // Storing a Go pointer into C memory using a slice + // copy should fail. + name: "barrierslice", + c: `#include <stdlib.h> + struct s16 { char *a[10]; }; + struct s16 *f16() { return malloc(sizeof(struct s16)); } + void f16b(struct s16 *p) {}`, + body: `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`, + fail: true, + expensive: true, + }, + { + // A very large value uses a GC program, which is a + // different code path. + name: "barriergcprogarray", + c: `#include <stdlib.h> + struct s17 { char *a[32769]; }; + struct s17 *f17() { return malloc(sizeof(struct s17)); } + void f17b(struct s17 *p) {}`, + body: `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`, + fail: true, + expensive: true, + }, + { + // Similar case, with a source on the heap. + name: "barriergcprogarrayheap", + c: `#include <stdlib.h> + struct s18 { char *a[32769]; }; + struct s18 *f18() { return malloc(sizeof(struct s18)); } + void f18b(struct s18 *p) {} + void f18c(void *p) {}`, + imports: []string{"unsafe"}, + body: `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`, + fail: true, + expensive: true, + }, + { + // A GC program with a struct. + name: "barriergcprogstruct", + c: `#include <stdlib.h> + struct s19a { char *a[32769]; }; + struct s19b { struct s19a f; }; + struct s19b *f19() { return malloc(sizeof(struct s19b)); } + void f19b(struct s19b *p) {}`, + body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`, + fail: true, + expensive: true, + }, + { + // Similar case, with a source on the heap. + name: "barriergcprogstructheap", + c: `#include <stdlib.h> + struct s20a { char *a[32769]; }; + struct s20b { struct s20a f; }; + struct s20b *f20() { return malloc(sizeof(struct s20b)); } + void f20b(struct s20b *p) {} + void f20c(void *p) {}`, + imports: []string{"unsafe"}, + body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`, + fail: true, + expensive: true, + }, + { + // Exported functions may not return Go pointers. + name: "export1", + c: `extern unsigned char *GoFn21();`, + support: `//export GoFn21 + func GoFn21() *byte { return new(byte) }`, + body: `C.GoFn21()`, + fail: true, + }, + { + // Returning a C pointer is fine. + name: "exportok", + c: `#include <stdlib.h> + extern unsigned char *GoFn22();`, + support: `//export GoFn22 + func GoFn22() *byte { return (*byte)(C.malloc(1)) }`, + body: `C.GoFn22()`, + }, + { + // Passing a Go string is fine. + name: "passstring", + c: `#include <stddef.h> + typedef struct { const char *p; ptrdiff_t n; } gostring23; + gostring23 f23(gostring23 s) { return s; }`, + imports: []string{"unsafe"}, + body: `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`, + }, + { + // Passing a slice of Go strings fails. + name: "passstringslice", + c: `void f24(void *p) {}`, + imports: []string{"strings", "unsafe"}, + support: `type S24 struct { a [1]string }`, + body: `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`, + fail: true, + }, + { + // Exported functions may not return strings. + name: "retstring", + c: `extern void f25();`, + imports: []string{"strings"}, + support: `//export GoStr25 + func GoStr25() string { return strings.Repeat("a", 2) }`, + body: `C.f25()`, + c1: `#include <stddef.h> + typedef struct { const char *p; ptrdiff_t n; } gostring25; + extern gostring25 GoStr25(); + void f25() { GoStr25(); }`, + fail: true, + }, + { + // Don't check non-pointer data. + // Uses unsafe code to get a pointer we shouldn't check. + // Although we use unsafe, the uintptr represents an integer + // that happens to have the same representation as a pointer; + // that is, we are testing something that is not unsafe. + name: "ptrdata1", + c: `#include <stdlib.h> + void f26(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`, + body: `i := 0; p := &S26{u:uintptr(unsafe.Pointer(&i))}; q := (*S26)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f26(unsafe.Pointer(q))`, + fail: false, + }, + { + // Like ptrdata1, but with a type that uses a GC program. + name: "ptrdata2", + c: `#include <stdlib.h> + void f27(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`, + body: `i := 0; p := S27{u:uintptr(unsafe.Pointer(&i))}; q := (*S27)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f27(unsafe.Pointer(q))`, + fail: false, + }, + { + // Check deferred pointers when they are used, not + // when the defer statement is run. + name: "defer1", + c: `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`, + body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`, + fail: true, + }, + { + // Check a pointer to a union if the union has any + // pointer fields. + name: "union1", + c: `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`, + imports: []string{"unsafe"}, + body: `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`, + fail: true, + }, + { + // Don't check a pointer to a union if the union does + // not have any pointer fields. + // Like ptrdata1 above, the uintptr represents an + // integer that happens to have the same + // representation as a pointer. + name: "union2", + c: `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`, + imports: []string{"unsafe"}, + body: `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`, + fail: false, + }, + { + // Test preemption while entering a cgo call. Issue #21306. + name: "preemptduringcall", + c: `void f30() {}`, + imports: []string{"runtime", "sync"}, + body: `var wg sync.WaitGroup; wg.Add(100); for i := 0; i < 100; i++ { go func(i int) { for j := 0; j < 100; j++ { C.f30(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`, + fail: false, + }, + { + // Test poller deadline with cgocheck=2. Issue #23435. + name: "deadline", + c: `#define US31 10`, + imports: []string{"os", "time"}, + body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`, + fail: false, + }, + { + // Test for double evaluation of channel receive. + name: "chanrecv", + c: `void f32(char** p) {}`, + imports: []string{"time"}, + body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f32(&(<-c)[0]);`, + fail: false, + }, + { + // Test that converting the address of a struct field + // to unsafe.Pointer still just checks that field. + // Issue #25941. + name: "structfield", + c: `void f33(void* p) {}`, + imports: []string{"unsafe"}, + support: `type S33 struct { p *int; a [8]byte; u uintptr }`, + body: `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`, + fail: false, + }, + { + // Test that converting multiple struct field + // addresses to unsafe.Pointer still just checks those + // fields. Issue #25941. + name: "structfield2", + c: `void f34(void* p, int r, void* s) {}`, + imports: []string{"unsafe"}, + support: `type S34 struct { a [8]byte; p *int; b int64; }`, + body: `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`, + fail: false, + }, + { + // Test that second argument to cgoCheckPointer is + // evaluated when a deferred function is deferred, not + // when it is run. + name: "defer2", + c: `void f35(char **pc) {}`, + support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`, + body: `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`, + fail: false, + }, + { + // Test that indexing into a function call still + // examines only the slice being indexed. + name: "buffer", + c: `void f36(void *p) {}`, + imports: []string{"bytes", "unsafe"}, + body: `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`, + fail: false, + }, + { + // Test that bgsweep releasing a finalizer is OK. + name: "finalizer", + c: `// Nothing to declare.`, + imports: []string{"os"}, + support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`, + body: `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`, + fail: false, + }, + { + // Test that converting generated struct to interface is OK. + name: "structof", + c: `// Nothing to declare.`, + imports: []string{"reflect"}, + support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`, + body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`, + fail: false, + }, + { + // Test that a converted address of a struct field results + // in a check for just that field and not the whole struct. + name: "structfieldcast", + c: `struct S40i { int i; int* p; }; void f40(struct S40i* p) {}`, + support: `type S40 struct { p *int; a C.struct_S40i }`, + body: `s := &S40{p: new(int)}; C.f40((*C.struct_S40i)(&s.a))`, + fail: false, + }, +} + +func TestPointerChecks(t *testing.T) { + dir, exe := buildPtrTests(t) + + // We (TestPointerChecks) return before the parallel subtest functions do, + // so we can't just defer os.RemoveAll(dir). Instead we have to wait for + // the parallel subtests to finish. This code looks racy but is not: + // the add +1 run in serial before testOne blocks. The -1 run in parallel + // after testOne finishes. + var pending int32 + for _, pt := range ptrTests { + pt := pt + t.Run(pt.name, func(t *testing.T) { + atomic.AddInt32(&pending, +1) + defer func() { + if atomic.AddInt32(&pending, -1) == 0 { + os.RemoveAll(dir) + } + }() + testOne(t, pt, exe) + }) + } +} + +func buildPtrTests(t *testing.T) (dir, exe string) { + var gopath string + if *tmp != "" { + gopath = *tmp + dir = "" + } else { + d, err := os.MkdirTemp("", filepath.Base(t.Name())) + if err != nil { + t.Fatal(err) + } + dir = d + gopath = d + } + + src := filepath.Join(gopath, "src", "ptrtest") + if err := os.MkdirAll(src, 0777); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(src, "go.mod"), []byte("module ptrtest"), 0666); err != nil { + t.Fatal(err) + } + + // Prepare two cgo inputs: one for standard cgo and one for //export cgo. + // (The latter cannot have C definitions, only declarations.) + var cgo1, cgo2 bytes.Buffer + fmt.Fprintf(&cgo1, "package main\n\n/*\n") + fmt.Fprintf(&cgo2, "package main\n\n/*\n") + + // C code + for _, pt := range ptrTests { + cgo := &cgo1 + if strings.Contains(pt.support, "//export") { + cgo = &cgo2 + } + fmt.Fprintf(cgo, "%s\n", pt.c) + fmt.Fprintf(&cgo1, "%s\n", pt.c1) + } + fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n") + fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n") + + // Imports + did1 := make(map[string]bool) + did2 := make(map[string]bool) + did1["os"] = true // for ptrTestMain + fmt.Fprintf(&cgo1, "import \"os\"\n") + + for _, pt := range ptrTests { + did := did1 + cgo := &cgo1 + if strings.Contains(pt.support, "//export") { + did = did2 + cgo = &cgo2 + } + for _, imp := range pt.imports { + if !did[imp] { + did[imp] = true + fmt.Fprintf(cgo, "import %q\n", imp) + } + } + } + + // Func support and bodies. + for _, pt := range ptrTests { + cgo := &cgo1 + if strings.Contains(pt.support, "//export") { + cgo = &cgo2 + } + fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body) + } + + // Func list and main dispatch. + fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n") + for _, pt := range ptrTests { + fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name) + } + fmt.Fprintf(&cgo1, "}\n\n") + fmt.Fprintf(&cgo1, "%s\n", ptrTestMain) + + if err := os.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil { + t.Fatal(err) + } + + cmd := exec.Command("go", "build", "-o", "ptrtest.exe") + cmd.Dir = src + cmd.Env = append(os.Environ(), "GOPATH="+gopath) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("go build: %v\n%s", err, out) + } + + return dir, filepath.Join(src, "ptrtest.exe") +} + +const ptrTestMain = ` +func main() { + for _, arg := range os.Args[1:] { + f := funcs[arg] + if f == nil { + panic("missing func "+arg) + } + f() + } +} +` + +var csem = make(chan bool, 16) + +func testOne(t *testing.T, pt ptrTest, exe string) { + t.Parallel() + + // Run the tests in parallel, but don't run too many + // executions in parallel, to avoid overloading the system. + runcmd := func(cgocheck string) ([]byte, error) { + csem <- true + defer func() { <-csem }() + cmd := exec.Command(exe, pt.name) + cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck) + return cmd.CombinedOutput() + } + + if pt.expensive { + buf, err := runcmd("1") + if err != nil { + t.Logf("%s", buf) + if pt.fail { + t.Fatalf("test marked expensive, but failed when not expensive: %v", err) + } else { + t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err) + } + } + + } + + cgocheck := "" + if pt.expensive { + cgocheck = "2" + } + + buf, err := runcmd(cgocheck) + if pt.fail { + if err == nil { + t.Logf("%s", buf) + t.Fatalf("did not fail as expected") + } else if !bytes.Contains(buf, []byte("Go pointer")) { + t.Logf("%s", buf) + t.Fatalf("did not print expected error (failed with %v)", err) + } + } else { + if err != nil { + t.Logf("%s", buf) + t.Fatalf("failed unexpectedly: %v", err) + } + + if !pt.expensive { + // Make sure it passes with the expensive checks. + buf, err := runcmd("2") + if err != nil { + t.Logf("%s", buf) + t.Fatalf("failed unexpectedly with expensive checks: %v", err) + } + } + } + + if pt.fail { + buf, err := runcmd("0") + if err != nil { + t.Logf("%s", buf) + t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err) + } + } +} diff --git a/misc/cgo/errors/testdata/err1.go b/misc/cgo/errors/testdata/err1.go new file mode 100644 index 0000000..ced7443 --- /dev/null +++ b/misc/cgo/errors/testdata/err1.go @@ -0,0 +1,22 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#cgo LDFLAGS: -L/nonexist + +void test() { + xxx; // ERROR HERE +} + +// Issue 8442. Cgo output unhelpful error messages for +// invalid C preambles. +void issue8442foo(UNDEF*); // ERROR HERE +*/ +import "C" + +func main() { + C.test() +} diff --git a/misc/cgo/errors/testdata/err2.go b/misc/cgo/errors/testdata/err2.go new file mode 100644 index 0000000..aa94158 --- /dev/null +++ b/misc/cgo/errors/testdata/err2.go @@ -0,0 +1,110 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <stdio.h> + +typedef struct foo foo_t; +typedef struct bar bar_t; + +foo_t *foop; + +long double x = 0; + +static int transform(int x) { return x; } + +typedef void v; +void F(v** p) {} + +void fvi(void *p, int x) {} + +void fppi(int** p) {} + +int i; +void fi(int i) {} +*/ +import "C" +import ( + "unsafe" +) + +func main() { + s := "" + _ = s + C.malloc(s) // ERROR HERE + + x := (*C.bar_t)(nil) + C.foop = x // ERROR HERE + + // issue 13129: used to output error about C.unsignedshort with CC=clang + var x1 C.ushort + x1 = int(0) // ERROR HERE: C\.ushort + + // issue 13423 + _ = C.fopen() // ERROR HERE + + // issue 13467 + var x2 rune = '✈' + var _ rune = C.transform(x2) // ERROR HERE: C\.int + + // issue 13635: used to output error about C.unsignedchar. + // This test tests all such types. + var ( + _ C.uchar = "uc" // ERROR HERE: C\.uchar + _ C.schar = "sc" // ERROR HERE: C\.schar + _ C.ushort = "us" // ERROR HERE: C\.ushort + _ C.uint = "ui" // ERROR HERE: C\.uint + _ C.ulong = "ul" // ERROR HERE: C\.ulong + _ C.longlong = "ll" // ERROR HERE: C\.longlong + _ C.ulonglong = "ull" // ERROR HERE: C\.ulonglong + _ C.complexfloat = "cf" // ERROR HERE: C\.complexfloat + _ C.complexdouble = "cd" // ERROR HERE: C\.complexdouble + ) + + // issue 13830 + // cgo converts C void* to Go unsafe.Pointer, so despite appearances C + // void** is Go *unsafe.Pointer. This test verifies that we detect the + // problem at build time. + { + type v [0]byte + + f := func(p **v) { + C.F((**C.v)(unsafe.Pointer(p))) // ERROR HERE + } + var p *v + f(&p) + } + + // issue 16116 + _ = C.fvi(1) // ERROR HERE + + // Issue 16591: Test that we detect an invalid call that was being + // hidden by a type conversion inserted by cgo checking. + { + type x *C.int + var p *x + C.fppi(p) // ERROR HERE + } + + // issue 26745 + _ = func(i int) int { + // typecheck reports at column 14 ('+'), but types2 reports at + // column 10 ('C'). + // TODO(mdempsky): Investigate why, and see if types2 can be + // updated to match typecheck behavior. + return C.i + 1 // ERROR HERE: \b(10|14)\b + } + _ = func(i int) { + // typecheck reports at column 7 ('('), but types2 reports at + // column 8 ('i'). The types2 position is more correct, but + // updating typecheck here is fundamentally challenging because of + // IR limitations. + C.fi(i) // ERROR HERE: \b(7|8)\b + } + + C.fi = C.fi // ERROR HERE + +} diff --git a/misc/cgo/errors/testdata/err4.go b/misc/cgo/errors/testdata/err4.go new file mode 100644 index 0000000..8e5f78e --- /dev/null +++ b/misc/cgo/errors/testdata/err4.go @@ -0,0 +1,15 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +long double x = 0; +*/ +import "C" + +func main() { + _ = C.x // ERROR HERE + _ = C.x +} diff --git a/misc/cgo/errors/testdata/err5.go b/misc/cgo/errors/testdata/err5.go new file mode 100644 index 0000000..c12a290 --- /dev/null +++ b/misc/cgo/errors/testdata/err5.go @@ -0,0 +1,11 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +//line /tmp/_cgo_.go:1 +//go:cgo_dynamic_linker "/elf/interp" +// ERROR MESSAGE: only allowed in cgo-generated code + +func main() {} diff --git a/misc/cgo/errors/testdata/issue11097a.go b/misc/cgo/errors/testdata/issue11097a.go new file mode 100644 index 0000000..028d10c --- /dev/null +++ b/misc/cgo/errors/testdata/issue11097a.go @@ -0,0 +1,15 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +//enum test { foo, bar }; +*/ +import "C" + +func main() { + var a = C.enum_test(1) // ERROR HERE + _ = a +} diff --git a/misc/cgo/errors/testdata/issue11097b.go b/misc/cgo/errors/testdata/issue11097b.go new file mode 100644 index 0000000..b00f24f --- /dev/null +++ b/misc/cgo/errors/testdata/issue11097b.go @@ -0,0 +1,15 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +//enum test { foo, bar }; +*/ +import "C" + +func main() { + p := new(C.enum_test) // ERROR HERE + _ = p +} diff --git a/misc/cgo/errors/testdata/issue14669.go b/misc/cgo/errors/testdata/issue14669.go new file mode 100644 index 0000000..04d2bcb --- /dev/null +++ b/misc/cgo/errors/testdata/issue14669.go @@ -0,0 +1,23 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 14669: test that fails when build with CGO_CFLAGS selecting +// optimization. + +package p + +/* +const int E = 1; + +typedef struct s { + int c; +} s; +*/ +import "C" + +func F() { + _ = C.s{ + c: C.E, + } +} diff --git a/misc/cgo/errors/testdata/issue18452.go b/misc/cgo/errors/testdata/issue18452.go new file mode 100644 index 0000000..0386d76 --- /dev/null +++ b/misc/cgo/errors/testdata/issue18452.go @@ -0,0 +1,18 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 18452: show pos info in undefined name errors + +package p + +import ( + "C" + "fmt" +) + +func a() { + fmt.Println("Hello, world!") + C.function_that_does_not_exist() // ERROR HERE + C.pi // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/issue18889.go b/misc/cgo/errors/testdata/issue18889.go new file mode 100644 index 0000000..bba6b8f --- /dev/null +++ b/misc/cgo/errors/testdata/issue18889.go @@ -0,0 +1,7 @@ +package main + +import "C" + +func main() { + _ = C.malloc // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/issue28069.go b/misc/cgo/errors/testdata/issue28069.go new file mode 100644 index 0000000..e19a3b4 --- /dev/null +++ b/misc/cgo/errors/testdata/issue28069.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that the error message for an unrepresentable typedef in a +// union appears on the right line. This test is only run if the size +// of long double is larger than 64. + +package main + +/* +typedef long double Float128; + +typedef struct SV { + union { + Float128 float128; + } value; +} SV; +*/ +import "C" + +type ts struct { + tv *C.SV // ERROR HERE +} + +func main() {} diff --git a/misc/cgo/errors/testdata/issue28721.go b/misc/cgo/errors/testdata/issue28721.go new file mode 100644 index 0000000..0eb2a92 --- /dev/null +++ b/misc/cgo/errors/testdata/issue28721.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// cgo should reject the use of mangled C names. + +package main + +/* +typedef struct a { + int i; +} a; +void fn(void) {} +*/ +import "C" + +type B _Ctype_struct_a // ERROR HERE + +var a _Ctype_struct_a // ERROR HERE + +type A struct { + a *_Ctype_struct_a // ERROR HERE +} + +var notExist _Ctype_NotExist // ERROR HERE + +func main() { + _Cfunc_fn() // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/issue33061.go b/misc/cgo/errors/testdata/issue33061.go new file mode 100644 index 0000000..77d5f7a --- /dev/null +++ b/misc/cgo/errors/testdata/issue33061.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// cgo shouldn't crash if there is an extra argument with a C reference. + +package main + +// void F(void* p) {}; +import "C" + +import "unsafe" + +func F() { + var i int + C.F(unsafe.Pointer(&i), C.int(0)) // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/issue42580.go b/misc/cgo/errors/testdata/issue42580.go new file mode 100644 index 0000000..aba80df --- /dev/null +++ b/misc/cgo/errors/testdata/issue42580.go @@ -0,0 +1,44 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 42580: cmd/cgo: shifting identifier position in ast + +package cgotest + +// typedef int (*intFunc) (); +// +// char* strarg = ""; +// +// int func_with_char(char* arg, void* dummy) +// {return 5;} +// +// int* get_arr(char* arg, void* dummy) +// {return NULL;} +import "C" +import "unsafe" + +// Test variables +var ( + checkedPointer = []byte{1} + doublePointerChecked = []byte{1} + singleInnerPointerChecked = []byte{1} +) + +// This test checks the positions of variable identifiers. +// Changing the positions of the test variables idents after this point will break the test. + +func TestSingleArgumentCast() C.int { + retcode := C.func_with_char((*C.char)(unsafe.Pointer(&checkedPointer[0])), unsafe.Pointer(C.strarg)) + return retcode +} + +func TestSingleArgumentCastRecFuncAsSimpleArg() C.int { + retcode := C.func_with_char((*C.char)(unsafe.Pointer(C.get_arr((*C.char)(unsafe.Pointer(&singleInnerPointerChecked[0])), unsafe.Pointer(C.strarg)))), nil) + return retcode +} + +func TestSingleArgumentCastRecFunc() C.int { + retcode := C.func_with_char((*C.char)(unsafe.Pointer(C.get_arr((*C.char)(unsafe.Pointer(&doublePointerChecked[0])), unsafe.Pointer(C.strarg)))), unsafe.Pointer(C.strarg)) + return retcode +} diff --git a/misc/cgo/errors/testdata/issue50710.go b/misc/cgo/errors/testdata/issue50710.go new file mode 100644 index 0000000..dffea22 --- /dev/null +++ b/misc/cgo/errors/testdata/issue50710.go @@ -0,0 +1,14 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// size_t StrLen(_GoString_ s) { +// return _GoStringLen(s); +// } +import "C" + +func main() { + C.StrLen1() // ERROR HERE +} diff --git a/misc/cgo/errors/testdata/long_double_size.go b/misc/cgo/errors/testdata/long_double_size.go new file mode 100644 index 0000000..8b797f8 --- /dev/null +++ b/misc/cgo/errors/testdata/long_double_size.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +const int sizeofLongDouble = sizeof(long double); +*/ +import "C" + +import "fmt" + +func main() { + fmt.Println(C.sizeofLongDouble) +} diff --git a/misc/cgo/errors/testdata/malloc.go b/misc/cgo/errors/testdata/malloc.go new file mode 100644 index 0000000..65da020 --- /dev/null +++ b/misc/cgo/errors/testdata/malloc.go @@ -0,0 +1,34 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that C.malloc does not return nil. + +package main + +// #include <stdlib.h> +import "C" + +import ( + "fmt" + "runtime" +) + +func main() { + var size C.size_t + size-- + + // The Dragonfly libc succeeds when asked to allocate + // 0xffffffffffffffff bytes, so pass a different value that + // causes it to fail. + if runtime.GOOS == "dragonfly" { + size = C.size_t(0x7fffffff << (32 * (^uintptr(0) >> 63))) + } + + p := C.malloc(size) + if p == nil { + fmt.Println("malloc: C.malloc returned nil") + // Just exit normally--the test script expects this + // program to crash, so exiting normally indicates failure. + } +} diff --git a/misc/cgo/fortran/fortran_test.go b/misc/cgo/fortran/fortran_test.go new file mode 100644 index 0000000..4604a4d --- /dev/null +++ b/misc/cgo/fortran/fortran_test.go @@ -0,0 +1,81 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fortran + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func TestFortran(t *testing.T) { + // Find the FORTRAN compiler. + fc := os.Getenv("FC") + if fc == "" { + fc, _ = exec.LookPath("gfortran") + } + if fc == "" { + t.Skip("fortran compiler not found (try setting $FC)") + } + + var fcExtra []string + if strings.Contains(fc, "gfortran") { + // TODO: This duplicates but also diverges from logic from cmd/go + // itself. For example, cmd/go merely adds -lgfortran without the extra + // library path work. If this is what's necessary to run gfortran, we + // should reconcile the logic here and in cmd/go.. Maybe this should + // become a cmd/go script test to share that logic. + + // Add -m32 if we're targeting 386, in case this is a cross-compile. + if runtime.GOARCH == "386" { + fcExtra = append(fcExtra, "-m32") + } + + // Find libgfortran. If the FORTRAN compiler isn't bundled + // with the C linker, this may be in a path the C linker can't + // find on its own. (See #14544) + libExt := "so" + switch runtime.GOOS { + case "darwin": + libExt = "dylib" + case "aix": + libExt = "a" + } + libPath, err := exec.Command(fc, append([]string{"-print-file-name=libgfortran." + libExt}, fcExtra...)...).CombinedOutput() + if err != nil { + t.Errorf("error invoking %s: %s", fc, err) + } + libDir := filepath.Dir(string(libPath)) + cgoLDFlags := os.Getenv("CGO_LDFLAGS") + cgoLDFlags += " -L " + libDir + if runtime.GOOS != "aix" { + cgoLDFlags += " -Wl,-rpath," + libDir + } + t.Logf("CGO_LDFLAGS=%s", cgoLDFlags) + os.Setenv("CGO_LDFLAGS", cgoLDFlags) + + } + + // Do a test build that doesn't involve Go FORTRAN support. + fcArgs := append([]string{"helloworld/helloworld.f90", "-o", "/dev/null"}, fcExtra...) + t.Logf("%s %s", fc, fcArgs) + if err := exec.Command(fc, fcArgs...).Run(); err != nil { + t.Skipf("skipping Fortran test: could not build helloworld.f90 with %s: %s", fc, err) + } + + // Finally, run the actual test. + t.Log("go", "run", "./testdata/testprog") + out, err := exec.Command("go", "run", "./testdata/testprog").CombinedOutput() + if err == nil && string(out) != "ok\n" { + err = fmt.Errorf("expected ok") + } + if err != nil { + t.Errorf("%s\nOutput:\n%s", err, string(out)) + } +} diff --git a/misc/cgo/fortran/helloworld/helloworld.f90 b/misc/cgo/fortran/helloworld/helloworld.f90 new file mode 100644 index 0000000..cbc34c1 --- /dev/null +++ b/misc/cgo/fortran/helloworld/helloworld.f90 @@ -0,0 +1,3 @@ + program HelloWorldF90 + write(*,*) "Hello World!" + end program HelloWorldF90 diff --git a/misc/cgo/fortran/testdata/testprog/answer.f90 b/misc/cgo/fortran/testdata/testprog/answer.f90 new file mode 100644 index 0000000..b3717ee --- /dev/null +++ b/misc/cgo/fortran/testdata/testprog/answer.f90 @@ -0,0 +1,9 @@ +! Copyright 2016 The Go Authors. All rights reserved. +! Use of this source code is governed by a BSD-style +! license that can be found in the LICENSE file. + +function the_answer() result(j) bind(C) + use iso_c_binding, only: c_int + integer(c_int) :: j ! output + j = 42 +end function the_answer diff --git a/misc/cgo/fortran/testdata/testprog/fortran.go b/misc/cgo/fortran/testdata/testprog/fortran.go new file mode 100644 index 0000000..d8004ce --- /dev/null +++ b/misc/cgo/fortran/testdata/testprog/fortran.go @@ -0,0 +1,21 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// int the_answer(); +import "C" +import "os" + +func TheAnswer() int { + return int(C.the_answer()) +} + +func main() { + if a := TheAnswer(); a != 42 { + println("Unexpected result for The Answer. Got:", a, " Want: 42") + os.Exit(1) + } + println("ok") +} diff --git a/misc/cgo/gmp/fib.go b/misc/cgo/gmp/fib.go new file mode 100644 index 0000000..f453fcf --- /dev/null +++ b/misc/cgo/gmp/fib.go @@ -0,0 +1,46 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore +// +build ignore + +// Compute Fibonacci numbers with two goroutines +// that pass integers back and forth. No actual +// concurrency, just threads and synchronization +// and foreign code on multiple pthreads. + +package main + +import ( + big "." + "runtime" +) + +func fibber(c chan *big.Int, out chan string, n int64) { + // Keep the fibbers in dedicated operating system + // threads, so that this program tests coordination + // between pthreads and not just goroutines. + runtime.LockOSThread() + + i := big.NewInt(n) + if n == 0 { + c <- i + } + for { + j := <-c + out <- j.String() + i.Add(i, j) + c <- i + } +} + +func main() { + c := make(chan *big.Int) + out := make(chan string) + go fibber(c, out, 0) + go fibber(c, out, 1) + for i := 0; i < 200; i++ { + println(<-out) + } +} diff --git a/misc/cgo/gmp/gmp.go b/misc/cgo/gmp/gmp.go new file mode 100644 index 0000000..0835fdc --- /dev/null +++ b/misc/cgo/gmp/gmp.go @@ -0,0 +1,379 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +An example of wrapping a C library in Go. This is the GNU +multiprecision library gmp's integer type mpz_t wrapped to look like +the Go package big's integer type Int. + +This is a syntactically valid Go program—it can be parsed with the Go +parser and processed by godoc—but it is not compiled directly by gc. +Instead, a separate tool, cgo, processes it to produce three output +files. The first two, 6g.go and 6c.c, are a Go source file for 6g and +a C source file for 6c; both compile as part of the named package +(gmp, in this example). The third, gcc.c, is a C source file for gcc; +it compiles into a shared object (.so) that is dynamically linked into +any 6.out that imports the first two files. + +The stanza + + // #include <gmp.h> + import "C" + +is a signal to cgo. The doc comment on the import of "C" provides +additional context for the C file. Here it is just a single #include +but it could contain arbitrary C definitions to be imported and used. + +Cgo recognizes any use of a qualified identifier C.xxx and uses gcc to +find the definition of xxx. If xxx is a type, cgo replaces C.xxx with +a Go translation. C arithmetic types translate to precisely-sized Go +arithmetic types. A C struct translates to a Go struct, field by +field; unrepresentable fields are replaced with opaque byte arrays. A +C union translates into a struct containing the first union member and +perhaps additional padding. C arrays become Go arrays. C pointers +become Go pointers. C function pointers become Go's uintptr. +C void pointers become Go's unsafe.Pointer. + +For example, mpz_t is defined in <gmp.h> as: + + typedef unsigned long int mp_limb_t; + + typedef struct + { + int _mp_alloc; + int _mp_size; + mp_limb_t *_mp_d; + } __mpz_struct; + + typedef __mpz_struct mpz_t[1]; + +Cgo generates: + + type _C_int int32 + type _C_mp_limb_t uint64 + type _C___mpz_struct struct { + _mp_alloc _C_int; + _mp_size _C_int; + _mp_d *_C_mp_limb_t; + } + type _C_mpz_t [1]_C___mpz_struct + +and then replaces each occurrence of a type C.xxx with _C_xxx. + +If xxx is data, cgo arranges for C.xxx to refer to the C variable, +with the type translated as described above. To do this, cgo must +introduce a Go variable that points at the C variable (the linker can +be told to initialize this pointer). For example, if the gmp library +provided + + mpz_t zero; + +then cgo would rewrite a reference to C.zero by introducing + + var _C_zero *C.mpz_t + +and then replacing all instances of C.zero with (*_C_zero). + +Cgo's most interesting translation is for functions. If xxx is a C +function, then cgo rewrites C.xxx into a new function _C_xxx that +calls the C xxx in a standard pthread. The new function translates +its arguments, calls xxx, and translates the return value. + +Translation of parameters and the return value follows the type +translation above except that arrays passed as parameters translate +explicitly in Go to pointers to arrays, as they do (implicitly) in C. + +Garbage collection is the big problem. It is fine for the Go world to +have pointers into the C world and to free those pointers when they +are no longer needed. To help, the Go code can define Go objects +holding the C pointers and use runtime.SetFinalizer on those Go objects. + +It is much more difficult for the C world to have pointers into the Go +world, because the Go garbage collector is unaware of the memory +allocated by C. The most important consideration is not to +constrain future implementations, so the rule is that Go code can +hand a Go pointer to C code but must separately arrange for +Go to hang on to a reference to the pointer until C is done with it. +*/ +package gmp + +/* +#cgo LDFLAGS: -lgmp +#include <gmp.h> +#include <stdlib.h> + +// gmp 5.0.0+ changed the type of the 3rd argument to mp_bitcnt_t, +// so, to support older versions, we wrap these two functions. +void _mpz_mul_2exp(mpz_ptr a, mpz_ptr b, unsigned long n) { + mpz_mul_2exp(a, b, n); +} +void _mpz_div_2exp(mpz_ptr a, mpz_ptr b, unsigned long n) { + mpz_div_2exp(a, b, n); +} +*/ +import "C" + +import ( + "os" + "unsafe" +) + +/* + * one of a kind + */ + +// An Int represents a signed multi-precision integer. +// The zero value for an Int represents the value 0. +type Int struct { + i C.mpz_t + init bool +} + +// NewInt returns a new Int initialized to x. +func NewInt(x int64) *Int { return new(Int).SetInt64(x) } + +// Int promises that the zero value is a 0, but in gmp +// the zero value is a crash. To bridge the gap, the +// init bool says whether this is a valid gmp value. +// doinit initializes z.i if it needs it. This is not inherent +// to FFI, just a mismatch between Go's convention of +// making zero values useful and gmp's decision not to. +func (z *Int) doinit() { + if z.init { + return + } + z.init = true + C.mpz_init(&z.i[0]) +} + +// Bytes returns z's representation as a big-endian byte array. +func (z *Int) Bytes() []byte { + b := make([]byte, (z.Len()+7)/8) + n := C.size_t(len(b)) + C.mpz_export(unsafe.Pointer(&b[0]), &n, 1, 1, 1, 0, &z.i[0]) + return b[0:n] +} + +// Len returns the length of z in bits. 0 is considered to have length 1. +func (z *Int) Len() int { + z.doinit() + return int(C.mpz_sizeinbase(&z.i[0], 2)) +} + +// Set sets z = x and returns z. +func (z *Int) Set(x *Int) *Int { + z.doinit() + C.mpz_set(&z.i[0], &x.i[0]) + return z +} + +// SetBytes interprets b as the bytes of a big-endian integer +// and sets z to that value. +func (z *Int) SetBytes(b []byte) *Int { + z.doinit() + if len(b) == 0 { + z.SetInt64(0) + } else { + C.mpz_import(&z.i[0], C.size_t(len(b)), 1, 1, 1, 0, unsafe.Pointer(&b[0])) + } + return z +} + +// SetInt64 sets z = x and returns z. +func (z *Int) SetInt64(x int64) *Int { + z.doinit() + // TODO(rsc): more work on 32-bit platforms + C.mpz_set_si(&z.i[0], C.long(x)) + return z +} + +// SetString interprets s as a number in the given base +// and sets z to that value. The base must be in the range [2,36]. +// SetString returns an error if s cannot be parsed or the base is invalid. +func (z *Int) SetString(s string, base int) error { + z.doinit() + if base < 2 || base > 36 { + return os.ErrInvalid + } + p := C.CString(s) + defer C.free(unsafe.Pointer(p)) + if C.mpz_set_str(&z.i[0], p, C.int(base)) < 0 { + return os.ErrInvalid + } + return nil +} + +// String returns the decimal representation of z. +func (z *Int) String() string { + if z == nil { + return "nil" + } + z.doinit() + p := C.mpz_get_str(nil, 10, &z.i[0]) + s := C.GoString(p) + C.free(unsafe.Pointer(p)) + return s +} + +func (z *Int) destroy() { + if z.init { + C.mpz_clear(&z.i[0]) + } + z.init = false +} + +/* + * arithmetic + */ + +// Add sets z = x + y and returns z. +func (z *Int) Add(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_add(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Sub sets z = x - y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_sub(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Mul sets z = x * y and returns z. +func (z *Int) Mul(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_mul(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Div sets z = x / y, rounding toward zero, and returns z. +func (z *Int) Div(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_tdiv_q(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Mod sets z = x % y and returns z. +// Like the result of the Go % operator, z has the same sign as x. +func (z *Int) Mod(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_tdiv_r(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Lsh sets z = x << s and returns z. +func (z *Int) Lsh(x *Int, s uint) *Int { + x.doinit() + z.doinit() + C._mpz_mul_2exp(&z.i[0], &x.i[0], C.ulong(s)) + return z +} + +// Rsh sets z = x >> s and returns z. +func (z *Int) Rsh(x *Int, s uint) *Int { + x.doinit() + z.doinit() + C._mpz_div_2exp(&z.i[0], &x.i[0], C.ulong(s)) + return z +} + +// Exp sets z = x^y % m and returns z. +// If m == nil, Exp sets z = x^y. +func (z *Int) Exp(x, y, m *Int) *Int { + m.doinit() + x.doinit() + y.doinit() + z.doinit() + if m == nil { + C.mpz_pow_ui(&z.i[0], &x.i[0], C.mpz_get_ui(&y.i[0])) + } else { + C.mpz_powm(&z.i[0], &x.i[0], &y.i[0], &m.i[0]) + } + return z +} + +func (z *Int) Int64() int64 { + if !z.init { + return 0 + } + return int64(C.mpz_get_si(&z.i[0])) +} + +// Neg sets z = -x and returns z. +func (z *Int) Neg(x *Int) *Int { + x.doinit() + z.doinit() + C.mpz_neg(&z.i[0], &x.i[0]) + return z +} + +// Abs sets z to the absolute value of x and returns z. +func (z *Int) Abs(x *Int) *Int { + x.doinit() + z.doinit() + C.mpz_abs(&z.i[0], &x.i[0]) + return z +} + +/* + * functions without a clear receiver + */ + +// CmpInt compares x and y. The result is +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +func CmpInt(x, y *Int) int { + x.doinit() + y.doinit() + switch cmp := C.mpz_cmp(&x.i[0], &y.i[0]); { + case cmp < 0: + return -1 + case cmp == 0: + return 0 + } + return +1 +} + +// DivModInt sets q = x / y and r = x % y. +func DivModInt(q, r, x, y *Int) { + q.doinit() + r.doinit() + x.doinit() + y.doinit() + C.mpz_tdiv_qr(&q.i[0], &r.i[0], &x.i[0], &y.i[0]) +} + +// GcdInt sets d to the greatest common divisor of a and b, +// which must be positive numbers. +// If x and y are not nil, GcdInt sets x and y such that d = a*x + b*y. +// If either a or b is not positive, GcdInt sets d = x = y = 0. +func GcdInt(d, x, y, a, b *Int) { + d.doinit() + x.doinit() + y.doinit() + a.doinit() + b.doinit() + C.mpz_gcdext(&d.i[0], &x.i[0], &y.i[0], &a.i[0], &b.i[0]) +} + +// ProbablyPrime performs n Miller-Rabin tests to check whether z is prime. +// If it returns true, z is prime with probability 1 - 1/4^n. +// If it returns false, z is not prime. +func (z *Int) ProbablyPrime(n int) bool { + z.doinit() + return int(C.mpz_probab_prime_p(&z.i[0], C.int(n))) > 0 +} diff --git a/misc/cgo/gmp/pi.go b/misc/cgo/gmp/pi.go new file mode 100644 index 0000000..5ea0349 --- /dev/null +++ b/misc/cgo/gmp/pi.go @@ -0,0 +1,74 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore +// +build ignore + +package main + +import ( + big "." + "fmt" + "runtime" +) + +var ( + tmp1 = big.NewInt(0) + tmp2 = big.NewInt(0) + numer = big.NewInt(1) + accum = big.NewInt(0) + denom = big.NewInt(1) + ten = big.NewInt(10) +) + +func extractDigit() int64 { + if big.CmpInt(numer, accum) > 0 { + return -1 + } + tmp1.Lsh(numer, 1).Add(tmp1, numer).Add(tmp1, accum) + big.DivModInt(tmp1, tmp2, tmp1, denom) + tmp2.Add(tmp2, numer) + if big.CmpInt(tmp2, denom) >= 0 { + return -1 + } + return tmp1.Int64() +} + +func nextTerm(k int64) { + y2 := k*2 + 1 + accum.Add(accum, tmp1.Lsh(numer, 1)) + accum.Mul(accum, tmp1.SetInt64(y2)) + numer.Mul(numer, tmp1.SetInt64(k)) + denom.Mul(denom, tmp1.SetInt64(y2)) +} + +func eliminateDigit(d int64) { + accum.Sub(accum, tmp1.Mul(denom, tmp1.SetInt64(d))) + accum.Mul(accum, ten) + numer.Mul(numer, ten) +} + +func main() { + i := 0 + k := int64(0) + for { + d := int64(-1) + for d < 0 { + k++ + nextTerm(k) + d = extractDigit() + } + eliminateDigit(d) + fmt.Printf("%c", d+'0') + + if i++; i%50 == 0 { + fmt.Printf("\n") + if i >= 1000 { + break + } + } + } + + fmt.Printf("\n%d calls; bit sizes: %d %d %d\n", runtime.NumCgoCall(), numer.Len(), accum.Len(), denom.Len()) +} diff --git a/misc/cgo/life/life_test.go b/misc/cgo/life/life_test.go new file mode 100644 index 0000000..0becb26 --- /dev/null +++ b/misc/cgo/life/life_test.go @@ -0,0 +1,63 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package life_test + +import ( + "bytes" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +func TestMain(m *testing.M) { + log.SetFlags(log.Lshortfile) + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { + GOPATH, err := os.MkdirTemp("", "cgolife") + if err != nil { + log.Panic(err) + } + defer os.RemoveAll(GOPATH) + os.Setenv("GOPATH", GOPATH) + + // Copy testdata into GOPATH/src/cgolife, along with a go.mod file + // declaring the same path. + modRoot := filepath.Join(GOPATH, "src", "cgolife") + if err := overlayDir(modRoot, "testdata"); err != nil { + log.Panic(err) + } + if err := os.Chdir(modRoot); err != nil { + log.Panic(err) + } + os.Setenv("PWD", modRoot) + if err := os.WriteFile("go.mod", []byte("module cgolife\n"), 0666); err != nil { + log.Panic(err) + } + + return m.Run() +} + +func TestTestRun(t *testing.T) { + if os.Getenv("GOOS") == "android" { + t.Skip("the go tool runs with CGO_ENABLED=0 on the android device") + } + out, err := exec.Command("go", "env", "GOROOT").Output() + if err != nil { + t.Fatal(err) + } + GOROOT := string(bytes.TrimSpace(out)) + + cmd := exec.Command("go", "run", filepath.Join(GOROOT, "test", "run.go"), "-", ".") + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) +} diff --git a/misc/cgo/life/overlaydir_test.go b/misc/cgo/life/overlaydir_test.go new file mode 100644 index 0000000..034c836 --- /dev/null +++ b/misc/cgo/life/overlaydir_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package life_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/life/testdata/c-life.c b/misc/cgo/life/testdata/c-life.c new file mode 100644 index 0000000..f853163 --- /dev/null +++ b/misc/cgo/life/testdata/c-life.c @@ -0,0 +1,56 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <assert.h> +#include "life.h" +#include "_cgo_export.h" + +const int MYCONST = 0; + +// Do the actual manipulation of the life board in C. This could be +// done easily in Go, we are just using C for demonstration +// purposes. +void +Step(int x, int y, int *a, int *n) +{ + struct GoStart_return r; + + // Use Go to start 4 goroutines each of which handles 1/4 of the + // board. + r = GoStart(0, x, y, 0, x / 2, 0, y / 2, a, n); + assert(r.r0 == 0 && r.r1 == 100); // test multiple returns + r = GoStart(1, x, y, x / 2, x, 0, y / 2, a, n); + assert(r.r0 == 1 && r.r1 == 101); // test multiple returns + GoStart(2, x, y, 0, x / 2, y / 2, y, a, n); + GoStart(3, x, y, x / 2, x, y / 2, y, a, n); + GoWait(0); + GoWait(1); + GoWait(2); + GoWait(3); +} + +// The actual computation. This is called in parallel. +void +DoStep(int xdim, int ydim, int xstart, int xend, int ystart, int yend, int *a, int *n) +{ + int x, y, c, i, j; + + for(x = xstart; x < xend; x++) { + for(y = ystart; y < yend; y++) { + c = 0; + for(i = -1; i <= 1; i++) { + for(j = -1; j <= 1; j++) { + if(x+i >= 0 && x+i < xdim && + y+j >= 0 && y+j < ydim && + (i != 0 || j != 0)) + c += a[(x+i)*xdim + (y+j)] != 0; + } + } + if(c == 3 || (c == 2 && a[x*xdim + y] != 0)) + n[x*xdim + y] = 1; + else + n[x*xdim + y] = 0; + } + } +} diff --git a/misc/cgo/life/testdata/life.go b/misc/cgo/life/testdata/life.go new file mode 100644 index 0000000..2e0af81 --- /dev/null +++ b/misc/cgo/life/testdata/life.go @@ -0,0 +1,41 @@ +// skip + +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgolife + +// #include "life.h" +import "C" + +import "unsafe" + +func Run(gen, x, y int, a []int32) { + n := make([]int32, x*y) + for i := 0; i < gen; i++ { + C.Step(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&a[0])), (*C.int)(unsafe.Pointer(&n[0]))) + copy(a, n) + } +} + +// Keep the channels visible from Go. +var chans [4]chan bool + +//export GoStart +// Double return value is just for testing. +func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) { + c := make(chan bool, int(C.MYCONST)) + go func() { + C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n) + c <- true + }() + chans[i] = c + return int(i), int(i + 100) +} + +//export GoWait +func GoWait(i C.int) { + <-chans[i] + chans[i] = nil +} diff --git a/misc/cgo/life/testdata/life.h b/misc/cgo/life/testdata/life.h new file mode 100644 index 0000000..11d2b97 --- /dev/null +++ b/misc/cgo/life/testdata/life.h @@ -0,0 +1,7 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +extern void Step(int, int, int *, int *); +extern void DoStep(int, int, int, int, int, int, int *, int *); +extern const int MYCONST; diff --git a/misc/cgo/life/testdata/main.go b/misc/cgo/life/testdata/main.go new file mode 100644 index 0000000..cc2ca7c --- /dev/null +++ b/misc/cgo/life/testdata/main.go @@ -0,0 +1,49 @@ +// run -tags=use_go_run + +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build test_run + +// Run the game of life in C using Go for parallelization. + +package main + +import ( + "flag" + "fmt" + + "cgolife" +) + +const MAXDIM = 100 + +var dim = flag.Int("dim", 16, "board dimensions") +var gen = flag.Int("gen", 10, "generations") + +func main() { + flag.Parse() + + var a [MAXDIM * MAXDIM]int32 + for i := 2; i < *dim; i += 8 { + for j := 2; j < *dim-3; j += 8 { + for y := 0; y < 3; y++ { + a[i**dim+j+y] = 1 + } + } + } + + cgolife.Run(*gen, *dim, *dim, a[:]) + + for i := 0; i < *dim; i++ { + for j := 0; j < *dim; j++ { + if a[i**dim+j] == 0 { + fmt.Print(" ") + } else { + fmt.Print("X") + } + } + fmt.Print("\n") + } +} diff --git a/misc/cgo/life/testdata/main.out b/misc/cgo/life/testdata/main.out new file mode 100644 index 0000000..26fc9c6 --- /dev/null +++ b/misc/cgo/life/testdata/main.out @@ -0,0 +1,16 @@ + + + XXX XXX + + + + + + + + XXX XXX + + + + + diff --git a/misc/cgo/nocgo/nocgo.go b/misc/cgo/nocgo/nocgo.go new file mode 100644 index 0000000..00ae5e9 --- /dev/null +++ b/misc/cgo/nocgo/nocgo.go @@ -0,0 +1,22 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that -static works when not using cgo. This test is in +// misc/cgo to take advantage of the testing framework support for +// when -static is expected to work. + +package nocgo + +func NoCgo() int { + c := make(chan int) + + // The test is run with external linking, which means that + // goroutines will be created via the runtime/cgo package. + // Make sure that works. + go func() { + c <- 42 + }() + + return <-c +} diff --git a/misc/cgo/nocgo/nocgo_test.go b/misc/cgo/nocgo/nocgo_test.go new file mode 100644 index 0000000..45d247c --- /dev/null +++ b/misc/cgo/nocgo/nocgo_test.go @@ -0,0 +1,14 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package nocgo + +import "testing" + +func TestNop(t *testing.T) { + i := NoCgo() + if i != 42 { + t.Errorf("got %d, want %d", i, 42) + } +} diff --git a/misc/cgo/stdio/overlaydir_test.go b/misc/cgo/stdio/overlaydir_test.go new file mode 100644 index 0000000..027ebf1 --- /dev/null +++ b/misc/cgo/stdio/overlaydir_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package stdio_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/stdio/stdio_test.go b/misc/cgo/stdio/stdio_test.go new file mode 100644 index 0000000..675418f --- /dev/null +++ b/misc/cgo/stdio/stdio_test.go @@ -0,0 +1,63 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package stdio_test + +import ( + "bytes" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +func TestMain(m *testing.M) { + log.SetFlags(log.Lshortfile) + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { + GOPATH, err := os.MkdirTemp("", "cgostdio") + if err != nil { + log.Panic(err) + } + defer os.RemoveAll(GOPATH) + os.Setenv("GOPATH", GOPATH) + + // Copy testdata into GOPATH/src/cgostdio, along with a go.mod file + // declaring the same path. + modRoot := filepath.Join(GOPATH, "src", "cgostdio") + if err := overlayDir(modRoot, "testdata"); err != nil { + log.Panic(err) + } + if err := os.Chdir(modRoot); err != nil { + log.Panic(err) + } + os.Setenv("PWD", modRoot) + if err := os.WriteFile("go.mod", []byte("module cgostdio\n"), 0666); err != nil { + log.Panic(err) + } + + return m.Run() +} + +func TestTestRun(t *testing.T) { + if os.Getenv("GOOS") == "android" { + t.Skip("subpackage stdio is not available on android") + } + out, err := exec.Command("go", "env", "GOROOT").Output() + if err != nil { + t.Fatal(err) + } + GOROOT := string(bytes.TrimSpace(out)) + + cmd := exec.Command("go", "run", filepath.Join(GOROOT, "test", "run.go"), "-", ".") + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) +} diff --git a/misc/cgo/stdio/testdata/chain.go b/misc/cgo/stdio/testdata/chain.go new file mode 100644 index 0000000..6c3f406 --- /dev/null +++ b/misc/cgo/stdio/testdata/chain.go @@ -0,0 +1,48 @@ +// run -tags=use_go_run + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build test_run + +// Pass numbers along a chain of threads. + +package main + +import ( + "runtime" + "strconv" + + "cgostdio/stdio" +) + +const N = 10 +const R = 5 + +func link(left chan<- int, right <-chan int) { + // Keep the links in dedicated operating system + // threads, so that this program tests coordination + // between pthreads and not just goroutines. + runtime.LockOSThread() + for { + v := <-right + stdio.Stdout.WriteString(strconv.Itoa(v) + "\n") + left <- 1 + v + } +} + +func main() { + leftmost := make(chan int) + var left chan int + right := leftmost + for i := 0; i < N; i++ { + left, right = right, make(chan int) + go link(left, right) + } + for i := 0; i < R; i++ { + right <- 0 + x := <-leftmost + stdio.Stdout.WriteString(strconv.Itoa(x) + "\n") + } +} diff --git a/misc/cgo/stdio/testdata/chain.out b/misc/cgo/stdio/testdata/chain.out new file mode 100644 index 0000000..963cf9b --- /dev/null +++ b/misc/cgo/stdio/testdata/chain.out @@ -0,0 +1,55 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/misc/cgo/stdio/testdata/fib.go b/misc/cgo/stdio/testdata/fib.go new file mode 100644 index 0000000..49cb0ea --- /dev/null +++ b/misc/cgo/stdio/testdata/fib.go @@ -0,0 +1,52 @@ +// run -tags=use_go_run + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build test_run + +// Compute Fibonacci numbers with two goroutines +// that pass integers back and forth. No actual +// concurrency, just threads and synchronization +// and foreign code on multiple pthreads. + +package main + +import ( + "runtime" + "strconv" + + "cgostdio/stdio" +) + +func fibber(c, out chan int64, i int64) { + // Keep the fibbers in dedicated operating system + // threads, so that this program tests coordination + // between pthreads and not just goroutines. + runtime.LockOSThread() + + if i == 0 { + c <- i + } + for { + j := <-c + stdio.Stdout.WriteString(strconv.FormatInt(j, 10) + "\n") + out <- j + <-out + i += j + c <- i + } +} + +func main() { + c := make(chan int64) + out := make(chan int64) + go fibber(c, out, 0) + go fibber(c, out, 1) + <-out + for i := 0; i < 90; i++ { + out <- 1 + <-out + } +} diff --git a/misc/cgo/stdio/testdata/fib.out b/misc/cgo/stdio/testdata/fib.out new file mode 100644 index 0000000..17ff503 --- /dev/null +++ b/misc/cgo/stdio/testdata/fib.out @@ -0,0 +1,91 @@ +0 +1 +1 +2 +3 +5 +8 +13 +21 +34 +55 +89 +144 +233 +377 +610 +987 +1597 +2584 +4181 +6765 +10946 +17711 +28657 +46368 +75025 +121393 +196418 +317811 +514229 +832040 +1346269 +2178309 +3524578 +5702887 +9227465 +14930352 +24157817 +39088169 +63245986 +102334155 +165580141 +267914296 +433494437 +701408733 +1134903170 +1836311903 +2971215073 +4807526976 +7778742049 +12586269025 +20365011074 +32951280099 +53316291173 +86267571272 +139583862445 +225851433717 +365435296162 +591286729879 +956722026041 +1548008755920 +2504730781961 +4052739537881 +6557470319842 +10610209857723 +17167680177565 +27777890035288 +44945570212853 +72723460248141 +117669030460994 +190392490709135 +308061521170129 +498454011879264 +806515533049393 +1304969544928657 +2111485077978050 +3416454622906707 +5527939700884757 +8944394323791464 +14472334024676221 +23416728348467685 +37889062373143906 +61305790721611591 +99194853094755497 +160500643816367088 +259695496911122585 +420196140727489673 +679891637638612258 +1100087778366101931 +1779979416004714189 +2880067194370816120 diff --git a/misc/cgo/stdio/testdata/hello.go b/misc/cgo/stdio/testdata/hello.go new file mode 100644 index 0000000..046bfee --- /dev/null +++ b/misc/cgo/stdio/testdata/hello.go @@ -0,0 +1,15 @@ +// run -tags=use_go_run + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build test_run + +package main + +import "cgostdio/stdio" + +func main() { + stdio.Stdout.WriteString(stdio.Greeting + "\n") +} diff --git a/misc/cgo/stdio/testdata/hello.out b/misc/cgo/stdio/testdata/hello.out new file mode 100644 index 0000000..4b5fa63 --- /dev/null +++ b/misc/cgo/stdio/testdata/hello.out @@ -0,0 +1 @@ +hello, world diff --git a/misc/cgo/stdio/testdata/run.out b/misc/cgo/stdio/testdata/run.out new file mode 100644 index 0000000..c0e4965 --- /dev/null +++ b/misc/cgo/stdio/testdata/run.out @@ -0,0 +1,150 @@ +* hello +hello, world +* fib +0 +1 +1 +2 +3 +5 +8 +13 +21 +34 +55 +89 +144 +233 +377 +610 +987 +1597 +2584 +4181 +6765 +10946 +17711 +28657 +46368 +75025 +121393 +196418 +317811 +514229 +832040 +1346269 +2178309 +3524578 +5702887 +9227465 +14930352 +24157817 +39088169 +63245986 +102334155 +165580141 +267914296 +433494437 +701408733 +1134903170 +1836311903 +2971215073 +4807526976 +7778742049 +12586269025 +20365011074 +32951280099 +53316291173 +86267571272 +139583862445 +225851433717 +365435296162 +591286729879 +956722026041 +1548008755920 +2504730781961 +4052739537881 +6557470319842 +10610209857723 +17167680177565 +27777890035288 +44945570212853 +72723460248141 +117669030460994 +190392490709135 +308061521170129 +498454011879264 +806515533049393 +1304969544928657 +2111485077978050 +3416454622906707 +5527939700884757 +8944394323791464 +14472334024676221 +23416728348467685 +37889062373143906 +61305790721611591 +99194853094755497 +160500643816367088 +259695496911122585 +420196140727489673 +679891637638612258 +1100087778366101931 +1779979416004714189 +2880067194370816120 +* chain +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/misc/cgo/stdio/testdata/stdio/file.go b/misc/cgo/stdio/testdata/stdio/file.go new file mode 100644 index 0000000..a024f2c --- /dev/null +++ b/misc/cgo/stdio/testdata/stdio/file.go @@ -0,0 +1,44 @@ +// skip + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +A trivial example of wrapping a C library in Go. +For a more complex example and explanation, +see ../gmp/gmp.go. +*/ + +package stdio + +/* +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <errno.h> + +char* greeting = "hello, world"; +*/ +import "C" +import "unsafe" + +type File C.FILE + +// Test reference to library symbol. +// Stdout and stderr are too special to be a reliable test. +//var = C.environ + +func (f *File) WriteString(s string) { + p := C.CString(s) + C.fputs(p, (*C.FILE)(f)) + C.free(unsafe.Pointer(p)) + f.Flush() +} + +func (f *File) Flush() { + C.fflush((*C.FILE)(f)) +} + +var Greeting = C.GoString(C.greeting) +var Gbytes = C.GoBytes(unsafe.Pointer(C.greeting), C.int(len(Greeting))) diff --git a/misc/cgo/stdio/testdata/stdio/stdio.go b/misc/cgo/stdio/testdata/stdio/stdio.go new file mode 100644 index 0000000..d216e44 --- /dev/null +++ b/misc/cgo/stdio/testdata/stdio/stdio.go @@ -0,0 +1,22 @@ +// skip + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package stdio + +/* +#include <stdio.h> + +// on mingw, stderr and stdout are defined as &_iob[FILENO] +// on netbsd, they are defined as &__sF[FILENO] +// and cgo doesn't recognize them, so write a function to get them, +// instead of depending on internals of libc implementation. +FILE *getStdout(void) { return stdout; } +FILE *getStderr(void) { return stderr; } +*/ +import "C" + +var Stdout = (*File)(C.getStdout()) +var Stderr = (*File)(C.getStderr()) diff --git a/misc/cgo/test/backdoor.go b/misc/cgo/test/backdoor.go new file mode 100644 index 0000000..6fb33d6 --- /dev/null +++ b/misc/cgo/test/backdoor.go @@ -0,0 +1,11 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import _ "unsafe" + +//go:linkname lockedOSThread runtime.lockedOSThread +//extern runtime_lockedOSThread +func lockedOSThread() bool diff --git a/misc/cgo/test/buildid_linux.go b/misc/cgo/test/buildid_linux.go new file mode 100644 index 0000000..84d3edb --- /dev/null +++ b/misc/cgo/test/buildid_linux.go @@ -0,0 +1,78 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +// Test that we have no more than one build ID. In the past we used +// to generate a separate build ID for each package using cgo, and the +// linker concatenated them all. We don't want that--we only want +// one. + +import ( + "bytes" + "debug/elf" + "os" + "testing" +) + +func testBuildID(t *testing.T) { + f, err := elf.Open("/proc/self/exe") + if err != nil { + if os.IsNotExist(err) { + t.Skip("no /proc/self/exe") + } + t.Fatal("opening /proc/self/exe: ", err) + } + defer f.Close() + + c := 0 +sections: + for i, s := range f.Sections { + if s.Type != elf.SHT_NOTE { + continue + } + + d, err := s.Data() + if err != nil { + t.Logf("reading data of note section %d: %v", i, err) + continue + } + + for len(d) > 0 { + + // ELF standards differ as to the sizes in + // note sections. Both the GNU linker and + // gold always generate 32-bit sizes, so that + // is what we assume here. + + if len(d) < 12 { + t.Logf("note section %d too short (%d < 12)", i, len(d)) + continue sections + } + + namesz := f.ByteOrder.Uint32(d) + descsz := f.ByteOrder.Uint32(d[4:]) + typ := f.ByteOrder.Uint32(d[8:]) + + an := (namesz + 3) &^ 3 + ad := (descsz + 3) &^ 3 + + if int(12+an+ad) > len(d) { + t.Logf("note section %d too short for header (%d < 12 + align(%d,4) + align(%d,4))", i, len(d), namesz, descsz) + continue sections + } + + // 3 == NT_GNU_BUILD_ID + if typ == 3 && namesz == 4 && bytes.Equal(d[12:16], []byte("GNU\000")) { + c++ + } + + d = d[12+an+ad:] + } + } + + if c > 1 { + t.Errorf("found %d build ID notes", c) + } +} diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go new file mode 100644 index 0000000..08dd9b3 --- /dev/null +++ b/misc/cgo/test/callback.go @@ -0,0 +1,1782 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +/* +void callback(void *f); +void callGoFoo(void); +void callGoStackCheck(void); +void callPanic(void); +int callGoReturnVal(void); +int returnAfterGrow(void); +int returnAfterGrowFromGo(void); +void callGoWithString(void); +*/ +import "C" + +import ( + "path" + "runtime" + "strings" + "sync" + "testing" + "unsafe" +) + +// Pass a func value from nestedCall to goCallback using an integer token. +var callbackMutex sync.Mutex +var callbackToken int +var callbackFuncs = make(map[int]func()) + +// nestedCall calls into C, back into Go, and finally to f. +func nestedCall(f func()) { + // callback(x) calls goCallback(x) + callbackMutex.Lock() + callbackToken++ + i := callbackToken + callbackFuncs[i] = f + callbackMutex.Unlock() + + // Pass the address of i because the C function was written to + // take a pointer. We could pass an int if we felt like + // rewriting the C code. + C.callback(unsafe.Pointer(&i)) + + callbackMutex.Lock() + delete(callbackFuncs, i) + callbackMutex.Unlock() +} + +//export goCallback +func goCallback(p unsafe.Pointer) { + i := *(*int)(p) + + callbackMutex.Lock() + f := callbackFuncs[i] + callbackMutex.Unlock() + + if f == nil { + panic("missing callback function") + } + f() +} + +func testCallback(t *testing.T) { + var x = false + nestedCall(func() { x = true }) + if !x { + t.Fatal("nestedCall did not call func") + } +} + +func testCallbackGC(t *testing.T) { + nestedCall(runtime.GC) +} + +func testCallbackPanic(t *testing.T) { + // Make sure panic during callback unwinds properly. + if lockedOSThread() { + t.Fatal("locked OS thread on entry to TestCallbackPanic") + } + defer func() { + s := recover() + if s == nil { + t.Fatal("did not panic") + } + if s.(string) != "callback panic" { + t.Fatal("wrong panic:", s) + } + if lockedOSThread() { + t.Fatal("locked OS thread on exit from TestCallbackPanic") + } + }() + nestedCall(func() { panic("callback panic") }) + panic("nestedCall returned") +} + +func testCallbackPanicLoop(t *testing.T) { + // Make sure we don't blow out m->g0 stack. + for i := 0; i < 100000; i++ { + testCallbackPanic(t) + } +} + +func testCallbackPanicLocked(t *testing.T) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if !lockedOSThread() { + t.Fatal("runtime.LockOSThread didn't") + } + defer func() { + s := recover() + if s == nil { + t.Fatal("did not panic") + } + if s.(string) != "callback panic" { + t.Fatal("wrong panic:", s) + } + if !lockedOSThread() { + t.Fatal("lost lock on OS thread after panic") + } + }() + nestedCall(func() { panic("callback panic") }) + panic("nestedCall returned") +} + +// Callback with zero arguments used to make the stack misaligned, +// which broke the garbage collector and other things. +func testZeroArgCallback(t *testing.T) { + defer func() { + s := recover() + if s != nil { + t.Fatal("panic during callback:", s) + } + }() + C.callGoFoo() +} + +//export goFoo +func goFoo() { + x := 1 + for i := 0; i < 10000; i++ { + // variadic call mallocs + writes to + variadic(x, x, x) + if x != 1 { + panic("bad x") + } + } +} + +func variadic(x ...interface{}) {} + +func testBlocking(t *testing.T) { + c := make(chan int) + go func() { + for i := 0; i < 10; i++ { + c <- <-c + } + }() + nestedCall(func() { + for i := 0; i < 10; i++ { + c <- i + if j := <-c; j != i { + t.Errorf("out of sync %d != %d", j, i) + } + } + }) +} + +// Test that the stack can be unwound through a call out and call back +// into Go. +func testCallbackCallers(t *testing.T) { + if runtime.Compiler != "gc" { + // The exact function names are not going to be the same. + t.Skip("skipping for non-gc toolchain") + } + pc := make([]uintptr, 100) + n := 0 + name := []string{ + "runtime.cgocallbackg1", + "runtime.cgocallbackg", + "runtime.cgocallback", + "runtime.systemstack_switch", + "runtime.cgocall", + "test._Cfunc_callback", + "test.nestedCall.func1", + "test.nestedCall", + "test.testCallbackCallers", + "test.TestCallbackCallers", + "testing.tRunner", + "runtime.goexit", + } + nestedCall(func() { + n = runtime.Callers(4, pc) + }) + if n != len(name) { + t.Errorf("expected %d frames, got %d", len(name), n) + } + for i := 0; i < n; i++ { + f := runtime.FuncForPC(pc[i] - 1) // TODO: use runtime.CallersFrames + if f == nil { + t.Fatalf("expected non-nil Func for pc %d", pc[i]) + } + fname := f.Name() + // Remove the prepended pathname from automatically + // generated cgo function names. + if strings.HasPrefix(fname, "_") { + fname = path.Base(f.Name()[1:]) + } + // In module mode, this package has a fully-qualified import path. + // Remove it if present. + fname = strings.TrimPrefix(fname, "misc/cgo/") + + namei := "" + if i < len(name) { + namei = name[i] + } + if fname != namei { + t.Errorf("stk[%d] = %q, want %q", i, fname, namei) + } + } +} + +func testPanicFromC(t *testing.T) { + defer func() { + r := recover() + if r == nil { + t.Fatal("did not panic") + } + if r.(string) != "panic from C" { + t.Fatal("wrong panic:", r) + } + }() + C.callPanic() +} + +// Test that C code can return a value if it calls a Go function that +// causes a stack copy. +func testReturnAfterGrow(t *testing.T) { + // Use a new goroutine so that we get a small stack. + c := make(chan int) + go func() { + c <- int(C.returnAfterGrow()) + }() + if got, want := <-c, 123456; got != want { + t.Errorf("got %d want %d", got, want) + } +} + +// Test that we can return a value from Go->C->Go if the Go code +// causes a stack copy. +func testReturnAfterGrowFromGo(t *testing.T) { + // Use a new goroutine so that we get a small stack. + c := make(chan int) + go func() { + c <- int(C.returnAfterGrowFromGo()) + }() + if got, want := <-c, 129*128/2; got != want { + t.Errorf("got %d want %d", got, want) + } +} + +//export goReturnVal +func goReturnVal() (r C.int) { + // Force a stack copy. + var f func(int) int + f = func(i int) int { + var buf [256]byte + use(buf[:]) + if i == 0 { + return 0 + } + return i + f(i-1) + } + r = C.int(f(128)) + return +} + +// Test that C can pass in a Go string from a string constant. +func testCallGoWithString(t *testing.T) { + C.callGoWithString() + want := "string passed from C to Go" + if stringFromGo != want { + t.Errorf("string passed through C is %s, want %s", stringFromGo, want) + } +} + +var stringFromGo string + +//export goWithString +func goWithString(s string) { + stringFromGo = s +} + +func testCallbackStack(t *testing.T) { + // Make cgo call and callback with different amount of stack available. + // We do not do any explicit checks, just ensure that it does not crash. + for _, f := range splitTests { + f() + } +} + +//export goStackCheck +func goStackCheck() { + // use some stack memory to trigger split stack check + var buf [256]byte + use(buf[:]) +} + +var Used byte + +func use(buf []byte) { + for _, c := range buf { + Used += c + } +} + +var splitTests = []func(){ + // Edit .+1,/^}/-1|seq 4 4 5000 | sed 's/.*/ stack&,/' | fmt + stack4, stack8, stack12, stack16, stack20, stack24, stack28, + stack32, stack36, stack40, stack44, stack48, stack52, stack56, + stack60, stack64, stack68, stack72, stack76, stack80, stack84, + stack88, stack92, stack96, stack100, stack104, stack108, stack112, + stack116, stack120, stack124, stack128, stack132, stack136, + stack140, stack144, stack148, stack152, stack156, stack160, + stack164, stack168, stack172, stack176, stack180, stack184, + stack188, stack192, stack196, stack200, stack204, stack208, + stack212, stack216, stack220, stack224, stack228, stack232, + stack236, stack240, stack244, stack248, stack252, stack256, + stack260, stack264, stack268, stack272, stack276, stack280, + stack284, stack288, stack292, stack296, stack300, stack304, + stack308, stack312, stack316, stack320, stack324, stack328, + stack332, stack336, stack340, stack344, stack348, stack352, + stack356, stack360, stack364, stack368, stack372, stack376, + stack380, stack384, stack388, stack392, stack396, stack400, + stack404, stack408, stack412, stack416, stack420, stack424, + stack428, stack432, stack436, stack440, stack444, stack448, + stack452, stack456, stack460, stack464, stack468, stack472, + stack476, stack480, stack484, stack488, stack492, stack496, + stack500, stack504, stack508, stack512, stack516, stack520, + stack524, stack528, stack532, stack536, stack540, stack544, + stack548, stack552, stack556, stack560, stack564, stack568, + stack572, stack576, stack580, stack584, stack588, stack592, + stack596, stack600, stack604, stack608, stack612, stack616, + stack620, stack624, stack628, stack632, stack636, stack640, + stack644, stack648, stack652, stack656, stack660, stack664, + stack668, stack672, stack676, stack680, stack684, stack688, + stack692, stack696, stack700, stack704, stack708, stack712, + stack716, stack720, stack724, stack728, stack732, stack736, + stack740, stack744, stack748, stack752, stack756, stack760, + stack764, stack768, stack772, stack776, stack780, stack784, + stack788, stack792, stack796, stack800, stack804, stack808, + stack812, stack816, stack820, stack824, stack828, stack832, + stack836, stack840, stack844, stack848, stack852, stack856, + stack860, stack864, stack868, stack872, stack876, stack880, + stack884, stack888, stack892, stack896, stack900, stack904, + stack908, stack912, stack916, stack920, stack924, stack928, + stack932, stack936, stack940, stack944, stack948, stack952, + stack956, stack960, stack964, stack968, stack972, stack976, + stack980, stack984, stack988, stack992, stack996, stack1000, + stack1004, stack1008, stack1012, stack1016, stack1020, stack1024, + stack1028, stack1032, stack1036, stack1040, stack1044, stack1048, + stack1052, stack1056, stack1060, stack1064, stack1068, stack1072, + stack1076, stack1080, stack1084, stack1088, stack1092, stack1096, + stack1100, stack1104, stack1108, stack1112, stack1116, stack1120, + stack1124, stack1128, stack1132, stack1136, stack1140, stack1144, + stack1148, stack1152, stack1156, stack1160, stack1164, stack1168, + stack1172, stack1176, stack1180, stack1184, stack1188, stack1192, + stack1196, stack1200, stack1204, stack1208, stack1212, stack1216, + stack1220, stack1224, stack1228, stack1232, stack1236, stack1240, + stack1244, stack1248, stack1252, stack1256, stack1260, stack1264, + stack1268, stack1272, stack1276, stack1280, stack1284, stack1288, + stack1292, stack1296, stack1300, stack1304, stack1308, stack1312, + stack1316, stack1320, stack1324, stack1328, stack1332, stack1336, + stack1340, stack1344, stack1348, stack1352, stack1356, stack1360, + stack1364, stack1368, stack1372, stack1376, stack1380, stack1384, + stack1388, stack1392, stack1396, stack1400, stack1404, stack1408, + stack1412, stack1416, stack1420, stack1424, stack1428, stack1432, + stack1436, stack1440, stack1444, stack1448, stack1452, stack1456, + stack1460, stack1464, stack1468, stack1472, stack1476, stack1480, + stack1484, stack1488, stack1492, stack1496, stack1500, stack1504, + stack1508, stack1512, stack1516, stack1520, stack1524, stack1528, + stack1532, stack1536, stack1540, stack1544, stack1548, stack1552, + stack1556, stack1560, stack1564, stack1568, stack1572, stack1576, + stack1580, stack1584, stack1588, stack1592, stack1596, stack1600, + stack1604, stack1608, stack1612, stack1616, stack1620, stack1624, + stack1628, stack1632, stack1636, stack1640, stack1644, stack1648, + stack1652, stack1656, stack1660, stack1664, stack1668, stack1672, + stack1676, stack1680, stack1684, stack1688, stack1692, stack1696, + stack1700, stack1704, stack1708, stack1712, stack1716, stack1720, + stack1724, stack1728, stack1732, stack1736, stack1740, stack1744, + stack1748, stack1752, stack1756, stack1760, stack1764, stack1768, + stack1772, stack1776, stack1780, stack1784, stack1788, stack1792, + stack1796, stack1800, stack1804, stack1808, stack1812, stack1816, + stack1820, stack1824, stack1828, stack1832, stack1836, stack1840, + stack1844, stack1848, stack1852, stack1856, stack1860, stack1864, + stack1868, stack1872, stack1876, stack1880, stack1884, stack1888, + stack1892, stack1896, stack1900, stack1904, stack1908, stack1912, + stack1916, stack1920, stack1924, stack1928, stack1932, stack1936, + stack1940, stack1944, stack1948, stack1952, stack1956, stack1960, + stack1964, stack1968, stack1972, stack1976, stack1980, stack1984, + stack1988, stack1992, stack1996, stack2000, stack2004, stack2008, + stack2012, stack2016, stack2020, stack2024, stack2028, stack2032, + stack2036, stack2040, stack2044, stack2048, stack2052, stack2056, + stack2060, stack2064, stack2068, stack2072, stack2076, stack2080, + stack2084, stack2088, stack2092, stack2096, stack2100, stack2104, + stack2108, stack2112, stack2116, stack2120, stack2124, stack2128, + stack2132, stack2136, stack2140, stack2144, stack2148, stack2152, + stack2156, stack2160, stack2164, stack2168, stack2172, stack2176, + stack2180, stack2184, stack2188, stack2192, stack2196, stack2200, + stack2204, stack2208, stack2212, stack2216, stack2220, stack2224, + stack2228, stack2232, stack2236, stack2240, stack2244, stack2248, + stack2252, stack2256, stack2260, stack2264, stack2268, stack2272, + stack2276, stack2280, stack2284, stack2288, stack2292, stack2296, + stack2300, stack2304, stack2308, stack2312, stack2316, stack2320, + stack2324, stack2328, stack2332, stack2336, stack2340, stack2344, + stack2348, stack2352, stack2356, stack2360, stack2364, stack2368, + stack2372, stack2376, stack2380, stack2384, stack2388, stack2392, + stack2396, stack2400, stack2404, stack2408, stack2412, stack2416, + stack2420, stack2424, stack2428, stack2432, stack2436, stack2440, + stack2444, stack2448, stack2452, stack2456, stack2460, stack2464, + stack2468, stack2472, stack2476, stack2480, stack2484, stack2488, + stack2492, stack2496, stack2500, stack2504, stack2508, stack2512, + stack2516, stack2520, stack2524, stack2528, stack2532, stack2536, + stack2540, stack2544, stack2548, stack2552, stack2556, stack2560, + stack2564, stack2568, stack2572, stack2576, stack2580, stack2584, + stack2588, stack2592, stack2596, stack2600, stack2604, stack2608, + stack2612, stack2616, stack2620, stack2624, stack2628, stack2632, + stack2636, stack2640, stack2644, stack2648, stack2652, stack2656, + stack2660, stack2664, stack2668, stack2672, stack2676, stack2680, + stack2684, stack2688, stack2692, stack2696, stack2700, stack2704, + stack2708, stack2712, stack2716, stack2720, stack2724, stack2728, + stack2732, stack2736, stack2740, stack2744, stack2748, stack2752, + stack2756, stack2760, stack2764, stack2768, stack2772, stack2776, + stack2780, stack2784, stack2788, stack2792, stack2796, stack2800, + stack2804, stack2808, stack2812, stack2816, stack2820, stack2824, + stack2828, stack2832, stack2836, stack2840, stack2844, stack2848, + stack2852, stack2856, stack2860, stack2864, stack2868, stack2872, + stack2876, stack2880, stack2884, stack2888, stack2892, stack2896, + stack2900, stack2904, stack2908, stack2912, stack2916, stack2920, + stack2924, stack2928, stack2932, stack2936, stack2940, stack2944, + stack2948, stack2952, stack2956, stack2960, stack2964, stack2968, + stack2972, stack2976, stack2980, stack2984, stack2988, stack2992, + stack2996, stack3000, stack3004, stack3008, stack3012, stack3016, + stack3020, stack3024, stack3028, stack3032, stack3036, stack3040, + stack3044, stack3048, stack3052, stack3056, stack3060, stack3064, + stack3068, stack3072, stack3076, stack3080, stack3084, stack3088, + stack3092, stack3096, stack3100, stack3104, stack3108, stack3112, + stack3116, stack3120, stack3124, stack3128, stack3132, stack3136, + stack3140, stack3144, stack3148, stack3152, stack3156, stack3160, + stack3164, stack3168, stack3172, stack3176, stack3180, stack3184, + stack3188, stack3192, stack3196, stack3200, stack3204, stack3208, + stack3212, stack3216, stack3220, stack3224, stack3228, stack3232, + stack3236, stack3240, stack3244, stack3248, stack3252, stack3256, + stack3260, stack3264, stack3268, stack3272, stack3276, stack3280, + stack3284, stack3288, stack3292, stack3296, stack3300, stack3304, + stack3308, stack3312, stack3316, stack3320, stack3324, stack3328, + stack3332, stack3336, stack3340, stack3344, stack3348, stack3352, + stack3356, stack3360, stack3364, stack3368, stack3372, stack3376, + stack3380, stack3384, stack3388, stack3392, stack3396, stack3400, + stack3404, stack3408, stack3412, stack3416, stack3420, stack3424, + stack3428, stack3432, stack3436, stack3440, stack3444, stack3448, + stack3452, stack3456, stack3460, stack3464, stack3468, stack3472, + stack3476, stack3480, stack3484, stack3488, stack3492, stack3496, + stack3500, stack3504, stack3508, stack3512, stack3516, stack3520, + stack3524, stack3528, stack3532, stack3536, stack3540, stack3544, + stack3548, stack3552, stack3556, stack3560, stack3564, stack3568, + stack3572, stack3576, stack3580, stack3584, stack3588, stack3592, + stack3596, stack3600, stack3604, stack3608, stack3612, stack3616, + stack3620, stack3624, stack3628, stack3632, stack3636, stack3640, + stack3644, stack3648, stack3652, stack3656, stack3660, stack3664, + stack3668, stack3672, stack3676, stack3680, stack3684, stack3688, + stack3692, stack3696, stack3700, stack3704, stack3708, stack3712, + stack3716, stack3720, stack3724, stack3728, stack3732, stack3736, + stack3740, stack3744, stack3748, stack3752, stack3756, stack3760, + stack3764, stack3768, stack3772, stack3776, stack3780, stack3784, + stack3788, stack3792, stack3796, stack3800, stack3804, stack3808, + stack3812, stack3816, stack3820, stack3824, stack3828, stack3832, + stack3836, stack3840, stack3844, stack3848, stack3852, stack3856, + stack3860, stack3864, stack3868, stack3872, stack3876, stack3880, + stack3884, stack3888, stack3892, stack3896, stack3900, stack3904, + stack3908, stack3912, stack3916, stack3920, stack3924, stack3928, + stack3932, stack3936, stack3940, stack3944, stack3948, stack3952, + stack3956, stack3960, stack3964, stack3968, stack3972, stack3976, + stack3980, stack3984, stack3988, stack3992, stack3996, stack4000, + stack4004, stack4008, stack4012, stack4016, stack4020, stack4024, + stack4028, stack4032, stack4036, stack4040, stack4044, stack4048, + stack4052, stack4056, stack4060, stack4064, stack4068, stack4072, + stack4076, stack4080, stack4084, stack4088, stack4092, stack4096, + stack4100, stack4104, stack4108, stack4112, stack4116, stack4120, + stack4124, stack4128, stack4132, stack4136, stack4140, stack4144, + stack4148, stack4152, stack4156, stack4160, stack4164, stack4168, + stack4172, stack4176, stack4180, stack4184, stack4188, stack4192, + stack4196, stack4200, stack4204, stack4208, stack4212, stack4216, + stack4220, stack4224, stack4228, stack4232, stack4236, stack4240, + stack4244, stack4248, stack4252, stack4256, stack4260, stack4264, + stack4268, stack4272, stack4276, stack4280, stack4284, stack4288, + stack4292, stack4296, stack4300, stack4304, stack4308, stack4312, + stack4316, stack4320, stack4324, stack4328, stack4332, stack4336, + stack4340, stack4344, stack4348, stack4352, stack4356, stack4360, + stack4364, stack4368, stack4372, stack4376, stack4380, stack4384, + stack4388, stack4392, stack4396, stack4400, stack4404, stack4408, + stack4412, stack4416, stack4420, stack4424, stack4428, stack4432, + stack4436, stack4440, stack4444, stack4448, stack4452, stack4456, + stack4460, stack4464, stack4468, stack4472, stack4476, stack4480, + stack4484, stack4488, stack4492, stack4496, stack4500, stack4504, + stack4508, stack4512, stack4516, stack4520, stack4524, stack4528, + stack4532, stack4536, stack4540, stack4544, stack4548, stack4552, + stack4556, stack4560, stack4564, stack4568, stack4572, stack4576, + stack4580, stack4584, stack4588, stack4592, stack4596, stack4600, + stack4604, stack4608, stack4612, stack4616, stack4620, stack4624, + stack4628, stack4632, stack4636, stack4640, stack4644, stack4648, + stack4652, stack4656, stack4660, stack4664, stack4668, stack4672, + stack4676, stack4680, stack4684, stack4688, stack4692, stack4696, + stack4700, stack4704, stack4708, stack4712, stack4716, stack4720, + stack4724, stack4728, stack4732, stack4736, stack4740, stack4744, + stack4748, stack4752, stack4756, stack4760, stack4764, stack4768, + stack4772, stack4776, stack4780, stack4784, stack4788, stack4792, + stack4796, stack4800, stack4804, stack4808, stack4812, stack4816, + stack4820, stack4824, stack4828, stack4832, stack4836, stack4840, + stack4844, stack4848, stack4852, stack4856, stack4860, stack4864, + stack4868, stack4872, stack4876, stack4880, stack4884, stack4888, + stack4892, stack4896, stack4900, stack4904, stack4908, stack4912, + stack4916, stack4920, stack4924, stack4928, stack4932, stack4936, + stack4940, stack4944, stack4948, stack4952, stack4956, stack4960, + stack4964, stack4968, stack4972, stack4976, stack4980, stack4984, + stack4988, stack4992, stack4996, stack5000, +} + +// Edit .+1,$ | seq 4 4 5000 | sed 's/.*/func stack&() { var buf [&]byte; use(buf[:]); C.callGoStackCheck() }/' +func stack4() { var buf [4]byte; use(buf[:]); C.callGoStackCheck() } +func stack8() { var buf [8]byte; use(buf[:]); C.callGoStackCheck() } +func stack12() { var buf [12]byte; use(buf[:]); C.callGoStackCheck() } +func stack16() { var buf [16]byte; use(buf[:]); C.callGoStackCheck() } +func stack20() { var buf [20]byte; use(buf[:]); C.callGoStackCheck() } +func stack24() { var buf [24]byte; use(buf[:]); C.callGoStackCheck() } +func stack28() { var buf [28]byte; use(buf[:]); C.callGoStackCheck() } +func stack32() { var buf [32]byte; use(buf[:]); C.callGoStackCheck() } +func stack36() { var buf [36]byte; use(buf[:]); C.callGoStackCheck() } +func stack40() { var buf [40]byte; use(buf[:]); C.callGoStackCheck() } +func stack44() { var buf [44]byte; use(buf[:]); C.callGoStackCheck() } +func stack48() { var buf [48]byte; use(buf[:]); C.callGoStackCheck() } +func stack52() { var buf [52]byte; use(buf[:]); C.callGoStackCheck() } +func stack56() { var buf [56]byte; use(buf[:]); C.callGoStackCheck() } +func stack60() { var buf [60]byte; use(buf[:]); C.callGoStackCheck() } +func stack64() { var buf [64]byte; use(buf[:]); C.callGoStackCheck() } +func stack68() { var buf [68]byte; use(buf[:]); C.callGoStackCheck() } +func stack72() { var buf [72]byte; use(buf[:]); C.callGoStackCheck() } +func stack76() { var buf [76]byte; use(buf[:]); C.callGoStackCheck() } +func stack80() { var buf [80]byte; use(buf[:]); C.callGoStackCheck() } +func stack84() { var buf [84]byte; use(buf[:]); C.callGoStackCheck() } +func stack88() { var buf [88]byte; use(buf[:]); C.callGoStackCheck() } +func stack92() { var buf [92]byte; use(buf[:]); C.callGoStackCheck() } +func stack96() { var buf [96]byte; use(buf[:]); C.callGoStackCheck() } +func stack100() { var buf [100]byte; use(buf[:]); C.callGoStackCheck() } +func stack104() { var buf [104]byte; use(buf[:]); C.callGoStackCheck() } +func stack108() { var buf [108]byte; use(buf[:]); C.callGoStackCheck() } +func stack112() { var buf [112]byte; use(buf[:]); C.callGoStackCheck() } +func stack116() { var buf [116]byte; use(buf[:]); C.callGoStackCheck() } +func stack120() { var buf [120]byte; use(buf[:]); C.callGoStackCheck() } +func stack124() { var buf [124]byte; use(buf[:]); C.callGoStackCheck() } +func stack128() { var buf [128]byte; use(buf[:]); C.callGoStackCheck() } +func stack132() { var buf [132]byte; use(buf[:]); C.callGoStackCheck() } +func stack136() { var buf [136]byte; use(buf[:]); C.callGoStackCheck() } +func stack140() { var buf [140]byte; use(buf[:]); C.callGoStackCheck() } +func stack144() { var buf [144]byte; use(buf[:]); C.callGoStackCheck() } +func stack148() { var buf [148]byte; use(buf[:]); C.callGoStackCheck() } +func stack152() { var buf [152]byte; use(buf[:]); C.callGoStackCheck() } +func stack156() { var buf [156]byte; use(buf[:]); C.callGoStackCheck() } +func stack160() { var buf [160]byte; use(buf[:]); C.callGoStackCheck() } +func stack164() { var buf [164]byte; use(buf[:]); C.callGoStackCheck() } +func stack168() { var buf [168]byte; use(buf[:]); C.callGoStackCheck() } +func stack172() { var buf [172]byte; use(buf[:]); C.callGoStackCheck() } +func stack176() { var buf [176]byte; use(buf[:]); C.callGoStackCheck() } +func stack180() { var buf [180]byte; use(buf[:]); C.callGoStackCheck() } +func stack184() { var buf [184]byte; use(buf[:]); C.callGoStackCheck() } +func stack188() { var buf [188]byte; use(buf[:]); C.callGoStackCheck() } +func stack192() { var buf [192]byte; use(buf[:]); C.callGoStackCheck() } +func stack196() { var buf [196]byte; use(buf[:]); C.callGoStackCheck() } +func stack200() { var buf [200]byte; use(buf[:]); C.callGoStackCheck() } +func stack204() { var buf [204]byte; use(buf[:]); C.callGoStackCheck() } +func stack208() { var buf [208]byte; use(buf[:]); C.callGoStackCheck() } +func stack212() { var buf [212]byte; use(buf[:]); C.callGoStackCheck() } +func stack216() { var buf [216]byte; use(buf[:]); C.callGoStackCheck() } +func stack220() { var buf [220]byte; use(buf[:]); C.callGoStackCheck() } +func stack224() { var buf [224]byte; use(buf[:]); C.callGoStackCheck() } +func stack228() { var buf [228]byte; use(buf[:]); C.callGoStackCheck() } +func stack232() { var buf [232]byte; use(buf[:]); C.callGoStackCheck() } +func stack236() { var buf [236]byte; use(buf[:]); C.callGoStackCheck() } +func stack240() { var buf [240]byte; use(buf[:]); C.callGoStackCheck() } +func stack244() { var buf [244]byte; use(buf[:]); C.callGoStackCheck() } +func stack248() { var buf [248]byte; use(buf[:]); C.callGoStackCheck() } +func stack252() { var buf [252]byte; use(buf[:]); C.callGoStackCheck() } +func stack256() { var buf [256]byte; use(buf[:]); C.callGoStackCheck() } +func stack260() { var buf [260]byte; use(buf[:]); C.callGoStackCheck() } +func stack264() { var buf [264]byte; use(buf[:]); C.callGoStackCheck() } +func stack268() { var buf [268]byte; use(buf[:]); C.callGoStackCheck() } +func stack272() { var buf [272]byte; use(buf[:]); C.callGoStackCheck() } +func stack276() { var buf [276]byte; use(buf[:]); C.callGoStackCheck() } +func stack280() { var buf [280]byte; use(buf[:]); C.callGoStackCheck() } +func stack284() { var buf [284]byte; use(buf[:]); C.callGoStackCheck() } +func stack288() { var buf [288]byte; use(buf[:]); C.callGoStackCheck() } +func stack292() { var buf [292]byte; use(buf[:]); C.callGoStackCheck() } +func stack296() { var buf [296]byte; use(buf[:]); C.callGoStackCheck() } +func stack300() { var buf [300]byte; use(buf[:]); C.callGoStackCheck() } +func stack304() { var buf [304]byte; use(buf[:]); C.callGoStackCheck() } +func stack308() { var buf [308]byte; use(buf[:]); C.callGoStackCheck() } +func stack312() { var buf [312]byte; use(buf[:]); C.callGoStackCheck() } +func stack316() { var buf [316]byte; use(buf[:]); C.callGoStackCheck() } +func stack320() { var buf [320]byte; use(buf[:]); C.callGoStackCheck() } +func stack324() { var buf [324]byte; use(buf[:]); C.callGoStackCheck() } +func stack328() { var buf [328]byte; use(buf[:]); C.callGoStackCheck() } +func stack332() { var buf [332]byte; use(buf[:]); C.callGoStackCheck() } +func stack336() { var buf [336]byte; use(buf[:]); C.callGoStackCheck() } +func stack340() { var buf [340]byte; use(buf[:]); C.callGoStackCheck() } +func stack344() { var buf [344]byte; use(buf[:]); C.callGoStackCheck() } +func stack348() { var buf [348]byte; use(buf[:]); C.callGoStackCheck() } +func stack352() { var buf [352]byte; use(buf[:]); C.callGoStackCheck() } +func stack356() { var buf [356]byte; use(buf[:]); C.callGoStackCheck() } +func stack360() { var buf [360]byte; use(buf[:]); C.callGoStackCheck() } +func stack364() { var buf [364]byte; use(buf[:]); C.callGoStackCheck() } +func stack368() { var buf [368]byte; use(buf[:]); C.callGoStackCheck() } +func stack372() { var buf [372]byte; use(buf[:]); C.callGoStackCheck() } +func stack376() { var buf [376]byte; use(buf[:]); C.callGoStackCheck() } +func stack380() { var buf [380]byte; use(buf[:]); C.callGoStackCheck() } +func stack384() { var buf [384]byte; use(buf[:]); C.callGoStackCheck() } +func stack388() { var buf [388]byte; use(buf[:]); C.callGoStackCheck() } +func stack392() { var buf [392]byte; use(buf[:]); C.callGoStackCheck() } +func stack396() { var buf [396]byte; use(buf[:]); C.callGoStackCheck() } +func stack400() { var buf [400]byte; use(buf[:]); C.callGoStackCheck() } +func stack404() { var buf [404]byte; use(buf[:]); C.callGoStackCheck() } +func stack408() { var buf [408]byte; use(buf[:]); C.callGoStackCheck() } +func stack412() { var buf [412]byte; use(buf[:]); C.callGoStackCheck() } +func stack416() { var buf [416]byte; use(buf[:]); C.callGoStackCheck() } +func stack420() { var buf [420]byte; use(buf[:]); C.callGoStackCheck() } +func stack424() { var buf [424]byte; use(buf[:]); C.callGoStackCheck() } +func stack428() { var buf [428]byte; use(buf[:]); C.callGoStackCheck() } +func stack432() { var buf [432]byte; use(buf[:]); C.callGoStackCheck() } +func stack436() { var buf [436]byte; use(buf[:]); C.callGoStackCheck() } +func stack440() { var buf [440]byte; use(buf[:]); C.callGoStackCheck() } +func stack444() { var buf [444]byte; use(buf[:]); C.callGoStackCheck() } +func stack448() { var buf [448]byte; use(buf[:]); C.callGoStackCheck() } +func stack452() { var buf [452]byte; use(buf[:]); C.callGoStackCheck() } +func stack456() { var buf [456]byte; use(buf[:]); C.callGoStackCheck() } +func stack460() { var buf [460]byte; use(buf[:]); C.callGoStackCheck() } +func stack464() { var buf [464]byte; use(buf[:]); C.callGoStackCheck() } +func stack468() { var buf [468]byte; use(buf[:]); C.callGoStackCheck() } +func stack472() { var buf [472]byte; use(buf[:]); C.callGoStackCheck() } +func stack476() { var buf [476]byte; use(buf[:]); C.callGoStackCheck() } +func stack480() { var buf [480]byte; use(buf[:]); C.callGoStackCheck() } +func stack484() { var buf [484]byte; use(buf[:]); C.callGoStackCheck() } +func stack488() { var buf [488]byte; use(buf[:]); C.callGoStackCheck() } +func stack492() { var buf [492]byte; use(buf[:]); C.callGoStackCheck() } +func stack496() { var buf [496]byte; use(buf[:]); C.callGoStackCheck() } +func stack500() { var buf [500]byte; use(buf[:]); C.callGoStackCheck() } +func stack504() { var buf [504]byte; use(buf[:]); C.callGoStackCheck() } +func stack508() { var buf [508]byte; use(buf[:]); C.callGoStackCheck() } +func stack512() { var buf [512]byte; use(buf[:]); C.callGoStackCheck() } +func stack516() { var buf [516]byte; use(buf[:]); C.callGoStackCheck() } +func stack520() { var buf [520]byte; use(buf[:]); C.callGoStackCheck() } +func stack524() { var buf [524]byte; use(buf[:]); C.callGoStackCheck() } +func stack528() { var buf [528]byte; use(buf[:]); C.callGoStackCheck() } +func stack532() { var buf [532]byte; use(buf[:]); C.callGoStackCheck() } +func stack536() { var buf [536]byte; use(buf[:]); C.callGoStackCheck() } +func stack540() { var buf [540]byte; use(buf[:]); C.callGoStackCheck() } +func stack544() { var buf [544]byte; use(buf[:]); C.callGoStackCheck() } +func stack548() { var buf [548]byte; use(buf[:]); C.callGoStackCheck() } +func stack552() { var buf [552]byte; use(buf[:]); C.callGoStackCheck() } +func stack556() { var buf [556]byte; use(buf[:]); C.callGoStackCheck() } +func stack560() { var buf [560]byte; use(buf[:]); C.callGoStackCheck() } +func stack564() { var buf [564]byte; use(buf[:]); C.callGoStackCheck() } +func stack568() { var buf [568]byte; use(buf[:]); C.callGoStackCheck() } +func stack572() { var buf [572]byte; use(buf[:]); C.callGoStackCheck() } +func stack576() { var buf [576]byte; use(buf[:]); C.callGoStackCheck() } +func stack580() { var buf [580]byte; use(buf[:]); C.callGoStackCheck() } +func stack584() { var buf [584]byte; use(buf[:]); C.callGoStackCheck() } +func stack588() { var buf [588]byte; use(buf[:]); C.callGoStackCheck() } +func stack592() { var buf [592]byte; use(buf[:]); C.callGoStackCheck() } +func stack596() { var buf [596]byte; use(buf[:]); C.callGoStackCheck() } +func stack600() { var buf [600]byte; use(buf[:]); C.callGoStackCheck() } +func stack604() { var buf [604]byte; use(buf[:]); C.callGoStackCheck() } +func stack608() { var buf [608]byte; use(buf[:]); C.callGoStackCheck() } +func stack612() { var buf [612]byte; use(buf[:]); C.callGoStackCheck() } +func stack616() { var buf [616]byte; use(buf[:]); C.callGoStackCheck() } +func stack620() { var buf [620]byte; use(buf[:]); C.callGoStackCheck() } +func stack624() { var buf [624]byte; use(buf[:]); C.callGoStackCheck() } +func stack628() { var buf [628]byte; use(buf[:]); C.callGoStackCheck() } +func stack632() { var buf [632]byte; use(buf[:]); C.callGoStackCheck() } +func stack636() { var buf [636]byte; use(buf[:]); C.callGoStackCheck() } +func stack640() { var buf [640]byte; use(buf[:]); C.callGoStackCheck() } +func stack644() { var buf [644]byte; use(buf[:]); C.callGoStackCheck() } +func stack648() { var buf [648]byte; use(buf[:]); C.callGoStackCheck() } +func stack652() { var buf [652]byte; use(buf[:]); C.callGoStackCheck() } +func stack656() { var buf [656]byte; use(buf[:]); C.callGoStackCheck() } +func stack660() { var buf [660]byte; use(buf[:]); C.callGoStackCheck() } +func stack664() { var buf [664]byte; use(buf[:]); C.callGoStackCheck() } +func stack668() { var buf [668]byte; use(buf[:]); C.callGoStackCheck() } +func stack672() { var buf [672]byte; use(buf[:]); C.callGoStackCheck() } +func stack676() { var buf [676]byte; use(buf[:]); C.callGoStackCheck() } +func stack680() { var buf [680]byte; use(buf[:]); C.callGoStackCheck() } +func stack684() { var buf [684]byte; use(buf[:]); C.callGoStackCheck() } +func stack688() { var buf [688]byte; use(buf[:]); C.callGoStackCheck() } +func stack692() { var buf [692]byte; use(buf[:]); C.callGoStackCheck() } +func stack696() { var buf [696]byte; use(buf[:]); C.callGoStackCheck() } +func stack700() { var buf [700]byte; use(buf[:]); C.callGoStackCheck() } +func stack704() { var buf [704]byte; use(buf[:]); C.callGoStackCheck() } +func stack708() { var buf [708]byte; use(buf[:]); C.callGoStackCheck() } +func stack712() { var buf [712]byte; use(buf[:]); C.callGoStackCheck() } +func stack716() { var buf [716]byte; use(buf[:]); C.callGoStackCheck() } +func stack720() { var buf [720]byte; use(buf[:]); C.callGoStackCheck() } +func stack724() { var buf [724]byte; use(buf[:]); C.callGoStackCheck() } +func stack728() { var buf [728]byte; use(buf[:]); C.callGoStackCheck() } +func stack732() { var buf [732]byte; use(buf[:]); C.callGoStackCheck() } +func stack736() { var buf [736]byte; use(buf[:]); C.callGoStackCheck() } +func stack740() { var buf [740]byte; use(buf[:]); C.callGoStackCheck() } +func stack744() { var buf [744]byte; use(buf[:]); C.callGoStackCheck() } +func stack748() { var buf [748]byte; use(buf[:]); C.callGoStackCheck() } +func stack752() { var buf [752]byte; use(buf[:]); C.callGoStackCheck() } +func stack756() { var buf [756]byte; use(buf[:]); C.callGoStackCheck() } +func stack760() { var buf [760]byte; use(buf[:]); C.callGoStackCheck() } +func stack764() { var buf [764]byte; use(buf[:]); C.callGoStackCheck() } +func stack768() { var buf [768]byte; use(buf[:]); C.callGoStackCheck() } +func stack772() { var buf [772]byte; use(buf[:]); C.callGoStackCheck() } +func stack776() { var buf [776]byte; use(buf[:]); C.callGoStackCheck() } +func stack780() { var buf [780]byte; use(buf[:]); C.callGoStackCheck() } +func stack784() { var buf [784]byte; use(buf[:]); C.callGoStackCheck() } +func stack788() { var buf [788]byte; use(buf[:]); C.callGoStackCheck() } +func stack792() { var buf [792]byte; use(buf[:]); C.callGoStackCheck() } +func stack796() { var buf [796]byte; use(buf[:]); C.callGoStackCheck() } +func stack800() { var buf [800]byte; use(buf[:]); C.callGoStackCheck() } +func stack804() { var buf [804]byte; use(buf[:]); C.callGoStackCheck() } +func stack808() { var buf [808]byte; use(buf[:]); C.callGoStackCheck() } +func stack812() { var buf [812]byte; use(buf[:]); C.callGoStackCheck() } +func stack816() { var buf [816]byte; use(buf[:]); C.callGoStackCheck() } +func stack820() { var buf [820]byte; use(buf[:]); C.callGoStackCheck() } +func stack824() { var buf [824]byte; use(buf[:]); C.callGoStackCheck() } +func stack828() { var buf [828]byte; use(buf[:]); C.callGoStackCheck() } +func stack832() { var buf [832]byte; use(buf[:]); C.callGoStackCheck() } +func stack836() { var buf [836]byte; use(buf[:]); C.callGoStackCheck() } +func stack840() { var buf [840]byte; use(buf[:]); C.callGoStackCheck() } +func stack844() { var buf [844]byte; use(buf[:]); C.callGoStackCheck() } +func stack848() { var buf [848]byte; use(buf[:]); C.callGoStackCheck() } +func stack852() { var buf [852]byte; use(buf[:]); C.callGoStackCheck() } +func stack856() { var buf [856]byte; use(buf[:]); C.callGoStackCheck() } +func stack860() { var buf [860]byte; use(buf[:]); C.callGoStackCheck() } +func stack864() { var buf [864]byte; use(buf[:]); C.callGoStackCheck() } +func stack868() { var buf [868]byte; use(buf[:]); C.callGoStackCheck() } +func stack872() { var buf [872]byte; use(buf[:]); C.callGoStackCheck() } +func stack876() { var buf [876]byte; use(buf[:]); C.callGoStackCheck() } +func stack880() { var buf [880]byte; use(buf[:]); C.callGoStackCheck() } +func stack884() { var buf [884]byte; use(buf[:]); C.callGoStackCheck() } +func stack888() { var buf [888]byte; use(buf[:]); C.callGoStackCheck() } +func stack892() { var buf [892]byte; use(buf[:]); C.callGoStackCheck() } +func stack896() { var buf [896]byte; use(buf[:]); C.callGoStackCheck() } +func stack900() { var buf [900]byte; use(buf[:]); C.callGoStackCheck() } +func stack904() { var buf [904]byte; use(buf[:]); C.callGoStackCheck() } +func stack908() { var buf [908]byte; use(buf[:]); C.callGoStackCheck() } +func stack912() { var buf [912]byte; use(buf[:]); C.callGoStackCheck() } +func stack916() { var buf [916]byte; use(buf[:]); C.callGoStackCheck() } +func stack920() { var buf [920]byte; use(buf[:]); C.callGoStackCheck() } +func stack924() { var buf [924]byte; use(buf[:]); C.callGoStackCheck() } +func stack928() { var buf [928]byte; use(buf[:]); C.callGoStackCheck() } +func stack932() { var buf [932]byte; use(buf[:]); C.callGoStackCheck() } +func stack936() { var buf [936]byte; use(buf[:]); C.callGoStackCheck() } +func stack940() { var buf [940]byte; use(buf[:]); C.callGoStackCheck() } +func stack944() { var buf [944]byte; use(buf[:]); C.callGoStackCheck() } +func stack948() { var buf [948]byte; use(buf[:]); C.callGoStackCheck() } +func stack952() { var buf [952]byte; use(buf[:]); C.callGoStackCheck() } +func stack956() { var buf [956]byte; use(buf[:]); C.callGoStackCheck() } +func stack960() { var buf [960]byte; use(buf[:]); C.callGoStackCheck() } +func stack964() { var buf [964]byte; use(buf[:]); C.callGoStackCheck() } +func stack968() { var buf [968]byte; use(buf[:]); C.callGoStackCheck() } +func stack972() { var buf [972]byte; use(buf[:]); C.callGoStackCheck() } +func stack976() { var buf [976]byte; use(buf[:]); C.callGoStackCheck() } +func stack980() { var buf [980]byte; use(buf[:]); C.callGoStackCheck() } +func stack984() { var buf [984]byte; use(buf[:]); C.callGoStackCheck() } +func stack988() { var buf [988]byte; use(buf[:]); C.callGoStackCheck() } +func stack992() { var buf [992]byte; use(buf[:]); C.callGoStackCheck() } +func stack996() { var buf [996]byte; use(buf[:]); C.callGoStackCheck() } +func stack1000() { var buf [1000]byte; use(buf[:]); C.callGoStackCheck() } +func stack1004() { var buf [1004]byte; use(buf[:]); C.callGoStackCheck() } +func stack1008() { var buf [1008]byte; use(buf[:]); C.callGoStackCheck() } +func stack1012() { var buf [1012]byte; use(buf[:]); C.callGoStackCheck() } +func stack1016() { var buf [1016]byte; use(buf[:]); C.callGoStackCheck() } +func stack1020() { var buf [1020]byte; use(buf[:]); C.callGoStackCheck() } +func stack1024() { var buf [1024]byte; use(buf[:]); C.callGoStackCheck() } +func stack1028() { var buf [1028]byte; use(buf[:]); C.callGoStackCheck() } +func stack1032() { var buf [1032]byte; use(buf[:]); C.callGoStackCheck() } +func stack1036() { var buf [1036]byte; use(buf[:]); C.callGoStackCheck() } +func stack1040() { var buf [1040]byte; use(buf[:]); C.callGoStackCheck() } +func stack1044() { var buf [1044]byte; use(buf[:]); C.callGoStackCheck() } +func stack1048() { var buf [1048]byte; use(buf[:]); C.callGoStackCheck() } +func stack1052() { var buf [1052]byte; use(buf[:]); C.callGoStackCheck() } +func stack1056() { var buf [1056]byte; use(buf[:]); C.callGoStackCheck() } +func stack1060() { var buf [1060]byte; use(buf[:]); C.callGoStackCheck() } +func stack1064() { var buf [1064]byte; use(buf[:]); C.callGoStackCheck() } +func stack1068() { var buf [1068]byte; use(buf[:]); C.callGoStackCheck() } +func stack1072() { var buf [1072]byte; use(buf[:]); C.callGoStackCheck() } +func stack1076() { var buf [1076]byte; use(buf[:]); C.callGoStackCheck() } +func stack1080() { var buf [1080]byte; use(buf[:]); C.callGoStackCheck() } +func stack1084() { var buf [1084]byte; use(buf[:]); C.callGoStackCheck() } +func stack1088() { var buf [1088]byte; use(buf[:]); C.callGoStackCheck() } +func stack1092() { var buf [1092]byte; use(buf[:]); C.callGoStackCheck() } +func stack1096() { var buf [1096]byte; use(buf[:]); C.callGoStackCheck() } +func stack1100() { var buf [1100]byte; use(buf[:]); C.callGoStackCheck() } +func stack1104() { var buf [1104]byte; use(buf[:]); C.callGoStackCheck() } +func stack1108() { var buf [1108]byte; use(buf[:]); C.callGoStackCheck() } +func stack1112() { var buf [1112]byte; use(buf[:]); C.callGoStackCheck() } +func stack1116() { var buf [1116]byte; use(buf[:]); C.callGoStackCheck() } +func stack1120() { var buf [1120]byte; use(buf[:]); C.callGoStackCheck() } +func stack1124() { var buf [1124]byte; use(buf[:]); C.callGoStackCheck() } +func stack1128() { var buf [1128]byte; use(buf[:]); C.callGoStackCheck() } +func stack1132() { var buf [1132]byte; use(buf[:]); C.callGoStackCheck() } +func stack1136() { var buf [1136]byte; use(buf[:]); C.callGoStackCheck() } +func stack1140() { var buf [1140]byte; use(buf[:]); C.callGoStackCheck() } +func stack1144() { var buf [1144]byte; use(buf[:]); C.callGoStackCheck() } +func stack1148() { var buf [1148]byte; use(buf[:]); C.callGoStackCheck() } +func stack1152() { var buf [1152]byte; use(buf[:]); C.callGoStackCheck() } +func stack1156() { var buf [1156]byte; use(buf[:]); C.callGoStackCheck() } +func stack1160() { var buf [1160]byte; use(buf[:]); C.callGoStackCheck() } +func stack1164() { var buf [1164]byte; use(buf[:]); C.callGoStackCheck() } +func stack1168() { var buf [1168]byte; use(buf[:]); C.callGoStackCheck() } +func stack1172() { var buf [1172]byte; use(buf[:]); C.callGoStackCheck() } +func stack1176() { var buf [1176]byte; use(buf[:]); C.callGoStackCheck() } +func stack1180() { var buf [1180]byte; use(buf[:]); C.callGoStackCheck() } +func stack1184() { var buf [1184]byte; use(buf[:]); C.callGoStackCheck() } +func stack1188() { var buf [1188]byte; use(buf[:]); C.callGoStackCheck() } +func stack1192() { var buf [1192]byte; use(buf[:]); C.callGoStackCheck() } +func stack1196() { var buf [1196]byte; use(buf[:]); C.callGoStackCheck() } +func stack1200() { var buf [1200]byte; use(buf[:]); C.callGoStackCheck() } +func stack1204() { var buf [1204]byte; use(buf[:]); C.callGoStackCheck() } +func stack1208() { var buf [1208]byte; use(buf[:]); C.callGoStackCheck() } +func stack1212() { var buf [1212]byte; use(buf[:]); C.callGoStackCheck() } +func stack1216() { var buf [1216]byte; use(buf[:]); C.callGoStackCheck() } +func stack1220() { var buf [1220]byte; use(buf[:]); C.callGoStackCheck() } +func stack1224() { var buf [1224]byte; use(buf[:]); C.callGoStackCheck() } +func stack1228() { var buf [1228]byte; use(buf[:]); C.callGoStackCheck() } +func stack1232() { var buf [1232]byte; use(buf[:]); C.callGoStackCheck() } +func stack1236() { var buf [1236]byte; use(buf[:]); C.callGoStackCheck() } +func stack1240() { var buf [1240]byte; use(buf[:]); C.callGoStackCheck() } +func stack1244() { var buf [1244]byte; use(buf[:]); C.callGoStackCheck() } +func stack1248() { var buf [1248]byte; use(buf[:]); C.callGoStackCheck() } +func stack1252() { var buf [1252]byte; use(buf[:]); C.callGoStackCheck() } +func stack1256() { var buf [1256]byte; use(buf[:]); C.callGoStackCheck() } +func stack1260() { var buf [1260]byte; use(buf[:]); C.callGoStackCheck() } +func stack1264() { var buf [1264]byte; use(buf[:]); C.callGoStackCheck() } +func stack1268() { var buf [1268]byte; use(buf[:]); C.callGoStackCheck() } +func stack1272() { var buf [1272]byte; use(buf[:]); C.callGoStackCheck() } +func stack1276() { var buf [1276]byte; use(buf[:]); C.callGoStackCheck() } +func stack1280() { var buf [1280]byte; use(buf[:]); C.callGoStackCheck() } +func stack1284() { var buf [1284]byte; use(buf[:]); C.callGoStackCheck() } +func stack1288() { var buf [1288]byte; use(buf[:]); C.callGoStackCheck() } +func stack1292() { var buf [1292]byte; use(buf[:]); C.callGoStackCheck() } +func stack1296() { var buf [1296]byte; use(buf[:]); C.callGoStackCheck() } +func stack1300() { var buf [1300]byte; use(buf[:]); C.callGoStackCheck() } +func stack1304() { var buf [1304]byte; use(buf[:]); C.callGoStackCheck() } +func stack1308() { var buf [1308]byte; use(buf[:]); C.callGoStackCheck() } +func stack1312() { var buf [1312]byte; use(buf[:]); C.callGoStackCheck() } +func stack1316() { var buf [1316]byte; use(buf[:]); C.callGoStackCheck() } +func stack1320() { var buf [1320]byte; use(buf[:]); C.callGoStackCheck() } +func stack1324() { var buf [1324]byte; use(buf[:]); C.callGoStackCheck() } +func stack1328() { var buf [1328]byte; use(buf[:]); C.callGoStackCheck() } +func stack1332() { var buf [1332]byte; use(buf[:]); C.callGoStackCheck() } +func stack1336() { var buf [1336]byte; use(buf[:]); C.callGoStackCheck() } +func stack1340() { var buf [1340]byte; use(buf[:]); C.callGoStackCheck() } +func stack1344() { var buf [1344]byte; use(buf[:]); C.callGoStackCheck() } +func stack1348() { var buf [1348]byte; use(buf[:]); C.callGoStackCheck() } +func stack1352() { var buf [1352]byte; use(buf[:]); C.callGoStackCheck() } +func stack1356() { var buf [1356]byte; use(buf[:]); C.callGoStackCheck() } +func stack1360() { var buf [1360]byte; use(buf[:]); C.callGoStackCheck() } +func stack1364() { var buf [1364]byte; use(buf[:]); C.callGoStackCheck() } +func stack1368() { var buf [1368]byte; use(buf[:]); C.callGoStackCheck() } +func stack1372() { var buf [1372]byte; use(buf[:]); C.callGoStackCheck() } +func stack1376() { var buf [1376]byte; use(buf[:]); C.callGoStackCheck() } +func stack1380() { var buf [1380]byte; use(buf[:]); C.callGoStackCheck() } +func stack1384() { var buf [1384]byte; use(buf[:]); C.callGoStackCheck() } +func stack1388() { var buf [1388]byte; use(buf[:]); C.callGoStackCheck() } +func stack1392() { var buf [1392]byte; use(buf[:]); C.callGoStackCheck() } +func stack1396() { var buf [1396]byte; use(buf[:]); C.callGoStackCheck() } +func stack1400() { var buf [1400]byte; use(buf[:]); C.callGoStackCheck() } +func stack1404() { var buf [1404]byte; use(buf[:]); C.callGoStackCheck() } +func stack1408() { var buf [1408]byte; use(buf[:]); C.callGoStackCheck() } +func stack1412() { var buf [1412]byte; use(buf[:]); C.callGoStackCheck() } +func stack1416() { var buf [1416]byte; use(buf[:]); C.callGoStackCheck() } +func stack1420() { var buf [1420]byte; use(buf[:]); C.callGoStackCheck() } +func stack1424() { var buf [1424]byte; use(buf[:]); C.callGoStackCheck() } +func stack1428() { var buf [1428]byte; use(buf[:]); C.callGoStackCheck() } +func stack1432() { var buf [1432]byte; use(buf[:]); C.callGoStackCheck() } +func stack1436() { var buf [1436]byte; use(buf[:]); C.callGoStackCheck() } +func stack1440() { var buf [1440]byte; use(buf[:]); C.callGoStackCheck() } +func stack1444() { var buf [1444]byte; use(buf[:]); C.callGoStackCheck() } +func stack1448() { var buf [1448]byte; use(buf[:]); C.callGoStackCheck() } +func stack1452() { var buf [1452]byte; use(buf[:]); C.callGoStackCheck() } +func stack1456() { var buf [1456]byte; use(buf[:]); C.callGoStackCheck() } +func stack1460() { var buf [1460]byte; use(buf[:]); C.callGoStackCheck() } +func stack1464() { var buf [1464]byte; use(buf[:]); C.callGoStackCheck() } +func stack1468() { var buf [1468]byte; use(buf[:]); C.callGoStackCheck() } +func stack1472() { var buf [1472]byte; use(buf[:]); C.callGoStackCheck() } +func stack1476() { var buf [1476]byte; use(buf[:]); C.callGoStackCheck() } +func stack1480() { var buf [1480]byte; use(buf[:]); C.callGoStackCheck() } +func stack1484() { var buf [1484]byte; use(buf[:]); C.callGoStackCheck() } +func stack1488() { var buf [1488]byte; use(buf[:]); C.callGoStackCheck() } +func stack1492() { var buf [1492]byte; use(buf[:]); C.callGoStackCheck() } +func stack1496() { var buf [1496]byte; use(buf[:]); C.callGoStackCheck() } +func stack1500() { var buf [1500]byte; use(buf[:]); C.callGoStackCheck() } +func stack1504() { var buf [1504]byte; use(buf[:]); C.callGoStackCheck() } +func stack1508() { var buf [1508]byte; use(buf[:]); C.callGoStackCheck() } +func stack1512() { var buf [1512]byte; use(buf[:]); C.callGoStackCheck() } +func stack1516() { var buf [1516]byte; use(buf[:]); C.callGoStackCheck() } +func stack1520() { var buf [1520]byte; use(buf[:]); C.callGoStackCheck() } +func stack1524() { var buf [1524]byte; use(buf[:]); C.callGoStackCheck() } +func stack1528() { var buf [1528]byte; use(buf[:]); C.callGoStackCheck() } +func stack1532() { var buf [1532]byte; use(buf[:]); C.callGoStackCheck() } +func stack1536() { var buf [1536]byte; use(buf[:]); C.callGoStackCheck() } +func stack1540() { var buf [1540]byte; use(buf[:]); C.callGoStackCheck() } +func stack1544() { var buf [1544]byte; use(buf[:]); C.callGoStackCheck() } +func stack1548() { var buf [1548]byte; use(buf[:]); C.callGoStackCheck() } +func stack1552() { var buf [1552]byte; use(buf[:]); C.callGoStackCheck() } +func stack1556() { var buf [1556]byte; use(buf[:]); C.callGoStackCheck() } +func stack1560() { var buf [1560]byte; use(buf[:]); C.callGoStackCheck() } +func stack1564() { var buf [1564]byte; use(buf[:]); C.callGoStackCheck() } +func stack1568() { var buf [1568]byte; use(buf[:]); C.callGoStackCheck() } +func stack1572() { var buf [1572]byte; use(buf[:]); C.callGoStackCheck() } +func stack1576() { var buf [1576]byte; use(buf[:]); C.callGoStackCheck() } +func stack1580() { var buf [1580]byte; use(buf[:]); C.callGoStackCheck() } +func stack1584() { var buf [1584]byte; use(buf[:]); C.callGoStackCheck() } +func stack1588() { var buf [1588]byte; use(buf[:]); C.callGoStackCheck() } +func stack1592() { var buf [1592]byte; use(buf[:]); C.callGoStackCheck() } +func stack1596() { var buf [1596]byte; use(buf[:]); C.callGoStackCheck() } +func stack1600() { var buf [1600]byte; use(buf[:]); C.callGoStackCheck() } +func stack1604() { var buf [1604]byte; use(buf[:]); C.callGoStackCheck() } +func stack1608() { var buf [1608]byte; use(buf[:]); C.callGoStackCheck() } +func stack1612() { var buf [1612]byte; use(buf[:]); C.callGoStackCheck() } +func stack1616() { var buf [1616]byte; use(buf[:]); C.callGoStackCheck() } +func stack1620() { var buf [1620]byte; use(buf[:]); C.callGoStackCheck() } +func stack1624() { var buf [1624]byte; use(buf[:]); C.callGoStackCheck() } +func stack1628() { var buf [1628]byte; use(buf[:]); C.callGoStackCheck() } +func stack1632() { var buf [1632]byte; use(buf[:]); C.callGoStackCheck() } +func stack1636() { var buf [1636]byte; use(buf[:]); C.callGoStackCheck() } +func stack1640() { var buf [1640]byte; use(buf[:]); C.callGoStackCheck() } +func stack1644() { var buf [1644]byte; use(buf[:]); C.callGoStackCheck() } +func stack1648() { var buf [1648]byte; use(buf[:]); C.callGoStackCheck() } +func stack1652() { var buf [1652]byte; use(buf[:]); C.callGoStackCheck() } +func stack1656() { var buf [1656]byte; use(buf[:]); C.callGoStackCheck() } +func stack1660() { var buf [1660]byte; use(buf[:]); C.callGoStackCheck() } +func stack1664() { var buf [1664]byte; use(buf[:]); C.callGoStackCheck() } +func stack1668() { var buf [1668]byte; use(buf[:]); C.callGoStackCheck() } +func stack1672() { var buf [1672]byte; use(buf[:]); C.callGoStackCheck() } +func stack1676() { var buf [1676]byte; use(buf[:]); C.callGoStackCheck() } +func stack1680() { var buf [1680]byte; use(buf[:]); C.callGoStackCheck() } +func stack1684() { var buf [1684]byte; use(buf[:]); C.callGoStackCheck() } +func stack1688() { var buf [1688]byte; use(buf[:]); C.callGoStackCheck() } +func stack1692() { var buf [1692]byte; use(buf[:]); C.callGoStackCheck() } +func stack1696() { var buf [1696]byte; use(buf[:]); C.callGoStackCheck() } +func stack1700() { var buf [1700]byte; use(buf[:]); C.callGoStackCheck() } +func stack1704() { var buf [1704]byte; use(buf[:]); C.callGoStackCheck() } +func stack1708() { var buf [1708]byte; use(buf[:]); C.callGoStackCheck() } +func stack1712() { var buf [1712]byte; use(buf[:]); C.callGoStackCheck() } +func stack1716() { var buf [1716]byte; use(buf[:]); C.callGoStackCheck() } +func stack1720() { var buf [1720]byte; use(buf[:]); C.callGoStackCheck() } +func stack1724() { var buf [1724]byte; use(buf[:]); C.callGoStackCheck() } +func stack1728() { var buf [1728]byte; use(buf[:]); C.callGoStackCheck() } +func stack1732() { var buf [1732]byte; use(buf[:]); C.callGoStackCheck() } +func stack1736() { var buf [1736]byte; use(buf[:]); C.callGoStackCheck() } +func stack1740() { var buf [1740]byte; use(buf[:]); C.callGoStackCheck() } +func stack1744() { var buf [1744]byte; use(buf[:]); C.callGoStackCheck() } +func stack1748() { var buf [1748]byte; use(buf[:]); C.callGoStackCheck() } +func stack1752() { var buf [1752]byte; use(buf[:]); C.callGoStackCheck() } +func stack1756() { var buf [1756]byte; use(buf[:]); C.callGoStackCheck() } +func stack1760() { var buf [1760]byte; use(buf[:]); C.callGoStackCheck() } +func stack1764() { var buf [1764]byte; use(buf[:]); C.callGoStackCheck() } +func stack1768() { var buf [1768]byte; use(buf[:]); C.callGoStackCheck() } +func stack1772() { var buf [1772]byte; use(buf[:]); C.callGoStackCheck() } +func stack1776() { var buf [1776]byte; use(buf[:]); C.callGoStackCheck() } +func stack1780() { var buf [1780]byte; use(buf[:]); C.callGoStackCheck() } +func stack1784() { var buf [1784]byte; use(buf[:]); C.callGoStackCheck() } +func stack1788() { var buf [1788]byte; use(buf[:]); C.callGoStackCheck() } +func stack1792() { var buf [1792]byte; use(buf[:]); C.callGoStackCheck() } +func stack1796() { var buf [1796]byte; use(buf[:]); C.callGoStackCheck() } +func stack1800() { var buf [1800]byte; use(buf[:]); C.callGoStackCheck() } +func stack1804() { var buf [1804]byte; use(buf[:]); C.callGoStackCheck() } +func stack1808() { var buf [1808]byte; use(buf[:]); C.callGoStackCheck() } +func stack1812() { var buf [1812]byte; use(buf[:]); C.callGoStackCheck() } +func stack1816() { var buf [1816]byte; use(buf[:]); C.callGoStackCheck() } +func stack1820() { var buf [1820]byte; use(buf[:]); C.callGoStackCheck() } +func stack1824() { var buf [1824]byte; use(buf[:]); C.callGoStackCheck() } +func stack1828() { var buf [1828]byte; use(buf[:]); C.callGoStackCheck() } +func stack1832() { var buf [1832]byte; use(buf[:]); C.callGoStackCheck() } +func stack1836() { var buf [1836]byte; use(buf[:]); C.callGoStackCheck() } +func stack1840() { var buf [1840]byte; use(buf[:]); C.callGoStackCheck() } +func stack1844() { var buf [1844]byte; use(buf[:]); C.callGoStackCheck() } +func stack1848() { var buf [1848]byte; use(buf[:]); C.callGoStackCheck() } +func stack1852() { var buf [1852]byte; use(buf[:]); C.callGoStackCheck() } +func stack1856() { var buf [1856]byte; use(buf[:]); C.callGoStackCheck() } +func stack1860() { var buf [1860]byte; use(buf[:]); C.callGoStackCheck() } +func stack1864() { var buf [1864]byte; use(buf[:]); C.callGoStackCheck() } +func stack1868() { var buf [1868]byte; use(buf[:]); C.callGoStackCheck() } +func stack1872() { var buf [1872]byte; use(buf[:]); C.callGoStackCheck() } +func stack1876() { var buf [1876]byte; use(buf[:]); C.callGoStackCheck() } +func stack1880() { var buf [1880]byte; use(buf[:]); C.callGoStackCheck() } +func stack1884() { var buf [1884]byte; use(buf[:]); C.callGoStackCheck() } +func stack1888() { var buf [1888]byte; use(buf[:]); C.callGoStackCheck() } +func stack1892() { var buf [1892]byte; use(buf[:]); C.callGoStackCheck() } +func stack1896() { var buf [1896]byte; use(buf[:]); C.callGoStackCheck() } +func stack1900() { var buf [1900]byte; use(buf[:]); C.callGoStackCheck() } +func stack1904() { var buf [1904]byte; use(buf[:]); C.callGoStackCheck() } +func stack1908() { var buf [1908]byte; use(buf[:]); C.callGoStackCheck() } +func stack1912() { var buf [1912]byte; use(buf[:]); C.callGoStackCheck() } +func stack1916() { var buf [1916]byte; use(buf[:]); C.callGoStackCheck() } +func stack1920() { var buf [1920]byte; use(buf[:]); C.callGoStackCheck() } +func stack1924() { var buf [1924]byte; use(buf[:]); C.callGoStackCheck() } +func stack1928() { var buf [1928]byte; use(buf[:]); C.callGoStackCheck() } +func stack1932() { var buf [1932]byte; use(buf[:]); C.callGoStackCheck() } +func stack1936() { var buf [1936]byte; use(buf[:]); C.callGoStackCheck() } +func stack1940() { var buf [1940]byte; use(buf[:]); C.callGoStackCheck() } +func stack1944() { var buf [1944]byte; use(buf[:]); C.callGoStackCheck() } +func stack1948() { var buf [1948]byte; use(buf[:]); C.callGoStackCheck() } +func stack1952() { var buf [1952]byte; use(buf[:]); C.callGoStackCheck() } +func stack1956() { var buf [1956]byte; use(buf[:]); C.callGoStackCheck() } +func stack1960() { var buf [1960]byte; use(buf[:]); C.callGoStackCheck() } +func stack1964() { var buf [1964]byte; use(buf[:]); C.callGoStackCheck() } +func stack1968() { var buf [1968]byte; use(buf[:]); C.callGoStackCheck() } +func stack1972() { var buf [1972]byte; use(buf[:]); C.callGoStackCheck() } +func stack1976() { var buf [1976]byte; use(buf[:]); C.callGoStackCheck() } +func stack1980() { var buf [1980]byte; use(buf[:]); C.callGoStackCheck() } +func stack1984() { var buf [1984]byte; use(buf[:]); C.callGoStackCheck() } +func stack1988() { var buf [1988]byte; use(buf[:]); C.callGoStackCheck() } +func stack1992() { var buf [1992]byte; use(buf[:]); C.callGoStackCheck() } +func stack1996() { var buf [1996]byte; use(buf[:]); C.callGoStackCheck() } +func stack2000() { var buf [2000]byte; use(buf[:]); C.callGoStackCheck() } +func stack2004() { var buf [2004]byte; use(buf[:]); C.callGoStackCheck() } +func stack2008() { var buf [2008]byte; use(buf[:]); C.callGoStackCheck() } +func stack2012() { var buf [2012]byte; use(buf[:]); C.callGoStackCheck() } +func stack2016() { var buf [2016]byte; use(buf[:]); C.callGoStackCheck() } +func stack2020() { var buf [2020]byte; use(buf[:]); C.callGoStackCheck() } +func stack2024() { var buf [2024]byte; use(buf[:]); C.callGoStackCheck() } +func stack2028() { var buf [2028]byte; use(buf[:]); C.callGoStackCheck() } +func stack2032() { var buf [2032]byte; use(buf[:]); C.callGoStackCheck() } +func stack2036() { var buf [2036]byte; use(buf[:]); C.callGoStackCheck() } +func stack2040() { var buf [2040]byte; use(buf[:]); C.callGoStackCheck() } +func stack2044() { var buf [2044]byte; use(buf[:]); C.callGoStackCheck() } +func stack2048() { var buf [2048]byte; use(buf[:]); C.callGoStackCheck() } +func stack2052() { var buf [2052]byte; use(buf[:]); C.callGoStackCheck() } +func stack2056() { var buf [2056]byte; use(buf[:]); C.callGoStackCheck() } +func stack2060() { var buf [2060]byte; use(buf[:]); C.callGoStackCheck() } +func stack2064() { var buf [2064]byte; use(buf[:]); C.callGoStackCheck() } +func stack2068() { var buf [2068]byte; use(buf[:]); C.callGoStackCheck() } +func stack2072() { var buf [2072]byte; use(buf[:]); C.callGoStackCheck() } +func stack2076() { var buf [2076]byte; use(buf[:]); C.callGoStackCheck() } +func stack2080() { var buf [2080]byte; use(buf[:]); C.callGoStackCheck() } +func stack2084() { var buf [2084]byte; use(buf[:]); C.callGoStackCheck() } +func stack2088() { var buf [2088]byte; use(buf[:]); C.callGoStackCheck() } +func stack2092() { var buf [2092]byte; use(buf[:]); C.callGoStackCheck() } +func stack2096() { var buf [2096]byte; use(buf[:]); C.callGoStackCheck() } +func stack2100() { var buf [2100]byte; use(buf[:]); C.callGoStackCheck() } +func stack2104() { var buf [2104]byte; use(buf[:]); C.callGoStackCheck() } +func stack2108() { var buf [2108]byte; use(buf[:]); C.callGoStackCheck() } +func stack2112() { var buf [2112]byte; use(buf[:]); C.callGoStackCheck() } +func stack2116() { var buf [2116]byte; use(buf[:]); C.callGoStackCheck() } +func stack2120() { var buf [2120]byte; use(buf[:]); C.callGoStackCheck() } +func stack2124() { var buf [2124]byte; use(buf[:]); C.callGoStackCheck() } +func stack2128() { var buf [2128]byte; use(buf[:]); C.callGoStackCheck() } +func stack2132() { var buf [2132]byte; use(buf[:]); C.callGoStackCheck() } +func stack2136() { var buf [2136]byte; use(buf[:]); C.callGoStackCheck() } +func stack2140() { var buf [2140]byte; use(buf[:]); C.callGoStackCheck() } +func stack2144() { var buf [2144]byte; use(buf[:]); C.callGoStackCheck() } +func stack2148() { var buf [2148]byte; use(buf[:]); C.callGoStackCheck() } +func stack2152() { var buf [2152]byte; use(buf[:]); C.callGoStackCheck() } +func stack2156() { var buf [2156]byte; use(buf[:]); C.callGoStackCheck() } +func stack2160() { var buf [2160]byte; use(buf[:]); C.callGoStackCheck() } +func stack2164() { var buf [2164]byte; use(buf[:]); C.callGoStackCheck() } +func stack2168() { var buf [2168]byte; use(buf[:]); C.callGoStackCheck() } +func stack2172() { var buf [2172]byte; use(buf[:]); C.callGoStackCheck() } +func stack2176() { var buf [2176]byte; use(buf[:]); C.callGoStackCheck() } +func stack2180() { var buf [2180]byte; use(buf[:]); C.callGoStackCheck() } +func stack2184() { var buf [2184]byte; use(buf[:]); C.callGoStackCheck() } +func stack2188() { var buf [2188]byte; use(buf[:]); C.callGoStackCheck() } +func stack2192() { var buf [2192]byte; use(buf[:]); C.callGoStackCheck() } +func stack2196() { var buf [2196]byte; use(buf[:]); C.callGoStackCheck() } +func stack2200() { var buf [2200]byte; use(buf[:]); C.callGoStackCheck() } +func stack2204() { var buf [2204]byte; use(buf[:]); C.callGoStackCheck() } +func stack2208() { var buf [2208]byte; use(buf[:]); C.callGoStackCheck() } +func stack2212() { var buf [2212]byte; use(buf[:]); C.callGoStackCheck() } +func stack2216() { var buf [2216]byte; use(buf[:]); C.callGoStackCheck() } +func stack2220() { var buf [2220]byte; use(buf[:]); C.callGoStackCheck() } +func stack2224() { var buf [2224]byte; use(buf[:]); C.callGoStackCheck() } +func stack2228() { var buf [2228]byte; use(buf[:]); C.callGoStackCheck() } +func stack2232() { var buf [2232]byte; use(buf[:]); C.callGoStackCheck() } +func stack2236() { var buf [2236]byte; use(buf[:]); C.callGoStackCheck() } +func stack2240() { var buf [2240]byte; use(buf[:]); C.callGoStackCheck() } +func stack2244() { var buf [2244]byte; use(buf[:]); C.callGoStackCheck() } +func stack2248() { var buf [2248]byte; use(buf[:]); C.callGoStackCheck() } +func stack2252() { var buf [2252]byte; use(buf[:]); C.callGoStackCheck() } +func stack2256() { var buf [2256]byte; use(buf[:]); C.callGoStackCheck() } +func stack2260() { var buf [2260]byte; use(buf[:]); C.callGoStackCheck() } +func stack2264() { var buf [2264]byte; use(buf[:]); C.callGoStackCheck() } +func stack2268() { var buf [2268]byte; use(buf[:]); C.callGoStackCheck() } +func stack2272() { var buf [2272]byte; use(buf[:]); C.callGoStackCheck() } +func stack2276() { var buf [2276]byte; use(buf[:]); C.callGoStackCheck() } +func stack2280() { var buf [2280]byte; use(buf[:]); C.callGoStackCheck() } +func stack2284() { var buf [2284]byte; use(buf[:]); C.callGoStackCheck() } +func stack2288() { var buf [2288]byte; use(buf[:]); C.callGoStackCheck() } +func stack2292() { var buf [2292]byte; use(buf[:]); C.callGoStackCheck() } +func stack2296() { var buf [2296]byte; use(buf[:]); C.callGoStackCheck() } +func stack2300() { var buf [2300]byte; use(buf[:]); C.callGoStackCheck() } +func stack2304() { var buf [2304]byte; use(buf[:]); C.callGoStackCheck() } +func stack2308() { var buf [2308]byte; use(buf[:]); C.callGoStackCheck() } +func stack2312() { var buf [2312]byte; use(buf[:]); C.callGoStackCheck() } +func stack2316() { var buf [2316]byte; use(buf[:]); C.callGoStackCheck() } +func stack2320() { var buf [2320]byte; use(buf[:]); C.callGoStackCheck() } +func stack2324() { var buf [2324]byte; use(buf[:]); C.callGoStackCheck() } +func stack2328() { var buf [2328]byte; use(buf[:]); C.callGoStackCheck() } +func stack2332() { var buf [2332]byte; use(buf[:]); C.callGoStackCheck() } +func stack2336() { var buf [2336]byte; use(buf[:]); C.callGoStackCheck() } +func stack2340() { var buf [2340]byte; use(buf[:]); C.callGoStackCheck() } +func stack2344() { var buf [2344]byte; use(buf[:]); C.callGoStackCheck() } +func stack2348() { var buf [2348]byte; use(buf[:]); C.callGoStackCheck() } +func stack2352() { var buf [2352]byte; use(buf[:]); C.callGoStackCheck() } +func stack2356() { var buf [2356]byte; use(buf[:]); C.callGoStackCheck() } +func stack2360() { var buf [2360]byte; use(buf[:]); C.callGoStackCheck() } +func stack2364() { var buf [2364]byte; use(buf[:]); C.callGoStackCheck() } +func stack2368() { var buf [2368]byte; use(buf[:]); C.callGoStackCheck() } +func stack2372() { var buf [2372]byte; use(buf[:]); C.callGoStackCheck() } +func stack2376() { var buf [2376]byte; use(buf[:]); C.callGoStackCheck() } +func stack2380() { var buf [2380]byte; use(buf[:]); C.callGoStackCheck() } +func stack2384() { var buf [2384]byte; use(buf[:]); C.callGoStackCheck() } +func stack2388() { var buf [2388]byte; use(buf[:]); C.callGoStackCheck() } +func stack2392() { var buf [2392]byte; use(buf[:]); C.callGoStackCheck() } +func stack2396() { var buf [2396]byte; use(buf[:]); C.callGoStackCheck() } +func stack2400() { var buf [2400]byte; use(buf[:]); C.callGoStackCheck() } +func stack2404() { var buf [2404]byte; use(buf[:]); C.callGoStackCheck() } +func stack2408() { var buf [2408]byte; use(buf[:]); C.callGoStackCheck() } +func stack2412() { var buf [2412]byte; use(buf[:]); C.callGoStackCheck() } +func stack2416() { var buf [2416]byte; use(buf[:]); C.callGoStackCheck() } +func stack2420() { var buf [2420]byte; use(buf[:]); C.callGoStackCheck() } +func stack2424() { var buf [2424]byte; use(buf[:]); C.callGoStackCheck() } +func stack2428() { var buf [2428]byte; use(buf[:]); C.callGoStackCheck() } +func stack2432() { var buf [2432]byte; use(buf[:]); C.callGoStackCheck() } +func stack2436() { var buf [2436]byte; use(buf[:]); C.callGoStackCheck() } +func stack2440() { var buf [2440]byte; use(buf[:]); C.callGoStackCheck() } +func stack2444() { var buf [2444]byte; use(buf[:]); C.callGoStackCheck() } +func stack2448() { var buf [2448]byte; use(buf[:]); C.callGoStackCheck() } +func stack2452() { var buf [2452]byte; use(buf[:]); C.callGoStackCheck() } +func stack2456() { var buf [2456]byte; use(buf[:]); C.callGoStackCheck() } +func stack2460() { var buf [2460]byte; use(buf[:]); C.callGoStackCheck() } +func stack2464() { var buf [2464]byte; use(buf[:]); C.callGoStackCheck() } +func stack2468() { var buf [2468]byte; use(buf[:]); C.callGoStackCheck() } +func stack2472() { var buf [2472]byte; use(buf[:]); C.callGoStackCheck() } +func stack2476() { var buf [2476]byte; use(buf[:]); C.callGoStackCheck() } +func stack2480() { var buf [2480]byte; use(buf[:]); C.callGoStackCheck() } +func stack2484() { var buf [2484]byte; use(buf[:]); C.callGoStackCheck() } +func stack2488() { var buf [2488]byte; use(buf[:]); C.callGoStackCheck() } +func stack2492() { var buf [2492]byte; use(buf[:]); C.callGoStackCheck() } +func stack2496() { var buf [2496]byte; use(buf[:]); C.callGoStackCheck() } +func stack2500() { var buf [2500]byte; use(buf[:]); C.callGoStackCheck() } +func stack2504() { var buf [2504]byte; use(buf[:]); C.callGoStackCheck() } +func stack2508() { var buf [2508]byte; use(buf[:]); C.callGoStackCheck() } +func stack2512() { var buf [2512]byte; use(buf[:]); C.callGoStackCheck() } +func stack2516() { var buf [2516]byte; use(buf[:]); C.callGoStackCheck() } +func stack2520() { var buf [2520]byte; use(buf[:]); C.callGoStackCheck() } +func stack2524() { var buf [2524]byte; use(buf[:]); C.callGoStackCheck() } +func stack2528() { var buf [2528]byte; use(buf[:]); C.callGoStackCheck() } +func stack2532() { var buf [2532]byte; use(buf[:]); C.callGoStackCheck() } +func stack2536() { var buf [2536]byte; use(buf[:]); C.callGoStackCheck() } +func stack2540() { var buf [2540]byte; use(buf[:]); C.callGoStackCheck() } +func stack2544() { var buf [2544]byte; use(buf[:]); C.callGoStackCheck() } +func stack2548() { var buf [2548]byte; use(buf[:]); C.callGoStackCheck() } +func stack2552() { var buf [2552]byte; use(buf[:]); C.callGoStackCheck() } +func stack2556() { var buf [2556]byte; use(buf[:]); C.callGoStackCheck() } +func stack2560() { var buf [2560]byte; use(buf[:]); C.callGoStackCheck() } +func stack2564() { var buf [2564]byte; use(buf[:]); C.callGoStackCheck() } +func stack2568() { var buf [2568]byte; use(buf[:]); C.callGoStackCheck() } +func stack2572() { var buf [2572]byte; use(buf[:]); C.callGoStackCheck() } +func stack2576() { var buf [2576]byte; use(buf[:]); C.callGoStackCheck() } +func stack2580() { var buf [2580]byte; use(buf[:]); C.callGoStackCheck() } +func stack2584() { var buf [2584]byte; use(buf[:]); C.callGoStackCheck() } +func stack2588() { var buf [2588]byte; use(buf[:]); C.callGoStackCheck() } +func stack2592() { var buf [2592]byte; use(buf[:]); C.callGoStackCheck() } +func stack2596() { var buf [2596]byte; use(buf[:]); C.callGoStackCheck() } +func stack2600() { var buf [2600]byte; use(buf[:]); C.callGoStackCheck() } +func stack2604() { var buf [2604]byte; use(buf[:]); C.callGoStackCheck() } +func stack2608() { var buf [2608]byte; use(buf[:]); C.callGoStackCheck() } +func stack2612() { var buf [2612]byte; use(buf[:]); C.callGoStackCheck() } +func stack2616() { var buf [2616]byte; use(buf[:]); C.callGoStackCheck() } +func stack2620() { var buf [2620]byte; use(buf[:]); C.callGoStackCheck() } +func stack2624() { var buf [2624]byte; use(buf[:]); C.callGoStackCheck() } +func stack2628() { var buf [2628]byte; use(buf[:]); C.callGoStackCheck() } +func stack2632() { var buf [2632]byte; use(buf[:]); C.callGoStackCheck() } +func stack2636() { var buf [2636]byte; use(buf[:]); C.callGoStackCheck() } +func stack2640() { var buf [2640]byte; use(buf[:]); C.callGoStackCheck() } +func stack2644() { var buf [2644]byte; use(buf[:]); C.callGoStackCheck() } +func stack2648() { var buf [2648]byte; use(buf[:]); C.callGoStackCheck() } +func stack2652() { var buf [2652]byte; use(buf[:]); C.callGoStackCheck() } +func stack2656() { var buf [2656]byte; use(buf[:]); C.callGoStackCheck() } +func stack2660() { var buf [2660]byte; use(buf[:]); C.callGoStackCheck() } +func stack2664() { var buf [2664]byte; use(buf[:]); C.callGoStackCheck() } +func stack2668() { var buf [2668]byte; use(buf[:]); C.callGoStackCheck() } +func stack2672() { var buf [2672]byte; use(buf[:]); C.callGoStackCheck() } +func stack2676() { var buf [2676]byte; use(buf[:]); C.callGoStackCheck() } +func stack2680() { var buf [2680]byte; use(buf[:]); C.callGoStackCheck() } +func stack2684() { var buf [2684]byte; use(buf[:]); C.callGoStackCheck() } +func stack2688() { var buf [2688]byte; use(buf[:]); C.callGoStackCheck() } +func stack2692() { var buf [2692]byte; use(buf[:]); C.callGoStackCheck() } +func stack2696() { var buf [2696]byte; use(buf[:]); C.callGoStackCheck() } +func stack2700() { var buf [2700]byte; use(buf[:]); C.callGoStackCheck() } +func stack2704() { var buf [2704]byte; use(buf[:]); C.callGoStackCheck() } +func stack2708() { var buf [2708]byte; use(buf[:]); C.callGoStackCheck() } +func stack2712() { var buf [2712]byte; use(buf[:]); C.callGoStackCheck() } +func stack2716() { var buf [2716]byte; use(buf[:]); C.callGoStackCheck() } +func stack2720() { var buf [2720]byte; use(buf[:]); C.callGoStackCheck() } +func stack2724() { var buf [2724]byte; use(buf[:]); C.callGoStackCheck() } +func stack2728() { var buf [2728]byte; use(buf[:]); C.callGoStackCheck() } +func stack2732() { var buf [2732]byte; use(buf[:]); C.callGoStackCheck() } +func stack2736() { var buf [2736]byte; use(buf[:]); C.callGoStackCheck() } +func stack2740() { var buf [2740]byte; use(buf[:]); C.callGoStackCheck() } +func stack2744() { var buf [2744]byte; use(buf[:]); C.callGoStackCheck() } +func stack2748() { var buf [2748]byte; use(buf[:]); C.callGoStackCheck() } +func stack2752() { var buf [2752]byte; use(buf[:]); C.callGoStackCheck() } +func stack2756() { var buf [2756]byte; use(buf[:]); C.callGoStackCheck() } +func stack2760() { var buf [2760]byte; use(buf[:]); C.callGoStackCheck() } +func stack2764() { var buf [2764]byte; use(buf[:]); C.callGoStackCheck() } +func stack2768() { var buf [2768]byte; use(buf[:]); C.callGoStackCheck() } +func stack2772() { var buf [2772]byte; use(buf[:]); C.callGoStackCheck() } +func stack2776() { var buf [2776]byte; use(buf[:]); C.callGoStackCheck() } +func stack2780() { var buf [2780]byte; use(buf[:]); C.callGoStackCheck() } +func stack2784() { var buf [2784]byte; use(buf[:]); C.callGoStackCheck() } +func stack2788() { var buf [2788]byte; use(buf[:]); C.callGoStackCheck() } +func stack2792() { var buf [2792]byte; use(buf[:]); C.callGoStackCheck() } +func stack2796() { var buf [2796]byte; use(buf[:]); C.callGoStackCheck() } +func stack2800() { var buf [2800]byte; use(buf[:]); C.callGoStackCheck() } +func stack2804() { var buf [2804]byte; use(buf[:]); C.callGoStackCheck() } +func stack2808() { var buf [2808]byte; use(buf[:]); C.callGoStackCheck() } +func stack2812() { var buf [2812]byte; use(buf[:]); C.callGoStackCheck() } +func stack2816() { var buf [2816]byte; use(buf[:]); C.callGoStackCheck() } +func stack2820() { var buf [2820]byte; use(buf[:]); C.callGoStackCheck() } +func stack2824() { var buf [2824]byte; use(buf[:]); C.callGoStackCheck() } +func stack2828() { var buf [2828]byte; use(buf[:]); C.callGoStackCheck() } +func stack2832() { var buf [2832]byte; use(buf[:]); C.callGoStackCheck() } +func stack2836() { var buf [2836]byte; use(buf[:]); C.callGoStackCheck() } +func stack2840() { var buf [2840]byte; use(buf[:]); C.callGoStackCheck() } +func stack2844() { var buf [2844]byte; use(buf[:]); C.callGoStackCheck() } +func stack2848() { var buf [2848]byte; use(buf[:]); C.callGoStackCheck() } +func stack2852() { var buf [2852]byte; use(buf[:]); C.callGoStackCheck() } +func stack2856() { var buf [2856]byte; use(buf[:]); C.callGoStackCheck() } +func stack2860() { var buf [2860]byte; use(buf[:]); C.callGoStackCheck() } +func stack2864() { var buf [2864]byte; use(buf[:]); C.callGoStackCheck() } +func stack2868() { var buf [2868]byte; use(buf[:]); C.callGoStackCheck() } +func stack2872() { var buf [2872]byte; use(buf[:]); C.callGoStackCheck() } +func stack2876() { var buf [2876]byte; use(buf[:]); C.callGoStackCheck() } +func stack2880() { var buf [2880]byte; use(buf[:]); C.callGoStackCheck() } +func stack2884() { var buf [2884]byte; use(buf[:]); C.callGoStackCheck() } +func stack2888() { var buf [2888]byte; use(buf[:]); C.callGoStackCheck() } +func stack2892() { var buf [2892]byte; use(buf[:]); C.callGoStackCheck() } +func stack2896() { var buf [2896]byte; use(buf[:]); C.callGoStackCheck() } +func stack2900() { var buf [2900]byte; use(buf[:]); C.callGoStackCheck() } +func stack2904() { var buf [2904]byte; use(buf[:]); C.callGoStackCheck() } +func stack2908() { var buf [2908]byte; use(buf[:]); C.callGoStackCheck() } +func stack2912() { var buf [2912]byte; use(buf[:]); C.callGoStackCheck() } +func stack2916() { var buf [2916]byte; use(buf[:]); C.callGoStackCheck() } +func stack2920() { var buf [2920]byte; use(buf[:]); C.callGoStackCheck() } +func stack2924() { var buf [2924]byte; use(buf[:]); C.callGoStackCheck() } +func stack2928() { var buf [2928]byte; use(buf[:]); C.callGoStackCheck() } +func stack2932() { var buf [2932]byte; use(buf[:]); C.callGoStackCheck() } +func stack2936() { var buf [2936]byte; use(buf[:]); C.callGoStackCheck() } +func stack2940() { var buf [2940]byte; use(buf[:]); C.callGoStackCheck() } +func stack2944() { var buf [2944]byte; use(buf[:]); C.callGoStackCheck() } +func stack2948() { var buf [2948]byte; use(buf[:]); C.callGoStackCheck() } +func stack2952() { var buf [2952]byte; use(buf[:]); C.callGoStackCheck() } +func stack2956() { var buf [2956]byte; use(buf[:]); C.callGoStackCheck() } +func stack2960() { var buf [2960]byte; use(buf[:]); C.callGoStackCheck() } +func stack2964() { var buf [2964]byte; use(buf[:]); C.callGoStackCheck() } +func stack2968() { var buf [2968]byte; use(buf[:]); C.callGoStackCheck() } +func stack2972() { var buf [2972]byte; use(buf[:]); C.callGoStackCheck() } +func stack2976() { var buf [2976]byte; use(buf[:]); C.callGoStackCheck() } +func stack2980() { var buf [2980]byte; use(buf[:]); C.callGoStackCheck() } +func stack2984() { var buf [2984]byte; use(buf[:]); C.callGoStackCheck() } +func stack2988() { var buf [2988]byte; use(buf[:]); C.callGoStackCheck() } +func stack2992() { var buf [2992]byte; use(buf[:]); C.callGoStackCheck() } +func stack2996() { var buf [2996]byte; use(buf[:]); C.callGoStackCheck() } +func stack3000() { var buf [3000]byte; use(buf[:]); C.callGoStackCheck() } +func stack3004() { var buf [3004]byte; use(buf[:]); C.callGoStackCheck() } +func stack3008() { var buf [3008]byte; use(buf[:]); C.callGoStackCheck() } +func stack3012() { var buf [3012]byte; use(buf[:]); C.callGoStackCheck() } +func stack3016() { var buf [3016]byte; use(buf[:]); C.callGoStackCheck() } +func stack3020() { var buf [3020]byte; use(buf[:]); C.callGoStackCheck() } +func stack3024() { var buf [3024]byte; use(buf[:]); C.callGoStackCheck() } +func stack3028() { var buf [3028]byte; use(buf[:]); C.callGoStackCheck() } +func stack3032() { var buf [3032]byte; use(buf[:]); C.callGoStackCheck() } +func stack3036() { var buf [3036]byte; use(buf[:]); C.callGoStackCheck() } +func stack3040() { var buf [3040]byte; use(buf[:]); C.callGoStackCheck() } +func stack3044() { var buf [3044]byte; use(buf[:]); C.callGoStackCheck() } +func stack3048() { var buf [3048]byte; use(buf[:]); C.callGoStackCheck() } +func stack3052() { var buf [3052]byte; use(buf[:]); C.callGoStackCheck() } +func stack3056() { var buf [3056]byte; use(buf[:]); C.callGoStackCheck() } +func stack3060() { var buf [3060]byte; use(buf[:]); C.callGoStackCheck() } +func stack3064() { var buf [3064]byte; use(buf[:]); C.callGoStackCheck() } +func stack3068() { var buf [3068]byte; use(buf[:]); C.callGoStackCheck() } +func stack3072() { var buf [3072]byte; use(buf[:]); C.callGoStackCheck() } +func stack3076() { var buf [3076]byte; use(buf[:]); C.callGoStackCheck() } +func stack3080() { var buf [3080]byte; use(buf[:]); C.callGoStackCheck() } +func stack3084() { var buf [3084]byte; use(buf[:]); C.callGoStackCheck() } +func stack3088() { var buf [3088]byte; use(buf[:]); C.callGoStackCheck() } +func stack3092() { var buf [3092]byte; use(buf[:]); C.callGoStackCheck() } +func stack3096() { var buf [3096]byte; use(buf[:]); C.callGoStackCheck() } +func stack3100() { var buf [3100]byte; use(buf[:]); C.callGoStackCheck() } +func stack3104() { var buf [3104]byte; use(buf[:]); C.callGoStackCheck() } +func stack3108() { var buf [3108]byte; use(buf[:]); C.callGoStackCheck() } +func stack3112() { var buf [3112]byte; use(buf[:]); C.callGoStackCheck() } +func stack3116() { var buf [3116]byte; use(buf[:]); C.callGoStackCheck() } +func stack3120() { var buf [3120]byte; use(buf[:]); C.callGoStackCheck() } +func stack3124() { var buf [3124]byte; use(buf[:]); C.callGoStackCheck() } +func stack3128() { var buf [3128]byte; use(buf[:]); C.callGoStackCheck() } +func stack3132() { var buf [3132]byte; use(buf[:]); C.callGoStackCheck() } +func stack3136() { var buf [3136]byte; use(buf[:]); C.callGoStackCheck() } +func stack3140() { var buf [3140]byte; use(buf[:]); C.callGoStackCheck() } +func stack3144() { var buf [3144]byte; use(buf[:]); C.callGoStackCheck() } +func stack3148() { var buf [3148]byte; use(buf[:]); C.callGoStackCheck() } +func stack3152() { var buf [3152]byte; use(buf[:]); C.callGoStackCheck() } +func stack3156() { var buf [3156]byte; use(buf[:]); C.callGoStackCheck() } +func stack3160() { var buf [3160]byte; use(buf[:]); C.callGoStackCheck() } +func stack3164() { var buf [3164]byte; use(buf[:]); C.callGoStackCheck() } +func stack3168() { var buf [3168]byte; use(buf[:]); C.callGoStackCheck() } +func stack3172() { var buf [3172]byte; use(buf[:]); C.callGoStackCheck() } +func stack3176() { var buf [3176]byte; use(buf[:]); C.callGoStackCheck() } +func stack3180() { var buf [3180]byte; use(buf[:]); C.callGoStackCheck() } +func stack3184() { var buf [3184]byte; use(buf[:]); C.callGoStackCheck() } +func stack3188() { var buf [3188]byte; use(buf[:]); C.callGoStackCheck() } +func stack3192() { var buf [3192]byte; use(buf[:]); C.callGoStackCheck() } +func stack3196() { var buf [3196]byte; use(buf[:]); C.callGoStackCheck() } +func stack3200() { var buf [3200]byte; use(buf[:]); C.callGoStackCheck() } +func stack3204() { var buf [3204]byte; use(buf[:]); C.callGoStackCheck() } +func stack3208() { var buf [3208]byte; use(buf[:]); C.callGoStackCheck() } +func stack3212() { var buf [3212]byte; use(buf[:]); C.callGoStackCheck() } +func stack3216() { var buf [3216]byte; use(buf[:]); C.callGoStackCheck() } +func stack3220() { var buf [3220]byte; use(buf[:]); C.callGoStackCheck() } +func stack3224() { var buf [3224]byte; use(buf[:]); C.callGoStackCheck() } +func stack3228() { var buf [3228]byte; use(buf[:]); C.callGoStackCheck() } +func stack3232() { var buf [3232]byte; use(buf[:]); C.callGoStackCheck() } +func stack3236() { var buf [3236]byte; use(buf[:]); C.callGoStackCheck() } +func stack3240() { var buf [3240]byte; use(buf[:]); C.callGoStackCheck() } +func stack3244() { var buf [3244]byte; use(buf[:]); C.callGoStackCheck() } +func stack3248() { var buf [3248]byte; use(buf[:]); C.callGoStackCheck() } +func stack3252() { var buf [3252]byte; use(buf[:]); C.callGoStackCheck() } +func stack3256() { var buf [3256]byte; use(buf[:]); C.callGoStackCheck() } +func stack3260() { var buf [3260]byte; use(buf[:]); C.callGoStackCheck() } +func stack3264() { var buf [3264]byte; use(buf[:]); C.callGoStackCheck() } +func stack3268() { var buf [3268]byte; use(buf[:]); C.callGoStackCheck() } +func stack3272() { var buf [3272]byte; use(buf[:]); C.callGoStackCheck() } +func stack3276() { var buf [3276]byte; use(buf[:]); C.callGoStackCheck() } +func stack3280() { var buf [3280]byte; use(buf[:]); C.callGoStackCheck() } +func stack3284() { var buf [3284]byte; use(buf[:]); C.callGoStackCheck() } +func stack3288() { var buf [3288]byte; use(buf[:]); C.callGoStackCheck() } +func stack3292() { var buf [3292]byte; use(buf[:]); C.callGoStackCheck() } +func stack3296() { var buf [3296]byte; use(buf[:]); C.callGoStackCheck() } +func stack3300() { var buf [3300]byte; use(buf[:]); C.callGoStackCheck() } +func stack3304() { var buf [3304]byte; use(buf[:]); C.callGoStackCheck() } +func stack3308() { var buf [3308]byte; use(buf[:]); C.callGoStackCheck() } +func stack3312() { var buf [3312]byte; use(buf[:]); C.callGoStackCheck() } +func stack3316() { var buf [3316]byte; use(buf[:]); C.callGoStackCheck() } +func stack3320() { var buf [3320]byte; use(buf[:]); C.callGoStackCheck() } +func stack3324() { var buf [3324]byte; use(buf[:]); C.callGoStackCheck() } +func stack3328() { var buf [3328]byte; use(buf[:]); C.callGoStackCheck() } +func stack3332() { var buf [3332]byte; use(buf[:]); C.callGoStackCheck() } +func stack3336() { var buf [3336]byte; use(buf[:]); C.callGoStackCheck() } +func stack3340() { var buf [3340]byte; use(buf[:]); C.callGoStackCheck() } +func stack3344() { var buf [3344]byte; use(buf[:]); C.callGoStackCheck() } +func stack3348() { var buf [3348]byte; use(buf[:]); C.callGoStackCheck() } +func stack3352() { var buf [3352]byte; use(buf[:]); C.callGoStackCheck() } +func stack3356() { var buf [3356]byte; use(buf[:]); C.callGoStackCheck() } +func stack3360() { var buf [3360]byte; use(buf[:]); C.callGoStackCheck() } +func stack3364() { var buf [3364]byte; use(buf[:]); C.callGoStackCheck() } +func stack3368() { var buf [3368]byte; use(buf[:]); C.callGoStackCheck() } +func stack3372() { var buf [3372]byte; use(buf[:]); C.callGoStackCheck() } +func stack3376() { var buf [3376]byte; use(buf[:]); C.callGoStackCheck() } +func stack3380() { var buf [3380]byte; use(buf[:]); C.callGoStackCheck() } +func stack3384() { var buf [3384]byte; use(buf[:]); C.callGoStackCheck() } +func stack3388() { var buf [3388]byte; use(buf[:]); C.callGoStackCheck() } +func stack3392() { var buf [3392]byte; use(buf[:]); C.callGoStackCheck() } +func stack3396() { var buf [3396]byte; use(buf[:]); C.callGoStackCheck() } +func stack3400() { var buf [3400]byte; use(buf[:]); C.callGoStackCheck() } +func stack3404() { var buf [3404]byte; use(buf[:]); C.callGoStackCheck() } +func stack3408() { var buf [3408]byte; use(buf[:]); C.callGoStackCheck() } +func stack3412() { var buf [3412]byte; use(buf[:]); C.callGoStackCheck() } +func stack3416() { var buf [3416]byte; use(buf[:]); C.callGoStackCheck() } +func stack3420() { var buf [3420]byte; use(buf[:]); C.callGoStackCheck() } +func stack3424() { var buf [3424]byte; use(buf[:]); C.callGoStackCheck() } +func stack3428() { var buf [3428]byte; use(buf[:]); C.callGoStackCheck() } +func stack3432() { var buf [3432]byte; use(buf[:]); C.callGoStackCheck() } +func stack3436() { var buf [3436]byte; use(buf[:]); C.callGoStackCheck() } +func stack3440() { var buf [3440]byte; use(buf[:]); C.callGoStackCheck() } +func stack3444() { var buf [3444]byte; use(buf[:]); C.callGoStackCheck() } +func stack3448() { var buf [3448]byte; use(buf[:]); C.callGoStackCheck() } +func stack3452() { var buf [3452]byte; use(buf[:]); C.callGoStackCheck() } +func stack3456() { var buf [3456]byte; use(buf[:]); C.callGoStackCheck() } +func stack3460() { var buf [3460]byte; use(buf[:]); C.callGoStackCheck() } +func stack3464() { var buf [3464]byte; use(buf[:]); C.callGoStackCheck() } +func stack3468() { var buf [3468]byte; use(buf[:]); C.callGoStackCheck() } +func stack3472() { var buf [3472]byte; use(buf[:]); C.callGoStackCheck() } +func stack3476() { var buf [3476]byte; use(buf[:]); C.callGoStackCheck() } +func stack3480() { var buf [3480]byte; use(buf[:]); C.callGoStackCheck() } +func stack3484() { var buf [3484]byte; use(buf[:]); C.callGoStackCheck() } +func stack3488() { var buf [3488]byte; use(buf[:]); C.callGoStackCheck() } +func stack3492() { var buf [3492]byte; use(buf[:]); C.callGoStackCheck() } +func stack3496() { var buf [3496]byte; use(buf[:]); C.callGoStackCheck() } +func stack3500() { var buf [3500]byte; use(buf[:]); C.callGoStackCheck() } +func stack3504() { var buf [3504]byte; use(buf[:]); C.callGoStackCheck() } +func stack3508() { var buf [3508]byte; use(buf[:]); C.callGoStackCheck() } +func stack3512() { var buf [3512]byte; use(buf[:]); C.callGoStackCheck() } +func stack3516() { var buf [3516]byte; use(buf[:]); C.callGoStackCheck() } +func stack3520() { var buf [3520]byte; use(buf[:]); C.callGoStackCheck() } +func stack3524() { var buf [3524]byte; use(buf[:]); C.callGoStackCheck() } +func stack3528() { var buf [3528]byte; use(buf[:]); C.callGoStackCheck() } +func stack3532() { var buf [3532]byte; use(buf[:]); C.callGoStackCheck() } +func stack3536() { var buf [3536]byte; use(buf[:]); C.callGoStackCheck() } +func stack3540() { var buf [3540]byte; use(buf[:]); C.callGoStackCheck() } +func stack3544() { var buf [3544]byte; use(buf[:]); C.callGoStackCheck() } +func stack3548() { var buf [3548]byte; use(buf[:]); C.callGoStackCheck() } +func stack3552() { var buf [3552]byte; use(buf[:]); C.callGoStackCheck() } +func stack3556() { var buf [3556]byte; use(buf[:]); C.callGoStackCheck() } +func stack3560() { var buf [3560]byte; use(buf[:]); C.callGoStackCheck() } +func stack3564() { var buf [3564]byte; use(buf[:]); C.callGoStackCheck() } +func stack3568() { var buf [3568]byte; use(buf[:]); C.callGoStackCheck() } +func stack3572() { var buf [3572]byte; use(buf[:]); C.callGoStackCheck() } +func stack3576() { var buf [3576]byte; use(buf[:]); C.callGoStackCheck() } +func stack3580() { var buf [3580]byte; use(buf[:]); C.callGoStackCheck() } +func stack3584() { var buf [3584]byte; use(buf[:]); C.callGoStackCheck() } +func stack3588() { var buf [3588]byte; use(buf[:]); C.callGoStackCheck() } +func stack3592() { var buf [3592]byte; use(buf[:]); C.callGoStackCheck() } +func stack3596() { var buf [3596]byte; use(buf[:]); C.callGoStackCheck() } +func stack3600() { var buf [3600]byte; use(buf[:]); C.callGoStackCheck() } +func stack3604() { var buf [3604]byte; use(buf[:]); C.callGoStackCheck() } +func stack3608() { var buf [3608]byte; use(buf[:]); C.callGoStackCheck() } +func stack3612() { var buf [3612]byte; use(buf[:]); C.callGoStackCheck() } +func stack3616() { var buf [3616]byte; use(buf[:]); C.callGoStackCheck() } +func stack3620() { var buf [3620]byte; use(buf[:]); C.callGoStackCheck() } +func stack3624() { var buf [3624]byte; use(buf[:]); C.callGoStackCheck() } +func stack3628() { var buf [3628]byte; use(buf[:]); C.callGoStackCheck() } +func stack3632() { var buf [3632]byte; use(buf[:]); C.callGoStackCheck() } +func stack3636() { var buf [3636]byte; use(buf[:]); C.callGoStackCheck() } +func stack3640() { var buf [3640]byte; use(buf[:]); C.callGoStackCheck() } +func stack3644() { var buf [3644]byte; use(buf[:]); C.callGoStackCheck() } +func stack3648() { var buf [3648]byte; use(buf[:]); C.callGoStackCheck() } +func stack3652() { var buf [3652]byte; use(buf[:]); C.callGoStackCheck() } +func stack3656() { var buf [3656]byte; use(buf[:]); C.callGoStackCheck() } +func stack3660() { var buf [3660]byte; use(buf[:]); C.callGoStackCheck() } +func stack3664() { var buf [3664]byte; use(buf[:]); C.callGoStackCheck() } +func stack3668() { var buf [3668]byte; use(buf[:]); C.callGoStackCheck() } +func stack3672() { var buf [3672]byte; use(buf[:]); C.callGoStackCheck() } +func stack3676() { var buf [3676]byte; use(buf[:]); C.callGoStackCheck() } +func stack3680() { var buf [3680]byte; use(buf[:]); C.callGoStackCheck() } +func stack3684() { var buf [3684]byte; use(buf[:]); C.callGoStackCheck() } +func stack3688() { var buf [3688]byte; use(buf[:]); C.callGoStackCheck() } +func stack3692() { var buf [3692]byte; use(buf[:]); C.callGoStackCheck() } +func stack3696() { var buf [3696]byte; use(buf[:]); C.callGoStackCheck() } +func stack3700() { var buf [3700]byte; use(buf[:]); C.callGoStackCheck() } +func stack3704() { var buf [3704]byte; use(buf[:]); C.callGoStackCheck() } +func stack3708() { var buf [3708]byte; use(buf[:]); C.callGoStackCheck() } +func stack3712() { var buf [3712]byte; use(buf[:]); C.callGoStackCheck() } +func stack3716() { var buf [3716]byte; use(buf[:]); C.callGoStackCheck() } +func stack3720() { var buf [3720]byte; use(buf[:]); C.callGoStackCheck() } +func stack3724() { var buf [3724]byte; use(buf[:]); C.callGoStackCheck() } +func stack3728() { var buf [3728]byte; use(buf[:]); C.callGoStackCheck() } +func stack3732() { var buf [3732]byte; use(buf[:]); C.callGoStackCheck() } +func stack3736() { var buf [3736]byte; use(buf[:]); C.callGoStackCheck() } +func stack3740() { var buf [3740]byte; use(buf[:]); C.callGoStackCheck() } +func stack3744() { var buf [3744]byte; use(buf[:]); C.callGoStackCheck() } +func stack3748() { var buf [3748]byte; use(buf[:]); C.callGoStackCheck() } +func stack3752() { var buf [3752]byte; use(buf[:]); C.callGoStackCheck() } +func stack3756() { var buf [3756]byte; use(buf[:]); C.callGoStackCheck() } +func stack3760() { var buf [3760]byte; use(buf[:]); C.callGoStackCheck() } +func stack3764() { var buf [3764]byte; use(buf[:]); C.callGoStackCheck() } +func stack3768() { var buf [3768]byte; use(buf[:]); C.callGoStackCheck() } +func stack3772() { var buf [3772]byte; use(buf[:]); C.callGoStackCheck() } +func stack3776() { var buf [3776]byte; use(buf[:]); C.callGoStackCheck() } +func stack3780() { var buf [3780]byte; use(buf[:]); C.callGoStackCheck() } +func stack3784() { var buf [3784]byte; use(buf[:]); C.callGoStackCheck() } +func stack3788() { var buf [3788]byte; use(buf[:]); C.callGoStackCheck() } +func stack3792() { var buf [3792]byte; use(buf[:]); C.callGoStackCheck() } +func stack3796() { var buf [3796]byte; use(buf[:]); C.callGoStackCheck() } +func stack3800() { var buf [3800]byte; use(buf[:]); C.callGoStackCheck() } +func stack3804() { var buf [3804]byte; use(buf[:]); C.callGoStackCheck() } +func stack3808() { var buf [3808]byte; use(buf[:]); C.callGoStackCheck() } +func stack3812() { var buf [3812]byte; use(buf[:]); C.callGoStackCheck() } +func stack3816() { var buf [3816]byte; use(buf[:]); C.callGoStackCheck() } +func stack3820() { var buf [3820]byte; use(buf[:]); C.callGoStackCheck() } +func stack3824() { var buf [3824]byte; use(buf[:]); C.callGoStackCheck() } +func stack3828() { var buf [3828]byte; use(buf[:]); C.callGoStackCheck() } +func stack3832() { var buf [3832]byte; use(buf[:]); C.callGoStackCheck() } +func stack3836() { var buf [3836]byte; use(buf[:]); C.callGoStackCheck() } +func stack3840() { var buf [3840]byte; use(buf[:]); C.callGoStackCheck() } +func stack3844() { var buf [3844]byte; use(buf[:]); C.callGoStackCheck() } +func stack3848() { var buf [3848]byte; use(buf[:]); C.callGoStackCheck() } +func stack3852() { var buf [3852]byte; use(buf[:]); C.callGoStackCheck() } +func stack3856() { var buf [3856]byte; use(buf[:]); C.callGoStackCheck() } +func stack3860() { var buf [3860]byte; use(buf[:]); C.callGoStackCheck() } +func stack3864() { var buf [3864]byte; use(buf[:]); C.callGoStackCheck() } +func stack3868() { var buf [3868]byte; use(buf[:]); C.callGoStackCheck() } +func stack3872() { var buf [3872]byte; use(buf[:]); C.callGoStackCheck() } +func stack3876() { var buf [3876]byte; use(buf[:]); C.callGoStackCheck() } +func stack3880() { var buf [3880]byte; use(buf[:]); C.callGoStackCheck() } +func stack3884() { var buf [3884]byte; use(buf[:]); C.callGoStackCheck() } +func stack3888() { var buf [3888]byte; use(buf[:]); C.callGoStackCheck() } +func stack3892() { var buf [3892]byte; use(buf[:]); C.callGoStackCheck() } +func stack3896() { var buf [3896]byte; use(buf[:]); C.callGoStackCheck() } +func stack3900() { var buf [3900]byte; use(buf[:]); C.callGoStackCheck() } +func stack3904() { var buf [3904]byte; use(buf[:]); C.callGoStackCheck() } +func stack3908() { var buf [3908]byte; use(buf[:]); C.callGoStackCheck() } +func stack3912() { var buf [3912]byte; use(buf[:]); C.callGoStackCheck() } +func stack3916() { var buf [3916]byte; use(buf[:]); C.callGoStackCheck() } +func stack3920() { var buf [3920]byte; use(buf[:]); C.callGoStackCheck() } +func stack3924() { var buf [3924]byte; use(buf[:]); C.callGoStackCheck() } +func stack3928() { var buf [3928]byte; use(buf[:]); C.callGoStackCheck() } +func stack3932() { var buf [3932]byte; use(buf[:]); C.callGoStackCheck() } +func stack3936() { var buf [3936]byte; use(buf[:]); C.callGoStackCheck() } +func stack3940() { var buf [3940]byte; use(buf[:]); C.callGoStackCheck() } +func stack3944() { var buf [3944]byte; use(buf[:]); C.callGoStackCheck() } +func stack3948() { var buf [3948]byte; use(buf[:]); C.callGoStackCheck() } +func stack3952() { var buf [3952]byte; use(buf[:]); C.callGoStackCheck() } +func stack3956() { var buf [3956]byte; use(buf[:]); C.callGoStackCheck() } +func stack3960() { var buf [3960]byte; use(buf[:]); C.callGoStackCheck() } +func stack3964() { var buf [3964]byte; use(buf[:]); C.callGoStackCheck() } +func stack3968() { var buf [3968]byte; use(buf[:]); C.callGoStackCheck() } +func stack3972() { var buf [3972]byte; use(buf[:]); C.callGoStackCheck() } +func stack3976() { var buf [3976]byte; use(buf[:]); C.callGoStackCheck() } +func stack3980() { var buf [3980]byte; use(buf[:]); C.callGoStackCheck() } +func stack3984() { var buf [3984]byte; use(buf[:]); C.callGoStackCheck() } +func stack3988() { var buf [3988]byte; use(buf[:]); C.callGoStackCheck() } +func stack3992() { var buf [3992]byte; use(buf[:]); C.callGoStackCheck() } +func stack3996() { var buf [3996]byte; use(buf[:]); C.callGoStackCheck() } +func stack4000() { var buf [4000]byte; use(buf[:]); C.callGoStackCheck() } +func stack4004() { var buf [4004]byte; use(buf[:]); C.callGoStackCheck() } +func stack4008() { var buf [4008]byte; use(buf[:]); C.callGoStackCheck() } +func stack4012() { var buf [4012]byte; use(buf[:]); C.callGoStackCheck() } +func stack4016() { var buf [4016]byte; use(buf[:]); C.callGoStackCheck() } +func stack4020() { var buf [4020]byte; use(buf[:]); C.callGoStackCheck() } +func stack4024() { var buf [4024]byte; use(buf[:]); C.callGoStackCheck() } +func stack4028() { var buf [4028]byte; use(buf[:]); C.callGoStackCheck() } +func stack4032() { var buf [4032]byte; use(buf[:]); C.callGoStackCheck() } +func stack4036() { var buf [4036]byte; use(buf[:]); C.callGoStackCheck() } +func stack4040() { var buf [4040]byte; use(buf[:]); C.callGoStackCheck() } +func stack4044() { var buf [4044]byte; use(buf[:]); C.callGoStackCheck() } +func stack4048() { var buf [4048]byte; use(buf[:]); C.callGoStackCheck() } +func stack4052() { var buf [4052]byte; use(buf[:]); C.callGoStackCheck() } +func stack4056() { var buf [4056]byte; use(buf[:]); C.callGoStackCheck() } +func stack4060() { var buf [4060]byte; use(buf[:]); C.callGoStackCheck() } +func stack4064() { var buf [4064]byte; use(buf[:]); C.callGoStackCheck() } +func stack4068() { var buf [4068]byte; use(buf[:]); C.callGoStackCheck() } +func stack4072() { var buf [4072]byte; use(buf[:]); C.callGoStackCheck() } +func stack4076() { var buf [4076]byte; use(buf[:]); C.callGoStackCheck() } +func stack4080() { var buf [4080]byte; use(buf[:]); C.callGoStackCheck() } +func stack4084() { var buf [4084]byte; use(buf[:]); C.callGoStackCheck() } +func stack4088() { var buf [4088]byte; use(buf[:]); C.callGoStackCheck() } +func stack4092() { var buf [4092]byte; use(buf[:]); C.callGoStackCheck() } +func stack4096() { var buf [4096]byte; use(buf[:]); C.callGoStackCheck() } +func stack4100() { var buf [4100]byte; use(buf[:]); C.callGoStackCheck() } +func stack4104() { var buf [4104]byte; use(buf[:]); C.callGoStackCheck() } +func stack4108() { var buf [4108]byte; use(buf[:]); C.callGoStackCheck() } +func stack4112() { var buf [4112]byte; use(buf[:]); C.callGoStackCheck() } +func stack4116() { var buf [4116]byte; use(buf[:]); C.callGoStackCheck() } +func stack4120() { var buf [4120]byte; use(buf[:]); C.callGoStackCheck() } +func stack4124() { var buf [4124]byte; use(buf[:]); C.callGoStackCheck() } +func stack4128() { var buf [4128]byte; use(buf[:]); C.callGoStackCheck() } +func stack4132() { var buf [4132]byte; use(buf[:]); C.callGoStackCheck() } +func stack4136() { var buf [4136]byte; use(buf[:]); C.callGoStackCheck() } +func stack4140() { var buf [4140]byte; use(buf[:]); C.callGoStackCheck() } +func stack4144() { var buf [4144]byte; use(buf[:]); C.callGoStackCheck() } +func stack4148() { var buf [4148]byte; use(buf[:]); C.callGoStackCheck() } +func stack4152() { var buf [4152]byte; use(buf[:]); C.callGoStackCheck() } +func stack4156() { var buf [4156]byte; use(buf[:]); C.callGoStackCheck() } +func stack4160() { var buf [4160]byte; use(buf[:]); C.callGoStackCheck() } +func stack4164() { var buf [4164]byte; use(buf[:]); C.callGoStackCheck() } +func stack4168() { var buf [4168]byte; use(buf[:]); C.callGoStackCheck() } +func stack4172() { var buf [4172]byte; use(buf[:]); C.callGoStackCheck() } +func stack4176() { var buf [4176]byte; use(buf[:]); C.callGoStackCheck() } +func stack4180() { var buf [4180]byte; use(buf[:]); C.callGoStackCheck() } +func stack4184() { var buf [4184]byte; use(buf[:]); C.callGoStackCheck() } +func stack4188() { var buf [4188]byte; use(buf[:]); C.callGoStackCheck() } +func stack4192() { var buf [4192]byte; use(buf[:]); C.callGoStackCheck() } +func stack4196() { var buf [4196]byte; use(buf[:]); C.callGoStackCheck() } +func stack4200() { var buf [4200]byte; use(buf[:]); C.callGoStackCheck() } +func stack4204() { var buf [4204]byte; use(buf[:]); C.callGoStackCheck() } +func stack4208() { var buf [4208]byte; use(buf[:]); C.callGoStackCheck() } +func stack4212() { var buf [4212]byte; use(buf[:]); C.callGoStackCheck() } +func stack4216() { var buf [4216]byte; use(buf[:]); C.callGoStackCheck() } +func stack4220() { var buf [4220]byte; use(buf[:]); C.callGoStackCheck() } +func stack4224() { var buf [4224]byte; use(buf[:]); C.callGoStackCheck() } +func stack4228() { var buf [4228]byte; use(buf[:]); C.callGoStackCheck() } +func stack4232() { var buf [4232]byte; use(buf[:]); C.callGoStackCheck() } +func stack4236() { var buf [4236]byte; use(buf[:]); C.callGoStackCheck() } +func stack4240() { var buf [4240]byte; use(buf[:]); C.callGoStackCheck() } +func stack4244() { var buf [4244]byte; use(buf[:]); C.callGoStackCheck() } +func stack4248() { var buf [4248]byte; use(buf[:]); C.callGoStackCheck() } +func stack4252() { var buf [4252]byte; use(buf[:]); C.callGoStackCheck() } +func stack4256() { var buf [4256]byte; use(buf[:]); C.callGoStackCheck() } +func stack4260() { var buf [4260]byte; use(buf[:]); C.callGoStackCheck() } +func stack4264() { var buf [4264]byte; use(buf[:]); C.callGoStackCheck() } +func stack4268() { var buf [4268]byte; use(buf[:]); C.callGoStackCheck() } +func stack4272() { var buf [4272]byte; use(buf[:]); C.callGoStackCheck() } +func stack4276() { var buf [4276]byte; use(buf[:]); C.callGoStackCheck() } +func stack4280() { var buf [4280]byte; use(buf[:]); C.callGoStackCheck() } +func stack4284() { var buf [4284]byte; use(buf[:]); C.callGoStackCheck() } +func stack4288() { var buf [4288]byte; use(buf[:]); C.callGoStackCheck() } +func stack4292() { var buf [4292]byte; use(buf[:]); C.callGoStackCheck() } +func stack4296() { var buf [4296]byte; use(buf[:]); C.callGoStackCheck() } +func stack4300() { var buf [4300]byte; use(buf[:]); C.callGoStackCheck() } +func stack4304() { var buf [4304]byte; use(buf[:]); C.callGoStackCheck() } +func stack4308() { var buf [4308]byte; use(buf[:]); C.callGoStackCheck() } +func stack4312() { var buf [4312]byte; use(buf[:]); C.callGoStackCheck() } +func stack4316() { var buf [4316]byte; use(buf[:]); C.callGoStackCheck() } +func stack4320() { var buf [4320]byte; use(buf[:]); C.callGoStackCheck() } +func stack4324() { var buf [4324]byte; use(buf[:]); C.callGoStackCheck() } +func stack4328() { var buf [4328]byte; use(buf[:]); C.callGoStackCheck() } +func stack4332() { var buf [4332]byte; use(buf[:]); C.callGoStackCheck() } +func stack4336() { var buf [4336]byte; use(buf[:]); C.callGoStackCheck() } +func stack4340() { var buf [4340]byte; use(buf[:]); C.callGoStackCheck() } +func stack4344() { var buf [4344]byte; use(buf[:]); C.callGoStackCheck() } +func stack4348() { var buf [4348]byte; use(buf[:]); C.callGoStackCheck() } +func stack4352() { var buf [4352]byte; use(buf[:]); C.callGoStackCheck() } +func stack4356() { var buf [4356]byte; use(buf[:]); C.callGoStackCheck() } +func stack4360() { var buf [4360]byte; use(buf[:]); C.callGoStackCheck() } +func stack4364() { var buf [4364]byte; use(buf[:]); C.callGoStackCheck() } +func stack4368() { var buf [4368]byte; use(buf[:]); C.callGoStackCheck() } +func stack4372() { var buf [4372]byte; use(buf[:]); C.callGoStackCheck() } +func stack4376() { var buf [4376]byte; use(buf[:]); C.callGoStackCheck() } +func stack4380() { var buf [4380]byte; use(buf[:]); C.callGoStackCheck() } +func stack4384() { var buf [4384]byte; use(buf[:]); C.callGoStackCheck() } +func stack4388() { var buf [4388]byte; use(buf[:]); C.callGoStackCheck() } +func stack4392() { var buf [4392]byte; use(buf[:]); C.callGoStackCheck() } +func stack4396() { var buf [4396]byte; use(buf[:]); C.callGoStackCheck() } +func stack4400() { var buf [4400]byte; use(buf[:]); C.callGoStackCheck() } +func stack4404() { var buf [4404]byte; use(buf[:]); C.callGoStackCheck() } +func stack4408() { var buf [4408]byte; use(buf[:]); C.callGoStackCheck() } +func stack4412() { var buf [4412]byte; use(buf[:]); C.callGoStackCheck() } +func stack4416() { var buf [4416]byte; use(buf[:]); C.callGoStackCheck() } +func stack4420() { var buf [4420]byte; use(buf[:]); C.callGoStackCheck() } +func stack4424() { var buf [4424]byte; use(buf[:]); C.callGoStackCheck() } +func stack4428() { var buf [4428]byte; use(buf[:]); C.callGoStackCheck() } +func stack4432() { var buf [4432]byte; use(buf[:]); C.callGoStackCheck() } +func stack4436() { var buf [4436]byte; use(buf[:]); C.callGoStackCheck() } +func stack4440() { var buf [4440]byte; use(buf[:]); C.callGoStackCheck() } +func stack4444() { var buf [4444]byte; use(buf[:]); C.callGoStackCheck() } +func stack4448() { var buf [4448]byte; use(buf[:]); C.callGoStackCheck() } +func stack4452() { var buf [4452]byte; use(buf[:]); C.callGoStackCheck() } +func stack4456() { var buf [4456]byte; use(buf[:]); C.callGoStackCheck() } +func stack4460() { var buf [4460]byte; use(buf[:]); C.callGoStackCheck() } +func stack4464() { var buf [4464]byte; use(buf[:]); C.callGoStackCheck() } +func stack4468() { var buf [4468]byte; use(buf[:]); C.callGoStackCheck() } +func stack4472() { var buf [4472]byte; use(buf[:]); C.callGoStackCheck() } +func stack4476() { var buf [4476]byte; use(buf[:]); C.callGoStackCheck() } +func stack4480() { var buf [4480]byte; use(buf[:]); C.callGoStackCheck() } +func stack4484() { var buf [4484]byte; use(buf[:]); C.callGoStackCheck() } +func stack4488() { var buf [4488]byte; use(buf[:]); C.callGoStackCheck() } +func stack4492() { var buf [4492]byte; use(buf[:]); C.callGoStackCheck() } +func stack4496() { var buf [4496]byte; use(buf[:]); C.callGoStackCheck() } +func stack4500() { var buf [4500]byte; use(buf[:]); C.callGoStackCheck() } +func stack4504() { var buf [4504]byte; use(buf[:]); C.callGoStackCheck() } +func stack4508() { var buf [4508]byte; use(buf[:]); C.callGoStackCheck() } +func stack4512() { var buf [4512]byte; use(buf[:]); C.callGoStackCheck() } +func stack4516() { var buf [4516]byte; use(buf[:]); C.callGoStackCheck() } +func stack4520() { var buf [4520]byte; use(buf[:]); C.callGoStackCheck() } +func stack4524() { var buf [4524]byte; use(buf[:]); C.callGoStackCheck() } +func stack4528() { var buf [4528]byte; use(buf[:]); C.callGoStackCheck() } +func stack4532() { var buf [4532]byte; use(buf[:]); C.callGoStackCheck() } +func stack4536() { var buf [4536]byte; use(buf[:]); C.callGoStackCheck() } +func stack4540() { var buf [4540]byte; use(buf[:]); C.callGoStackCheck() } +func stack4544() { var buf [4544]byte; use(buf[:]); C.callGoStackCheck() } +func stack4548() { var buf [4548]byte; use(buf[:]); C.callGoStackCheck() } +func stack4552() { var buf [4552]byte; use(buf[:]); C.callGoStackCheck() } +func stack4556() { var buf [4556]byte; use(buf[:]); C.callGoStackCheck() } +func stack4560() { var buf [4560]byte; use(buf[:]); C.callGoStackCheck() } +func stack4564() { var buf [4564]byte; use(buf[:]); C.callGoStackCheck() } +func stack4568() { var buf [4568]byte; use(buf[:]); C.callGoStackCheck() } +func stack4572() { var buf [4572]byte; use(buf[:]); C.callGoStackCheck() } +func stack4576() { var buf [4576]byte; use(buf[:]); C.callGoStackCheck() } +func stack4580() { var buf [4580]byte; use(buf[:]); C.callGoStackCheck() } +func stack4584() { var buf [4584]byte; use(buf[:]); C.callGoStackCheck() } +func stack4588() { var buf [4588]byte; use(buf[:]); C.callGoStackCheck() } +func stack4592() { var buf [4592]byte; use(buf[:]); C.callGoStackCheck() } +func stack4596() { var buf [4596]byte; use(buf[:]); C.callGoStackCheck() } +func stack4600() { var buf [4600]byte; use(buf[:]); C.callGoStackCheck() } +func stack4604() { var buf [4604]byte; use(buf[:]); C.callGoStackCheck() } +func stack4608() { var buf [4608]byte; use(buf[:]); C.callGoStackCheck() } +func stack4612() { var buf [4612]byte; use(buf[:]); C.callGoStackCheck() } +func stack4616() { var buf [4616]byte; use(buf[:]); C.callGoStackCheck() } +func stack4620() { var buf [4620]byte; use(buf[:]); C.callGoStackCheck() } +func stack4624() { var buf [4624]byte; use(buf[:]); C.callGoStackCheck() } +func stack4628() { var buf [4628]byte; use(buf[:]); C.callGoStackCheck() } +func stack4632() { var buf [4632]byte; use(buf[:]); C.callGoStackCheck() } +func stack4636() { var buf [4636]byte; use(buf[:]); C.callGoStackCheck() } +func stack4640() { var buf [4640]byte; use(buf[:]); C.callGoStackCheck() } +func stack4644() { var buf [4644]byte; use(buf[:]); C.callGoStackCheck() } +func stack4648() { var buf [4648]byte; use(buf[:]); C.callGoStackCheck() } +func stack4652() { var buf [4652]byte; use(buf[:]); C.callGoStackCheck() } +func stack4656() { var buf [4656]byte; use(buf[:]); C.callGoStackCheck() } +func stack4660() { var buf [4660]byte; use(buf[:]); C.callGoStackCheck() } +func stack4664() { var buf [4664]byte; use(buf[:]); C.callGoStackCheck() } +func stack4668() { var buf [4668]byte; use(buf[:]); C.callGoStackCheck() } +func stack4672() { var buf [4672]byte; use(buf[:]); C.callGoStackCheck() } +func stack4676() { var buf [4676]byte; use(buf[:]); C.callGoStackCheck() } +func stack4680() { var buf [4680]byte; use(buf[:]); C.callGoStackCheck() } +func stack4684() { var buf [4684]byte; use(buf[:]); C.callGoStackCheck() } +func stack4688() { var buf [4688]byte; use(buf[:]); C.callGoStackCheck() } +func stack4692() { var buf [4692]byte; use(buf[:]); C.callGoStackCheck() } +func stack4696() { var buf [4696]byte; use(buf[:]); C.callGoStackCheck() } +func stack4700() { var buf [4700]byte; use(buf[:]); C.callGoStackCheck() } +func stack4704() { var buf [4704]byte; use(buf[:]); C.callGoStackCheck() } +func stack4708() { var buf [4708]byte; use(buf[:]); C.callGoStackCheck() } +func stack4712() { var buf [4712]byte; use(buf[:]); C.callGoStackCheck() } +func stack4716() { var buf [4716]byte; use(buf[:]); C.callGoStackCheck() } +func stack4720() { var buf [4720]byte; use(buf[:]); C.callGoStackCheck() } +func stack4724() { var buf [4724]byte; use(buf[:]); C.callGoStackCheck() } +func stack4728() { var buf [4728]byte; use(buf[:]); C.callGoStackCheck() } +func stack4732() { var buf [4732]byte; use(buf[:]); C.callGoStackCheck() } +func stack4736() { var buf [4736]byte; use(buf[:]); C.callGoStackCheck() } +func stack4740() { var buf [4740]byte; use(buf[:]); C.callGoStackCheck() } +func stack4744() { var buf [4744]byte; use(buf[:]); C.callGoStackCheck() } +func stack4748() { var buf [4748]byte; use(buf[:]); C.callGoStackCheck() } +func stack4752() { var buf [4752]byte; use(buf[:]); C.callGoStackCheck() } +func stack4756() { var buf [4756]byte; use(buf[:]); C.callGoStackCheck() } +func stack4760() { var buf [4760]byte; use(buf[:]); C.callGoStackCheck() } +func stack4764() { var buf [4764]byte; use(buf[:]); C.callGoStackCheck() } +func stack4768() { var buf [4768]byte; use(buf[:]); C.callGoStackCheck() } +func stack4772() { var buf [4772]byte; use(buf[:]); C.callGoStackCheck() } +func stack4776() { var buf [4776]byte; use(buf[:]); C.callGoStackCheck() } +func stack4780() { var buf [4780]byte; use(buf[:]); C.callGoStackCheck() } +func stack4784() { var buf [4784]byte; use(buf[:]); C.callGoStackCheck() } +func stack4788() { var buf [4788]byte; use(buf[:]); C.callGoStackCheck() } +func stack4792() { var buf [4792]byte; use(buf[:]); C.callGoStackCheck() } +func stack4796() { var buf [4796]byte; use(buf[:]); C.callGoStackCheck() } +func stack4800() { var buf [4800]byte; use(buf[:]); C.callGoStackCheck() } +func stack4804() { var buf [4804]byte; use(buf[:]); C.callGoStackCheck() } +func stack4808() { var buf [4808]byte; use(buf[:]); C.callGoStackCheck() } +func stack4812() { var buf [4812]byte; use(buf[:]); C.callGoStackCheck() } +func stack4816() { var buf [4816]byte; use(buf[:]); C.callGoStackCheck() } +func stack4820() { var buf [4820]byte; use(buf[:]); C.callGoStackCheck() } +func stack4824() { var buf [4824]byte; use(buf[:]); C.callGoStackCheck() } +func stack4828() { var buf [4828]byte; use(buf[:]); C.callGoStackCheck() } +func stack4832() { var buf [4832]byte; use(buf[:]); C.callGoStackCheck() } +func stack4836() { var buf [4836]byte; use(buf[:]); C.callGoStackCheck() } +func stack4840() { var buf [4840]byte; use(buf[:]); C.callGoStackCheck() } +func stack4844() { var buf [4844]byte; use(buf[:]); C.callGoStackCheck() } +func stack4848() { var buf [4848]byte; use(buf[:]); C.callGoStackCheck() } +func stack4852() { var buf [4852]byte; use(buf[:]); C.callGoStackCheck() } +func stack4856() { var buf [4856]byte; use(buf[:]); C.callGoStackCheck() } +func stack4860() { var buf [4860]byte; use(buf[:]); C.callGoStackCheck() } +func stack4864() { var buf [4864]byte; use(buf[:]); C.callGoStackCheck() } +func stack4868() { var buf [4868]byte; use(buf[:]); C.callGoStackCheck() } +func stack4872() { var buf [4872]byte; use(buf[:]); C.callGoStackCheck() } +func stack4876() { var buf [4876]byte; use(buf[:]); C.callGoStackCheck() } +func stack4880() { var buf [4880]byte; use(buf[:]); C.callGoStackCheck() } +func stack4884() { var buf [4884]byte; use(buf[:]); C.callGoStackCheck() } +func stack4888() { var buf [4888]byte; use(buf[:]); C.callGoStackCheck() } +func stack4892() { var buf [4892]byte; use(buf[:]); C.callGoStackCheck() } +func stack4896() { var buf [4896]byte; use(buf[:]); C.callGoStackCheck() } +func stack4900() { var buf [4900]byte; use(buf[:]); C.callGoStackCheck() } +func stack4904() { var buf [4904]byte; use(buf[:]); C.callGoStackCheck() } +func stack4908() { var buf [4908]byte; use(buf[:]); C.callGoStackCheck() } +func stack4912() { var buf [4912]byte; use(buf[:]); C.callGoStackCheck() } +func stack4916() { var buf [4916]byte; use(buf[:]); C.callGoStackCheck() } +func stack4920() { var buf [4920]byte; use(buf[:]); C.callGoStackCheck() } +func stack4924() { var buf [4924]byte; use(buf[:]); C.callGoStackCheck() } +func stack4928() { var buf [4928]byte; use(buf[:]); C.callGoStackCheck() } +func stack4932() { var buf [4932]byte; use(buf[:]); C.callGoStackCheck() } +func stack4936() { var buf [4936]byte; use(buf[:]); C.callGoStackCheck() } +func stack4940() { var buf [4940]byte; use(buf[:]); C.callGoStackCheck() } +func stack4944() { var buf [4944]byte; use(buf[:]); C.callGoStackCheck() } +func stack4948() { var buf [4948]byte; use(buf[:]); C.callGoStackCheck() } +func stack4952() { var buf [4952]byte; use(buf[:]); C.callGoStackCheck() } +func stack4956() { var buf [4956]byte; use(buf[:]); C.callGoStackCheck() } +func stack4960() { var buf [4960]byte; use(buf[:]); C.callGoStackCheck() } +func stack4964() { var buf [4964]byte; use(buf[:]); C.callGoStackCheck() } +func stack4968() { var buf [4968]byte; use(buf[:]); C.callGoStackCheck() } +func stack4972() { var buf [4972]byte; use(buf[:]); C.callGoStackCheck() } +func stack4976() { var buf [4976]byte; use(buf[:]); C.callGoStackCheck() } +func stack4980() { var buf [4980]byte; use(buf[:]); C.callGoStackCheck() } +func stack4984() { var buf [4984]byte; use(buf[:]); C.callGoStackCheck() } +func stack4988() { var buf [4988]byte; use(buf[:]); C.callGoStackCheck() } +func stack4992() { var buf [4992]byte; use(buf[:]); C.callGoStackCheck() } +func stack4996() { var buf [4996]byte; use(buf[:]); C.callGoStackCheck() } +func stack5000() { var buf [5000]byte; use(buf[:]); C.callGoStackCheck() } diff --git a/misc/cgo/test/callback_c.c b/misc/cgo/test/callback_c.c new file mode 100644 index 0000000..8ecf70f --- /dev/null +++ b/misc/cgo/test/callback_c.c @@ -0,0 +1,67 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <string.h> + +#include "_cgo_export.h" + +void +callback(void *f) +{ + // use some stack space + volatile char data[64*1024]; + + data[0] = 0; + goCallback(f); + data[sizeof(data)-1] = 0; +} + +void +callGoFoo(void) +{ + extern void goFoo(void); + goFoo(); +} + +void +IntoC(void) +{ + BackIntoGo(); +} + +void +Issue1560InC(void) +{ + Issue1560FromC(); +} + +void +callGoStackCheck(void) +{ + extern void goStackCheck(void); + goStackCheck(); +} + +int +returnAfterGrow(void) +{ + extern int goReturnVal(void); + goReturnVal(); + return 123456; +} + +int +returnAfterGrowFromGo(void) +{ + extern int goReturnVal(void); + return goReturnVal(); +} + +void +callGoWithString(void) +{ + extern void goWithString(GoString); + const char *str = "string passed from C to Go"; + goWithString((GoString){str, strlen(str)}); +} diff --git a/misc/cgo/test/callback_c_gc.c b/misc/cgo/test/callback_c_gc.c new file mode 100644 index 0000000..eb720eb --- /dev/null +++ b/misc/cgo/test/callback_c_gc.c @@ -0,0 +1,25 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gc + +#include "_cgo_export.h" +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +/* Test calling panic from C. This is what SWIG does. */ + +extern void crosscall2(void (*fn)(void *, int), void *, int); +extern void _cgo_panic(void *, int); +extern void _cgo_allocate(void *, int); + +void +callPanic(void) +{ + struct { const char *p; } a; + a.p = "panic from C"; + crosscall2(_cgo_panic, &a, sizeof a); + *(int*)1 = 1; +} diff --git a/misc/cgo/test/callback_c_gccgo.c b/misc/cgo/test/callback_c_gccgo.c new file mode 100644 index 0000000..4eaa818 --- /dev/null +++ b/misc/cgo/test/callback_c_gccgo.c @@ -0,0 +1,21 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gccgo + +#include "_cgo_export.h" +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +/* Test calling panic from C. This is what SWIG does. */ + +extern void _cgo_panic(const char *); +extern void *_cgo_allocate(size_t); + +void +callPanic(void) +{ + _cgo_panic("panic from C"); +} diff --git a/misc/cgo/test/cgo_linux_test.go b/misc/cgo/test/cgo_linux_test.go new file mode 100644 index 0000000..3a1cf98 --- /dev/null +++ b/misc/cgo/test/cgo_linux_test.go @@ -0,0 +1,43 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import ( + "os" + "runtime" + "testing" +) + +func TestSetgid(t *testing.T) { + if runtime.GOOS == "android" { + t.Skip("unsupported on Android") + } + if _, err := os.Stat("/etc/alpine-release"); err == nil { + t.Skip("setgid is broken with musl libc - go.dev/issue/39857") + } + testSetgid(t) +} + +func TestSetgidStress(t *testing.T) { + if runtime.GOOS == "android" { + t.Skip("unsupported on Android") + } + if _, err := os.Stat("/etc/alpine-release"); err == nil { + t.Skip("setgid is broken with musl libc - go.dev/issue/39857") + } + testSetgidStress(t) +} + +func Test1435(t *testing.T) { test1435(t) } +func Test6997(t *testing.T) { test6997(t) } + +func Test9400(t *testing.T) { + if _, err := os.Stat("/etc/alpine-release"); err == nil { + t.Skip("setgid is broken with musl libc - go.dev/issue/39857") + } + test9400(t) +} + +func TestBuildID(t *testing.T) { testBuildID(t) } diff --git a/misc/cgo/test/cgo_stubs_android_test.go b/misc/cgo/test/cgo_stubs_android_test.go new file mode 100644 index 0000000..a1c2482 --- /dev/null +++ b/misc/cgo/test/cgo_stubs_android_test.go @@ -0,0 +1,12 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import "testing" + +// Stubs for tests that fails to build on Android +func test6997(t *testing.T) {} +func test8694(t *testing.T) {} +func testSigaltstack(t *testing.T) {} diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go new file mode 100644 index 0000000..5b29895 --- /dev/null +++ b/misc/cgo/test/cgo_test.go @@ -0,0 +1,109 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import "testing" + +// The actual test functions are in non-_test.go files +// so that they can use cgo (import "C"). +// These wrappers are here for gotest to find. + +func Test1328(t *testing.T) { test1328(t) } +func Test1560(t *testing.T) { test1560(t) } +func Test1635(t *testing.T) { test1635(t) } +func Test3250(t *testing.T) { test3250(t) } +func Test3729(t *testing.T) { test3729(t) } +func Test3775(t *testing.T) { test3775(t) } +func Test4029(t *testing.T) { test4029(t) } +func Test4339(t *testing.T) { test4339(t) } +func Test5227(t *testing.T) { test5227(t) } +func Test5242(t *testing.T) { test5242(t) } +func Test5337(t *testing.T) { test5337(t) } +func Test5548(t *testing.T) { test5548(t) } +func Test5603(t *testing.T) { test5603(t) } +func Test5986(t *testing.T) { test5986(t) } +func Test6390(t *testing.T) { test6390(t) } +func Test6833(t *testing.T) { test6833(t) } +func Test6907(t *testing.T) { test6907(t) } +func Test6907Go(t *testing.T) { test6907Go(t) } +func Test7560(t *testing.T) { test7560(t) } +func Test7665(t *testing.T) { test7665(t) } +func Test7978(t *testing.T) { test7978(t) } +func Test8092(t *testing.T) { test8092(t) } +func Test8517(t *testing.T) { test8517(t) } +func Test8694(t *testing.T) { test8694(t) } +func Test8756(t *testing.T) { test8756(t) } +func Test8811(t *testing.T) { test8811(t) } +func Test9026(t *testing.T) { test9026(t) } +func Test9510(t *testing.T) { test9510(t) } +func Test9557(t *testing.T) { test9557(t) } +func Test10303(t *testing.T) { test10303(t, 10) } +func Test11925(t *testing.T) { test11925(t) } +func Test12030(t *testing.T) { test12030(t) } +func Test14838(t *testing.T) { test14838(t) } +func Test17065(t *testing.T) { test17065(t) } +func Test17537(t *testing.T) { test17537(t) } +func Test18126(t *testing.T) { test18126(t) } +func Test18720(t *testing.T) { test18720(t) } +func Test20129(t *testing.T) { test20129(t) } +func Test20266(t *testing.T) { test20266(t) } +func Test20369(t *testing.T) { test20369(t) } +func Test20910(t *testing.T) { test20910(t) } +func Test21708(t *testing.T) { test21708(t) } +func Test21809(t *testing.T) { test21809(t) } +func Test21897(t *testing.T) { test21897(t) } +func Test22906(t *testing.T) { test22906(t) } +func Test23356(t *testing.T) { test23356(t) } +func Test24206(t *testing.T) { test24206(t) } +func Test25143(t *testing.T) { test25143(t) } +func Test26066(t *testing.T) { test26066(t) } +func Test26213(t *testing.T) { test26213(t) } +func Test27660(t *testing.T) { test27660(t) } +func Test28896(t *testing.T) { test28896(t) } +func Test30065(t *testing.T) { test30065(t) } +func Test32579(t *testing.T) { test32579(t) } +func Test31891(t *testing.T) { test31891(t) } +func Test42018(t *testing.T) { test42018(t) } +func Test45451(t *testing.T) { test45451(t) } +func Test49633(t *testing.T) { test49633(t) } +func TestAlign(t *testing.T) { testAlign(t) } +func TestAtol(t *testing.T) { testAtol(t) } +func TestBlocking(t *testing.T) { testBlocking(t) } +func TestBoolAlign(t *testing.T) { testBoolAlign(t) } +func TestCallGoWithString(t *testing.T) { testCallGoWithString(t) } +func TestCallback(t *testing.T) { testCallback(t) } +func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) } +func TestCallbackGC(t *testing.T) { testCallbackGC(t) } +func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) } +func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) } +func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) } +func TestCallbackStack(t *testing.T) { testCallbackStack(t) } +func TestCflags(t *testing.T) { testCflags(t) } +func TestCheckConst(t *testing.T) { testCheckConst(t) } +func TestConst(t *testing.T) { testConst(t) } +func TestCthread(t *testing.T) { testCthread(t) } +func TestEnum(t *testing.T) { testEnum(t) } +func TestNamedEnum(t *testing.T) { testNamedEnum(t) } +func TestCastToEnum(t *testing.T) { testCastToEnum(t) } +func TestErrno(t *testing.T) { testErrno(t) } +func TestFpVar(t *testing.T) { testFpVar(t) } +func TestGCC68255(t *testing.T) { testGCC68255(t) } +func TestHandle(t *testing.T) { testHandle(t) } +func TestHelpers(t *testing.T) { testHelpers(t) } +func TestLibgcc(t *testing.T) { testLibgcc(t) } +func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) } +func TestNaming(t *testing.T) { testNaming(t) } +func TestPanicFromC(t *testing.T) { testPanicFromC(t) } +func TestPrintf(t *testing.T) { testPrintf(t) } +func TestReturnAfterGrow(t *testing.T) { testReturnAfterGrow(t) } +func TestReturnAfterGrowFromGo(t *testing.T) { testReturnAfterGrowFromGo(t) } +func TestSetEnv(t *testing.T) { testSetEnv(t) } +func TestThreadLock(t *testing.T) { testThreadLockFunc(t) } +func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) } +func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } + +func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } +func BenchmarkGoString(b *testing.B) { benchGoString(b) } +func BenchmarkCGoCallback(b *testing.B) { benchCallback(b) } diff --git a/misc/cgo/test/cgo_thread_lock.go b/misc/cgo/test/cgo_thread_lock.go new file mode 100644 index 0000000..3b9ac84 --- /dev/null +++ b/misc/cgo/test/cgo_thread_lock.go @@ -0,0 +1,54 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && freebsd && openbsd +// +build linux,freebsd,openbsd + +package cgotest + +/* +#include <unistd.h> +#include <sys/syscall.h> +void Gosched(void); +static int Ctid(void) { Gosched(); return syscall(SYS_gettid); } +*/ +import "C" + +import ( + "runtime" + "syscall" + "testing" + "time" +) + +//export Gosched +func Gosched() { + runtime.Gosched() +} + +func init() { + testThreadLockFunc = testThreadLock +} + +func testThreadLock(t *testing.T) { + stop := make(chan int) + go func() { + // We need the G continue running, + // so the M has a chance to run this G. + for { + select { + case <-stop: + return + case <-time.After(time.Millisecond * 100): + } + } + }() + defer close(stop) + + for i := 0; i < 1000; i++ { + if C.int(syscall.Gettid()) != C.Ctid() { + t.Fatalf("cgo has not locked OS thread") + } + } +} diff --git a/misc/cgo/test/cgo_unix_test.go b/misc/cgo/test/cgo_unix_test.go new file mode 100644 index 0000000..a324503 --- /dev/null +++ b/misc/cgo/test/cgo_unix_test.go @@ -0,0 +1,14 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows +// +build !windows + +package cgotest + +import "testing" + +func TestSigaltstack(t *testing.T) { testSigaltstack(t) } +func TestSigprocmask(t *testing.T) { testSigprocmask(t) } +func Test18146(t *testing.T) { test18146(t) } diff --git a/misc/cgo/test/cthread_unix.c b/misc/cgo/test/cthread_unix.c new file mode 100644 index 0000000..247d636 --- /dev/null +++ b/misc/cgo/test/cthread_unix.c @@ -0,0 +1,34 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris + +#include <pthread.h> +#include "_cgo_export.h" + +static void* +addThread(void *p) +{ + int i, max; + + max = *(int*)p; + for(i=0; i<max; i++) + Add(i); + return 0; +} + +void +doAdd(int max, int nthread) +{ + enum { MaxThread = 20 }; + int i; + pthread_t thread_id[MaxThread]; + + if(nthread > MaxThread) + nthread = MaxThread; + for(i=0; i<nthread; i++) + pthread_create(&thread_id[i], 0, addThread, &max); + for(i=0; i<nthread; i++) + pthread_join(thread_id[i], 0); +} diff --git a/misc/cgo/test/cthread_windows.c b/misc/cgo/test/cthread_windows.c new file mode 100644 index 0000000..3a62ddd --- /dev/null +++ b/misc/cgo/test/cthread_windows.c @@ -0,0 +1,37 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <process.h> +#include "_cgo_export.h" + +__stdcall +static unsigned int +addThread(void *p) +{ + int i, max; + + max = *(int*)p; + for(i=0; i<max; i++) + Add(i); + return 0; +} + +void +doAdd(int max, int nthread) +{ + enum { MaxThread = 20 }; + int i; + uintptr_t thread_id[MaxThread]; + + if(nthread > MaxThread) + nthread = MaxThread; + for(i=0; i<nthread; i++) + thread_id[i] = _beginthreadex(0, 0, addThread, &max, 0, 0); + for(i=0; i<nthread; i++) { + WaitForSingleObject((HANDLE)thread_id[i], INFINITE); + CloseHandle((HANDLE)thread_id[i]); + } +} diff --git a/misc/cgo/test/gcc68255.go b/misc/cgo/test/gcc68255.go new file mode 100644 index 0000000..7b25e02 --- /dev/null +++ b/misc/cgo/test/gcc68255.go @@ -0,0 +1,17 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import ( + "testing" + + "misc/cgo/test/gcc68255" +) + +func testGCC68255(t *testing.T) { + if !gcc68255.F() { + t.Error("C global variable was not initialized") + } +} diff --git a/misc/cgo/test/gcc68255/a.go b/misc/cgo/test/gcc68255/a.go new file mode 100644 index 0000000..e106dee --- /dev/null +++ b/misc/cgo/test/gcc68255/a.go @@ -0,0 +1,17 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that it's OK to have C code that does nothing other than +// initialize a global variable. This used to fail with gccgo. + +package gcc68255 + +/* +#include "c.h" +*/ +import "C" + +func F() bool { + return C.v != nil +} diff --git a/misc/cgo/test/gcc68255/c.c b/misc/cgo/test/gcc68255/c.c new file mode 100644 index 0000000..a4fe193 --- /dev/null +++ b/misc/cgo/test/gcc68255/c.c @@ -0,0 +1,8 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +static void f(void) { +} + +void (*v)(void) = f; diff --git a/misc/cgo/test/gcc68255/c.h b/misc/cgo/test/gcc68255/c.h new file mode 100644 index 0000000..05ecd81 --- /dev/null +++ b/misc/cgo/test/gcc68255/c.h @@ -0,0 +1,5 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +extern void (*v)(void); diff --git a/misc/cgo/test/issue1435.go b/misc/cgo/test/issue1435.go new file mode 100644 index 0000000..3fb721a --- /dev/null +++ b/misc/cgo/test/issue1435.go @@ -0,0 +1,204 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build linux && cgo +// +build linux,cgo + +package cgotest + +import ( + "fmt" + "os" + "runtime" + "sort" + "strings" + "syscall" + "testing" +) + +// #include <stdio.h> +// #include <stdlib.h> +// #include <pthread.h> +// #include <unistd.h> +// #include <sys/types.h> +// +// pthread_t *t = NULL; +// pthread_mutex_t mu; +// int nts = 0; +// int all_done = 0; +// +// static void *aFn(void *vargp) { +// int done = 0; +// while (!done) { +// usleep(100); +// pthread_mutex_lock(&mu); +// done = all_done; +// pthread_mutex_unlock(&mu); +// } +// return NULL; +// } +// +// void trial(int argc) { +// int i; +// nts = argc; +// t = calloc(nts, sizeof(pthread_t)); +// pthread_mutex_init(&mu, NULL); +// for (i = 0; i < nts; i++) { +// pthread_create(&t[i], NULL, aFn, NULL); +// } +// } +// +// void cleanup(void) { +// int i; +// pthread_mutex_lock(&mu); +// all_done = 1; +// pthread_mutex_unlock(&mu); +// for (i = 0; i < nts; i++) { +// pthread_join(t[i], NULL); +// } +// pthread_mutex_destroy(&mu); +// free(t); +// } +import "C" + +// compareStatus is used to confirm the contents of the thread +// specific status files match expectations. +func compareStatus(filter, expect string) error { + expected := filter + expect + pid := syscall.Getpid() + fs, err := os.ReadDir(fmt.Sprintf("/proc/%d/task", pid)) + if err != nil { + return fmt.Errorf("unable to find %d tasks: %v", pid, err) + } + expectedProc := fmt.Sprintf("Pid:\t%d", pid) + foundAThread := false + for _, f := range fs { + tf := fmt.Sprintf("/proc/%s/status", f.Name()) + d, err := os.ReadFile(tf) + if err != nil { + // There are a surprising number of ways this + // can error out on linux. We've seen all of + // the following, so treat any error here as + // equivalent to the "process is gone": + // os.IsNotExist(err), + // "... : no such process", + // "... : bad file descriptor. + continue + } + lines := strings.Split(string(d), "\n") + for _, line := range lines { + // Different kernel vintages pad differently. + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "Pid:\t") { + // On loaded systems, it is possible + // for a TID to be reused really + // quickly. As such, we need to + // validate that the thread status + // info we just read is a task of the + // same process PID as we are + // currently running, and not a + // recently terminated thread + // resurfaced in a different process. + if line != expectedProc { + break + } + // Fall through in the unlikely case + // that filter at some point is + // "Pid:\t". + } + if strings.HasPrefix(line, filter) { + if line == expected { + foundAThread = true + break + } + if filter == "Groups:" && strings.HasPrefix(line, "Groups:\t") { + // https://github.com/golang/go/issues/46145 + // Containers don't reliably output this line in sorted order so manually sort and compare that. + a := strings.Split(line[8:], " ") + sort.Strings(a) + got := strings.Join(a, " ") + if got == expected[8:] { + foundAThread = true + break + } + + } + return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc) + } + } + } + if !foundAThread { + return fmt.Errorf("found no thread /proc/<TID>/status files for process %q", expectedProc) + } + return nil +} + +// test1435 test 9 glibc implemented setuid/gid syscall functions are +// mapped. This test is a slightly more expansive test than that of +// src/syscall/syscall_linux_test.go:TestSetuidEtc() insofar as it +// launches concurrent threads from C code via CGo and validates that +// they are subject to the system calls being tested. For the actual +// Go functionality being tested here, the syscall_linux_test version +// is considered authoritative, but non-trivial improvements to that +// should be mirrored here. +func test1435(t *testing.T) { + if syscall.Getuid() != 0 { + t.Skip("skipping root only test") + } + if runtime.GOOS == "linux" { + if _, err := os.Stat("/etc/alpine-release"); err == nil { + t.Skip("skipping failing test on alpine - go.dev/issue/19938") + } + } + + // Launch some threads in C. + const cts = 5 + C.trial(cts) + defer C.cleanup() + + vs := []struct { + call string + fn func() error + filter, expect string + }{ + {call: "Setegid(1)", fn: func() error { return syscall.Setegid(1) }, filter: "Gid:", expect: "\t0\t1\t0\t1"}, + {call: "Setegid(0)", fn: func() error { return syscall.Setegid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, + + {call: "Seteuid(1)", fn: func() error { return syscall.Seteuid(1) }, filter: "Uid:", expect: "\t0\t1\t0\t1"}, + {call: "Setuid(0)", fn: func() error { return syscall.Setuid(0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setgid(1)", fn: func() error { return syscall.Setgid(1) }, filter: "Gid:", expect: "\t1\t1\t1\t1"}, + {call: "Setgid(0)", fn: func() error { return syscall.Setgid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setgroups([]int{0,1,2,3})", fn: func() error { return syscall.Setgroups([]int{0, 1, 2, 3}) }, filter: "Groups:", expect: "\t0 1 2 3"}, + {call: "Setgroups(nil)", fn: func() error { return syscall.Setgroups(nil) }, filter: "Groups:", expect: ""}, + {call: "Setgroups([]int{0})", fn: func() error { return syscall.Setgroups([]int{0}) }, filter: "Groups:", expect: "\t0"}, + + {call: "Setregid(101,0)", fn: func() error { return syscall.Setregid(101, 0) }, filter: "Gid:", expect: "\t101\t0\t0\t0"}, + {call: "Setregid(0,102)", fn: func() error { return syscall.Setregid(0, 102) }, filter: "Gid:", expect: "\t0\t102\t102\t102"}, + {call: "Setregid(0,0)", fn: func() error { return syscall.Setregid(0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setreuid(1,0)", fn: func() error { return syscall.Setreuid(1, 0) }, filter: "Uid:", expect: "\t1\t0\t0\t0"}, + {call: "Setreuid(0,2)", fn: func() error { return syscall.Setreuid(0, 2) }, filter: "Uid:", expect: "\t0\t2\t2\t2"}, + {call: "Setreuid(0,0)", fn: func() error { return syscall.Setreuid(0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setresgid(101,0,102)", fn: func() error { return syscall.Setresgid(101, 0, 102) }, filter: "Gid:", expect: "\t101\t0\t102\t0"}, + {call: "Setresgid(0,102,101)", fn: func() error { return syscall.Setresgid(0, 102, 101) }, filter: "Gid:", expect: "\t0\t102\t101\t102"}, + {call: "Setresgid(0,0,0)", fn: func() error { return syscall.Setresgid(0, 0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"}, + + {call: "Setresuid(1,0,2)", fn: func() error { return syscall.Setresuid(1, 0, 2) }, filter: "Uid:", expect: "\t1\t0\t2\t0"}, + {call: "Setresuid(0,2,1)", fn: func() error { return syscall.Setresuid(0, 2, 1) }, filter: "Uid:", expect: "\t0\t2\t1\t2"}, + {call: "Setresuid(0,0,0)", fn: func() error { return syscall.Setresuid(0, 0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"}, + } + + for i, v := range vs { + if err := v.fn(); err != nil { + t.Errorf("[%d] %q failed: %v", i, v.call, err) + continue + } + if err := compareStatus(v.filter, v.expect); err != nil { + t.Errorf("[%d] %q comparison: %v", i, v.call, err) + } + } +} diff --git a/misc/cgo/test/issue18146.go b/misc/cgo/test/issue18146.go new file mode 100644 index 0000000..e50f9ae --- /dev/null +++ b/misc/cgo/test/issue18146.go @@ -0,0 +1,129 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows +// +build !windows + +// Issue 18146: pthread_create failure during syscall.Exec. + +package cgotest + +import ( + "bytes" + "crypto/md5" + "os" + "os/exec" + "runtime" + "syscall" + "testing" + "time" +) + +func test18146(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + t.Skipf("skipping flaky test on %s; see golang.org/issue/18202", runtime.GOOS) + } + + if runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" { + t.Skipf("skipping on %s", runtime.GOARCH) + } + + attempts := 1000 + threads := 4 + + // Restrict the number of attempts based on RLIMIT_NPROC. + // Tediously, RLIMIT_NPROC was left out of the syscall package, + // probably because it is not in POSIX.1, so we define it here. + // It is not defined on Solaris. + var nproc int + setNproc := true + switch runtime.GOOS { + default: + setNproc = false + case "aix": + nproc = 9 + case "linux": + nproc = 6 + case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd": + nproc = 7 + } + if setNproc { + var rlim syscall.Rlimit + if syscall.Getrlimit(nproc, &rlim) == nil { + max := int(rlim.Cur) / (threads + 5) + if attempts > max { + t.Logf("lowering attempts from %d to %d for RLIMIT_NPROC", attempts, max) + attempts = max + } + } + } + + if os.Getenv("test18146") == "exec" { + runtime.GOMAXPROCS(1) + for n := threads; n > 0; n-- { + go func() { + for { + _ = md5.Sum([]byte("Hello, ï €!")) + } + }() + } + runtime.GOMAXPROCS(threads) + argv := append(os.Args, "-test.run=NoSuchTestExists") + if err := syscall.Exec(os.Args[0], argv, os.Environ()); err != nil { + t.Fatal(err) + } + } + + var cmds []*exec.Cmd + defer func() { + for _, cmd := range cmds { + cmd.Process.Kill() + } + }() + + args := append(append([]string(nil), os.Args[1:]...), "-test.run=Test18146") + for n := attempts; n > 0; n-- { + cmd := exec.Command(os.Args[0], args...) + cmd.Env = append(os.Environ(), "test18146=exec") + buf := bytes.NewBuffer(nil) + cmd.Stdout = buf + cmd.Stderr = buf + if err := cmd.Start(); err != nil { + // We are starting so many processes that on + // some systems (problem seen on Darwin, + // Dragonfly, OpenBSD) the fork call will fail + // with EAGAIN. + if pe, ok := err.(*os.PathError); ok { + err = pe.Err + } + if se, ok := err.(syscall.Errno); ok && (se == syscall.EAGAIN || se == syscall.EMFILE) { + time.Sleep(time.Millisecond) + continue + } + + t.Error(err) + return + } + cmds = append(cmds, cmd) + } + + failures := 0 + for _, cmd := range cmds { + err := cmd.Wait() + if err == nil { + continue + } + + t.Errorf("syscall.Exec failed: %v\n%s", err, cmd.Stdout) + failures++ + } + + if failures > 0 { + t.Logf("Failed %v of %v attempts.", failures, len(cmds)) + } +} diff --git a/misc/cgo/test/issue20266.go b/misc/cgo/test/issue20266.go new file mode 100644 index 0000000..9f95086 --- /dev/null +++ b/misc/cgo/test/issue20266.go @@ -0,0 +1,21 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 20266: use -I with a relative path. + +package cgotest + +/* +#cgo CFLAGS: -I issue20266 -Iissue20266 -Ddef20266 +#include "issue20266.h" +*/ +import "C" + +import "testing" + +func test20266(t *testing.T) { + if got, want := C.issue20266, 20266; got != want { + t.Errorf("got %d, want %d", got, want) + } +} diff --git a/misc/cgo/test/issue20266/issue20266.h b/misc/cgo/test/issue20266/issue20266.h new file mode 100644 index 0000000..8d3258e --- /dev/null +++ b/misc/cgo/test/issue20266/issue20266.h @@ -0,0 +1,9 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#define issue20266 20266 + +#ifndef def20266 +#error "expected def20266 to be defined" +#endif diff --git a/misc/cgo/test/issue20910.c b/misc/cgo/test/issue20910.c new file mode 100644 index 0000000..e8d623f --- /dev/null +++ b/misc/cgo/test/issue20910.c @@ -0,0 +1,19 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include "_cgo_export.h" + +/* Test calling a Go function with multiple return values. */ + +void +callMulti(void) +{ + struct multi_return result = multi(); + assert(strcmp(result.r0, "multi") == 0); + assert(result.r1 == 0); + free(result.r0); +} diff --git a/misc/cgo/test/issue21897.go b/misc/cgo/test/issue21897.go new file mode 100644 index 0000000..8f39252 --- /dev/null +++ b/misc/cgo/test/issue21897.go @@ -0,0 +1,57 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build darwin && cgo && !internal +// +build darwin,cgo,!internal + +package cgotest + +/* +#cgo LDFLAGS: -framework CoreFoundation +#include <CoreFoundation/CoreFoundation.h> +*/ +import "C" +import ( + "runtime/debug" + "testing" + "unsafe" +) + +func test21897(t *testing.T) { + // Please write barrier, kick in soon. + defer debug.SetGCPercent(debug.SetGCPercent(1)) + + for i := 0; i < 10000; i++ { + testCFNumberRef() + testCFDateRef() + testCFBooleanRef() + // Allocate some memory, so eventually the write barrier is enabled + // and it will see writes of bad pointers in the test* functions below. + byteSliceSink = make([]byte, 1024) + } +} + +var byteSliceSink []byte + +func testCFNumberRef() { + var v int64 = 0 + xCFNumberRef = C.CFNumberCreate(C.kCFAllocatorSystemDefault, C.kCFNumberSInt64Type, unsafe.Pointer(&v)) + //fmt.Printf("CFNumberRef: %x\n", uintptr(unsafe.Pointer(xCFNumberRef))) +} + +var xCFNumberRef C.CFNumberRef + +func testCFDateRef() { + xCFDateRef = C.CFDateCreate(C.kCFAllocatorSystemDefault, 0) // 0 value is 1 Jan 2001 00:00:00 GMT + //fmt.Printf("CFDateRef: %x\n", uintptr(unsafe.Pointer(xCFDateRef))) +} + +var xCFDateRef C.CFDateRef + +func testCFBooleanRef() { + xCFBooleanRef = C.kCFBooleanFalse + //fmt.Printf("CFBooleanRef: %x\n", uintptr(unsafe.Pointer(xCFBooleanRef))) +} + +var xCFBooleanRef C.CFBooleanRef diff --git a/misc/cgo/test/issue21897b.go b/misc/cgo/test/issue21897b.go new file mode 100644 index 0000000..50aece3 --- /dev/null +++ b/misc/cgo/test/issue21897b.go @@ -0,0 +1,14 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !darwin || !cgo || internal +// +build !darwin !cgo internal + +package cgotest + +import "testing" + +func test21897(t *testing.T) { + t.Skip("test runs only on darwin+cgo") +} diff --git a/misc/cgo/test/issue23555.go b/misc/cgo/test/issue23555.go new file mode 100644 index 0000000..e84c23c --- /dev/null +++ b/misc/cgo/test/issue23555.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that we can have two identical cgo packages in a single binary. +// No runtime test; just make sure it compiles. + +package cgotest + +import ( + _ "misc/cgo/test/issue23555a" + _ "misc/cgo/test/issue23555b" +) diff --git a/misc/cgo/test/issue23555a/a.go b/misc/cgo/test/issue23555a/a.go new file mode 100644 index 0000000..cb6626b --- /dev/null +++ b/misc/cgo/test/issue23555a/a.go @@ -0,0 +1,12 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue23555 + +// #include <stdlib.h> +import "C" + +func X() { + C.free(C.malloc(10)) +} diff --git a/misc/cgo/test/issue23555b/a.go b/misc/cgo/test/issue23555b/a.go new file mode 100644 index 0000000..cb6626b --- /dev/null +++ b/misc/cgo/test/issue23555b/a.go @@ -0,0 +1,12 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue23555 + +// #include <stdlib.h> +import "C" + +func X() { + C.free(C.malloc(10)) +} diff --git a/misc/cgo/test/issue24161_darwin_test.go b/misc/cgo/test/issue24161_darwin_test.go new file mode 100644 index 0000000..7bb2af6 --- /dev/null +++ b/misc/cgo/test/issue24161_darwin_test.go @@ -0,0 +1,31 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import ( + "testing" + + "misc/cgo/test/issue24161arg" + "misc/cgo/test/issue24161e0" + "misc/cgo/test/issue24161e1" + "misc/cgo/test/issue24161e2" + "misc/cgo/test/issue24161res" +) + +func Test24161Arg(t *testing.T) { + issue24161arg.Test(t) +} +func Test24161Res(t *testing.T) { + issue24161res.Test(t) +} +func Test24161Example0(t *testing.T) { + issue24161e0.Test(t) +} +func Test24161Example1(t *testing.T) { + issue24161e1.Test(t) +} +func Test24161Example2(t *testing.T) { + issue24161e2.Test(t) +} diff --git a/misc/cgo/test/issue24161arg/def.go b/misc/cgo/test/issue24161arg/def.go new file mode 100644 index 0000000..d33479a --- /dev/null +++ b/misc/cgo/test/issue24161arg/def.go @@ -0,0 +1,17 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package issue24161arg + +/* +#cgo LDFLAGS: -framework CoreFoundation +#include <CoreFoundation/CoreFoundation.h> +*/ +import "C" + +func test24161array() C.CFArrayRef { + return C.CFArrayCreate(0, nil, 0, nil) +} diff --git a/misc/cgo/test/issue24161arg/use.go b/misc/cgo/test/issue24161arg/use.go new file mode 100644 index 0000000..3e74944 --- /dev/null +++ b/misc/cgo/test/issue24161arg/use.go @@ -0,0 +1,19 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package issue24161arg + +/* +#cgo LDFLAGS: -framework CoreFoundation +#include <CoreFoundation/CoreFoundation.h> +*/ +import "C" +import "testing" + +func Test(t *testing.T) { + a := test24161array() + C.CFArrayCreateCopy(0, a) +} diff --git a/misc/cgo/test/issue24161e0/main.go b/misc/cgo/test/issue24161e0/main.go new file mode 100644 index 0000000..efe5345 --- /dev/null +++ b/misc/cgo/test/issue24161e0/main.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package issue24161e0 + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework CoreFoundation -framework Security +#include <TargetConditionals.h> +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> +#if TARGET_OS_IPHONE == 0 && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + typedef CFStringRef SecKeyAlgorithm; + static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;} + #define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo() + static SecKeyAlgorithm foo(void){return NULL;} +#endif +*/ +import "C" +import "testing" + +func f1() { + C.SecKeyCreateSignature(0, C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 0, nil) +} + +func Test(t *testing.T) {} diff --git a/misc/cgo/test/issue24161e1/main.go b/misc/cgo/test/issue24161e1/main.go new file mode 100644 index 0000000..82bf172 --- /dev/null +++ b/misc/cgo/test/issue24161e1/main.go @@ -0,0 +1,38 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package issue24161e1 + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework CoreFoundation -framework Security +#include <TargetConditionals.h> +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> +#if TARGET_OS_IPHONE == 0 && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + typedef CFStringRef SecKeyAlgorithm; + static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;} + #define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo() + static SecKeyAlgorithm foo(void){return NULL;} +#endif +*/ +import "C" +import ( + "fmt" + "testing" +) + +func f1() { + C.SecKeyCreateSignature(0, C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 0, nil) +} + +func f2(e C.CFErrorRef) { + if desc := C.CFErrorCopyDescription(e); desc != 0 { + fmt.Println(desc) + } +} + +func Test(t *testing.T) {} diff --git a/misc/cgo/test/issue24161e2/main.go b/misc/cgo/test/issue24161e2/main.go new file mode 100644 index 0000000..82d2ec1 --- /dev/null +++ b/misc/cgo/test/issue24161e2/main.go @@ -0,0 +1,40 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package issue24161e2 + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework CoreFoundation -framework Security +#include <TargetConditionals.h> +#include <CoreFoundation/CoreFoundation.h> +#include <Security/Security.h> +#if TARGET_OS_IPHONE == 0 && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + typedef CFStringRef SecKeyAlgorithm; + static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;} + #define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo() + static SecKeyAlgorithm foo(void){return NULL;} +#endif +*/ +import "C" +import ( + "fmt" + "testing" +) + +var _ C.CFStringRef + +func f1() { + C.SecKeyCreateSignature(0, C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 0, nil) +} + +func f2(e C.CFErrorRef) { + if desc := C.CFErrorCopyDescription(e); desc != 0 { + fmt.Println(desc) + } +} + +func Test(t *testing.T) {} diff --git a/misc/cgo/test/issue24161res/restype.go b/misc/cgo/test/issue24161res/restype.go new file mode 100644 index 0000000..e5719f2 --- /dev/null +++ b/misc/cgo/test/issue24161res/restype.go @@ -0,0 +1,23 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin + +package issue24161res + +/* +#cgo LDFLAGS: -framework CoreFoundation +#include <CoreFoundation/CoreFoundation.h> +*/ +import "C" +import ( + "reflect" + "testing" +) + +func Test(t *testing.T) { + if k := reflect.TypeOf(C.CFArrayCreate(0, nil, 0, nil)).Kind(); k != reflect.Uintptr { + t.Fatalf("bad kind %s\n", k) + } +} diff --git a/misc/cgo/test/issue26213/jni.h b/misc/cgo/test/issue26213/jni.h new file mode 100644 index 0000000..0c76979 --- /dev/null +++ b/misc/cgo/test/issue26213/jni.h @@ -0,0 +1,29 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// It's going to be hard to include a whole real JVM to test this. +// So we'll simulate a really easy JVM using just the parts we need. + +// This is the relevant part of jni.h. + +// On Android NDK16, jobject is defined like this in C and C++ +typedef void* jobject; + +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +typedef jobject jweak; + +// Note: jvalue is already a non-pointer type due to it being a C union. diff --git a/misc/cgo/test/issue26213/test26213.go b/misc/cgo/test/issue26213/test26213.go new file mode 100644 index 0000000..5d1f637 --- /dev/null +++ b/misc/cgo/test/issue26213/test26213.go @@ -0,0 +1,46 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue26213 + +/* +#include "jni.h" +*/ +import "C" +import ( + "testing" +) + +func Test26213(t *testing.T) { + var x1 C.jobject = 0 // Note: 0, not nil. That makes sure we use uintptr for these types. + _ = x1 + var x2 C.jclass = 0 + _ = x2 + var x3 C.jthrowable = 0 + _ = x3 + var x4 C.jstring = 0 + _ = x4 + var x5 C.jarray = 0 + _ = x5 + var x6 C.jbooleanArray = 0 + _ = x6 + var x7 C.jbyteArray = 0 + _ = x7 + var x8 C.jcharArray = 0 + _ = x8 + var x9 C.jshortArray = 0 + _ = x9 + var x10 C.jintArray = 0 + _ = x10 + var x11 C.jlongArray = 0 + _ = x11 + var x12 C.jfloatArray = 0 + _ = x12 + var x13 C.jdoubleArray = 0 + _ = x13 + var x14 C.jobjectArray = 0 + _ = x14 + var x15 C.jweak = 0 + _ = x15 +} diff --git a/misc/cgo/test/issue26430.go b/misc/cgo/test/issue26430.go new file mode 100644 index 0000000..b551939 --- /dev/null +++ b/misc/cgo/test/issue26430.go @@ -0,0 +1,10 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 26430: incomplete typedef leads to inconsistent typedefs error. +// No runtime test; just make sure it compiles. + +package cgotest + +import _ "misc/cgo/test/issue26430" diff --git a/misc/cgo/test/issue26430/a.go b/misc/cgo/test/issue26430/a.go new file mode 100644 index 0000000..fbaa46b --- /dev/null +++ b/misc/cgo/test/issue26430/a.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +// typedef struct S ST; +// static ST* F() { return 0; } +import "C" + +func F1() { + C.F() +} diff --git a/misc/cgo/test/issue26430/b.go b/misc/cgo/test/issue26430/b.go new file mode 100644 index 0000000..a7c527c --- /dev/null +++ b/misc/cgo/test/issue26430/b.go @@ -0,0 +1,13 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +// typedef struct S ST; +// struct S { int f; }; +import "C" + +func F2(p *C.ST) { + p.f = 1 +} diff --git a/misc/cgo/test/issue26743.go b/misc/cgo/test/issue26743.go new file mode 100644 index 0000000..716f2e4 --- /dev/null +++ b/misc/cgo/test/issue26743.go @@ -0,0 +1,10 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 26743: typedef of uint leads to inconsistent typedefs error. +// No runtime test; just make sure it compiles. + +package cgotest + +import _ "misc/cgo/test/issue26743" diff --git a/misc/cgo/test/issue26743/a.go b/misc/cgo/test/issue26743/a.go new file mode 100644 index 0000000..a3df179 --- /dev/null +++ b/misc/cgo/test/issue26743/a.go @@ -0,0 +1,11 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue26743 + +// typedef unsigned int uint; +// int C1(uint x) { return x; } +import "C" + +var V1 = C.C1(0) diff --git a/misc/cgo/test/issue26743/b.go b/misc/cgo/test/issue26743/b.go new file mode 100644 index 0000000..c5f1ae4 --- /dev/null +++ b/misc/cgo/test/issue26743/b.go @@ -0,0 +1,9 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue26743 + +import "C" + +var V2 C.uint diff --git a/misc/cgo/test/issue27054/egl.h b/misc/cgo/test/issue27054/egl.h new file mode 100644 index 0000000..3079627 --- /dev/null +++ b/misc/cgo/test/issue27054/egl.h @@ -0,0 +1,8 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is the relevant part of EGL/egl.h. + +typedef void *EGLDisplay; +typedef void *EGLConfig; diff --git a/misc/cgo/test/issue27054/test27054.go b/misc/cgo/test/issue27054/test27054.go new file mode 100644 index 0000000..01bf43a --- /dev/null +++ b/misc/cgo/test/issue27054/test27054.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue27054 + +/* +#include "egl.h" +*/ +import "C" +import ( + "testing" +) + +func Test27054(t *testing.T) { + var ( + // Note: 0, not nil. That makes sure we use uintptr for these types. + _ C.EGLDisplay = 0 + _ C.EGLConfig = 0 + ) +} diff --git a/misc/cgo/test/issue27340.go b/misc/cgo/test/issue27340.go new file mode 100644 index 0000000..2c51088 --- /dev/null +++ b/misc/cgo/test/issue27340.go @@ -0,0 +1,12 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Failed to resolve typedefs consistently. +// No runtime test; just make sure it compiles. + +package cgotest + +import "misc/cgo/test/issue27340" + +var issue27340Var = issue27340.Issue27340GoFunc diff --git a/misc/cgo/test/issue27340/a.go b/misc/cgo/test/issue27340/a.go new file mode 100644 index 0000000..f5b120c --- /dev/null +++ b/misc/cgo/test/issue27340/a.go @@ -0,0 +1,42 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Failed to resolve typedefs consistently. +// No runtime test; just make sure it compiles. +// In separate directory to isolate #pragma GCC diagnostic. + +package issue27340 + +// We use the #pragma to avoid a compiler warning about incompatible +// pointer types, because we generate code passing a struct ptr rather +// than using the typedef. This warning is expected and does not break +// a normal build. +// We can only disable -Wincompatible-pointer-types starting with GCC 5. + +// #if __GNU_MAJOR__ >= 5 +// +// #pragma GCC diagnostic ignored "-Wincompatible-pointer-types" +// +// typedef struct { +// int a; +// } issue27340Struct, *issue27340Ptr; +// +// static void issue27340CFunc(issue27340Ptr p) {} +// +// #else /* _GNU_MAJOR_ < 5 */ +// +// typedef struct { +// int a; +// } issue27340Struct; +// +// static issue27340Struct* issue27340Ptr(issue27340Struct* p) { return p; } +// +// static void issue27340CFunc(issue27340Struct *p) {} +// #endif /* _GNU_MAJOR_ < 5 */ +import "C" + +func Issue27340GoFunc() { + var s C.issue27340Struct + C.issue27340CFunc(C.issue27340Ptr(&s)) +} diff --git a/misc/cgo/test/issue29563.go b/misc/cgo/test/issue29563.go new file mode 100644 index 0000000..9f29da4 --- /dev/null +++ b/misc/cgo/test/issue29563.go @@ -0,0 +1,13 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows +// +build !windows + +// Issue 29563: internal linker fails on duplicate weak symbols. +// No runtime test; just make sure it compiles. + +package cgotest + +import _ "misc/cgo/test/issue29563" diff --git a/misc/cgo/test/issue29563/weak.go b/misc/cgo/test/issue29563/weak.go new file mode 100644 index 0000000..21cf635 --- /dev/null +++ b/misc/cgo/test/issue29563/weak.go @@ -0,0 +1,13 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue29563 + +//int foo1(); +//int foo2(); +import "C" + +func Bar() int { + return int(C.foo1()) + int(C.foo2()) +} diff --git a/misc/cgo/test/issue29563/weak1.c b/misc/cgo/test/issue29563/weak1.c new file mode 100644 index 0000000..86a2273 --- /dev/null +++ b/misc/cgo/test/issue29563/weak1.c @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +extern int weaksym __attribute__((__weak__)); +int weaksym = 42; + +int foo1() +{ + return weaksym; +} diff --git a/misc/cgo/test/issue29563/weak2.c b/misc/cgo/test/issue29563/weak2.c new file mode 100644 index 0000000..e01eae8 --- /dev/null +++ b/misc/cgo/test/issue29563/weak2.c @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +extern int weaksym __attribute__((__weak__)); +int weaksym = 42; + +int foo2() +{ + return weaksym; +} diff --git a/misc/cgo/test/issue30527.go b/misc/cgo/test/issue30527.go new file mode 100644 index 0000000..e0e18be --- /dev/null +++ b/misc/cgo/test/issue30527.go @@ -0,0 +1,14 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 30527: function call rewriting casts untyped +// constants to int because of ":=" usage. + +package cgotest + +import "misc/cgo/test/issue30527" + +func issue30527G() { + issue30527.G(nil) +} diff --git a/misc/cgo/test/issue30527/a.go b/misc/cgo/test/issue30527/a.go new file mode 100644 index 0000000..eb50147 --- /dev/null +++ b/misc/cgo/test/issue30527/a.go @@ -0,0 +1,19 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue30527 + +import "math" + +/* +#include <inttypes.h> + +static void issue30527F(char **p, uint64_t mod, uint32_t unused) {} +*/ +import "C" + +func G(p **C.char) { + C.issue30527F(p, math.MaxUint64, 1) + C.issue30527F(p, 1<<64-1, Z) +} diff --git a/misc/cgo/test/issue30527/b.go b/misc/cgo/test/issue30527/b.go new file mode 100644 index 0000000..87e8255 --- /dev/null +++ b/misc/cgo/test/issue30527/b.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue30527 + +const ( + X = 1 << iota + Y + Z +) diff --git a/misc/cgo/test/issue31891.c b/misc/cgo/test/issue31891.c new file mode 100644 index 0000000..67a0dda --- /dev/null +++ b/misc/cgo/test/issue31891.c @@ -0,0 +1,13 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "_cgo_export.h" + +void callIssue31891() { + Issue31891A a; + useIssue31891A(&a); + + Issue31891B b; + useIssue31891B(&b); +} diff --git a/misc/cgo/test/issue4029.c b/misc/cgo/test/issue4029.c new file mode 100644 index 0000000..e79c5a7 --- /dev/null +++ b/misc/cgo/test/issue4029.c @@ -0,0 +1,30 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows,!static +// +build !darwin !internal_pie,!arm64 + +#include <stdint.h> +#include <dlfcn.h> + +// Write our own versions of dlopen/dlsym/dlclose so that we represent +// the opaque handle as a Go uintptr rather than a Go pointer to avoid +// garbage collector confusion. See issue 23663. + +uintptr_t dlopen4029(char* name, int flags) { + return (uintptr_t)(dlopen(name, flags)); +} + +uintptr_t dlsym4029(uintptr_t handle, char* name) { + return (uintptr_t)(dlsym((void*)(handle), name)); +} + +int dlclose4029(uintptr_t handle) { + return dlclose((void*)(handle)); +} + +void call4029(void *arg) { + void (*fn)(void) = arg; + fn(); +} diff --git a/misc/cgo/test/issue4029.go b/misc/cgo/test/issue4029.go new file mode 100644 index 0000000..90ca08c --- /dev/null +++ b/misc/cgo/test/issue4029.go @@ -0,0 +1,80 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows && !static && (!darwin || (!internal_pie && !arm64)) +// +build !windows +// +build !static +// +build !darwin !internal_pie,!arm64 + +// Excluded in darwin internal linking PIE mode, as dynamic export is not +// supported. +// Excluded in internal linking mode on darwin/arm64, as it is always PIE. + +package cgotest + +/* +#include <stdint.h> +#include <dlfcn.h> +#cgo linux LDFLAGS: -ldl + +extern uintptr_t dlopen4029(char*, int); +extern uintptr_t dlsym4029(uintptr_t, char*); +extern int dlclose4029(uintptr_t); + +extern void call4029(uintptr_t arg); +*/ +import "C" + +import ( + "testing" +) + +var callbacks int + +//export IMPIsOpaque +func IMPIsOpaque() { + callbacks++ +} + +//export IMPInitWithFrame +func IMPInitWithFrame() { + callbacks++ +} + +//export IMPDrawRect +func IMPDrawRect() { + callbacks++ +} + +//export IMPWindowResize +func IMPWindowResize() { + callbacks++ +} + +func test4029(t *testing.T) { + loadThySelf(t, "IMPWindowResize") + loadThySelf(t, "IMPDrawRect") + loadThySelf(t, "IMPInitWithFrame") + loadThySelf(t, "IMPIsOpaque") + if callbacks != 4 { + t.Errorf("got %d callbacks, expected 4", callbacks) + } +} + +func loadThySelf(t *testing.T, symbol string) { + this_process := C.dlopen4029(nil, C.RTLD_NOW) + if this_process == 0 { + t.Error("dlopen:", C.GoString(C.dlerror())) + return + } + defer C.dlclose4029(this_process) + + symbol_address := C.dlsym4029(this_process, C.CString(symbol)) + if symbol_address == 0 { + t.Error("dlsym:", C.GoString(C.dlerror())) + return + } + t.Log(symbol, symbol_address) + C.call4029(symbol_address) +} diff --git a/misc/cgo/test/issue4029w.go b/misc/cgo/test/issue4029w.go new file mode 100644 index 0000000..c2f5948 --- /dev/null +++ b/misc/cgo/test/issue4029w.go @@ -0,0 +1,13 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build windows || static || (darwin && internal_pie) || (darwin && arm64) +// +build windows static darwin,internal_pie darwin,arm64 + +package cgotest + +import "testing" + +func test4029(t *testing.T) { +} diff --git a/misc/cgo/test/issue41761.go b/misc/cgo/test/issue41761.go new file mode 100644 index 0000000..ed45b46 --- /dev/null +++ b/misc/cgo/test/issue41761.go @@ -0,0 +1,20 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +/* + typedef struct S41761 S41761; +*/ +import "C" + +import ( + "misc/cgo/test/issue41761a" + "testing" +) + +func test41761(t *testing.T) { + var x issue41761a.T + _ = (*C.struct_S41761)(x.X) +} diff --git a/misc/cgo/test/issue41761a/a.go b/misc/cgo/test/issue41761a/a.go new file mode 100644 index 0000000..1c52782 --- /dev/null +++ b/misc/cgo/test/issue41761a/a.go @@ -0,0 +1,14 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue41761a + +/* + typedef struct S41761 S41761; +*/ +import "C" + +type T struct { + X *C.S41761 +} diff --git a/misc/cgo/test/issue42018.go b/misc/cgo/test/issue42018.go new file mode 100644 index 0000000..fab686a --- /dev/null +++ b/misc/cgo/test/issue42018.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows +// +build !windows + +package cgotest + +import "testing" + +func test42018(t *testing.T) { + t.Skip("skipping Windows-only test") +} diff --git a/misc/cgo/test/issue42018_windows.go b/misc/cgo/test/issue42018_windows.go new file mode 100644 index 0000000..8f4570a --- /dev/null +++ b/misc/cgo/test/issue42018_windows.go @@ -0,0 +1,46 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +/* +typedef void *HANDLE; + +struct HWND__{int unused;}; typedef struct HWND__ *HWND; +*/ +import "C" + +import ( + "testing" + "unsafe" +) + +func test42018(t *testing.T) { + // Test that Windows handles are marked go:notinheap, by growing the + // stack and checking for pointer adjustments. Trick from + // test/fixedbugs/issue40954.go. + var i int + handle := C.HANDLE(unsafe.Pointer(uintptr(unsafe.Pointer(&i)))) + recurseHANDLE(100, handle, uintptr(unsafe.Pointer(&i))) + hwnd := C.HWND(unsafe.Pointer(uintptr(unsafe.Pointer(&i)))) + recurseHWND(400, hwnd, uintptr(unsafe.Pointer(&i))) +} + +func recurseHANDLE(n int, p C.HANDLE, v uintptr) { + if n > 0 { + recurseHANDLE(n-1, p, v) + } + if uintptr(unsafe.Pointer(p)) != v { + panic("adjusted notinheap pointer") + } +} + +func recurseHWND(n int, p C.HWND, v uintptr) { + if n > 0 { + recurseHWND(n-1, p, v) + } + if uintptr(unsafe.Pointer(p)) != v { + panic("adjusted notinheap pointer") + } +} diff --git a/misc/cgo/test/issue42495.go b/misc/cgo/test/issue42495.go new file mode 100644 index 0000000..509a67d --- /dev/null +++ b/misc/cgo/test/issue42495.go @@ -0,0 +1,15 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +// typedef struct { } T42495A; +// typedef struct { int x[0]; } T42495B; +import "C" + +//export Issue42495A +func Issue42495A(C.T42495A) {} + +//export Issue42495B +func Issue42495B(C.T42495B) {} diff --git a/misc/cgo/test/issue4273.c b/misc/cgo/test/issue4273.c new file mode 100644 index 0000000..cac9876 --- /dev/null +++ b/misc/cgo/test/issue4273.c @@ -0,0 +1,10 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifdef __ELF__ +__attribute__((weak)) +__attribute__((visibility("hidden"))) +void _compilerrt_abort_impl(const char *file, int line, const char *func) { +} +#endif diff --git a/misc/cgo/test/issue4273b.c b/misc/cgo/test/issue4273b.c new file mode 100644 index 0000000..71e3f0d --- /dev/null +++ b/misc/cgo/test/issue4273b.c @@ -0,0 +1,11 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#ifdef __ELF__ +extern void _compilerrt_abort_impl(const char *file, int line, const char *func); + +void __my_abort(const char *file, int line, const char *func) { + _compilerrt_abort_impl(file, line, func); +} +#endif diff --git a/misc/cgo/test/issue4339.c b/misc/cgo/test/issue4339.c new file mode 100644 index 0000000..15d0004 --- /dev/null +++ b/misc/cgo/test/issue4339.c @@ -0,0 +1,18 @@ +#include <stdio.h> +#include "issue4339.h" + +static void +impl(void) +{ + //printf("impl\n"); +} + +Issue4339 exported4339 = {"bar", impl}; + +void +handle4339(Issue4339 *x) +{ + //printf("handle\n"); + x->bar(); + //printf("done\n"); +} diff --git a/misc/cgo/test/issue4339.h b/misc/cgo/test/issue4339.h new file mode 100644 index 0000000..20f6ceb --- /dev/null +++ b/misc/cgo/test/issue4339.h @@ -0,0 +1,9 @@ +typedef struct Issue4339 Issue4339; + +struct Issue4339 { + char *name; + void (*bar)(void); +}; + +extern Issue4339 exported4339; +void handle4339(Issue4339*); diff --git a/misc/cgo/test/issue43639.go b/misc/cgo/test/issue43639.go new file mode 100644 index 0000000..41e1471 --- /dev/null +++ b/misc/cgo/test/issue43639.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +// Issue 43639: No runtime test needed, make sure package misc/cgo/test/issue43639 compiles well. + +import _ "misc/cgo/test/issue43639" diff --git a/misc/cgo/test/issue43639/a.go b/misc/cgo/test/issue43639/a.go new file mode 100644 index 0000000..fe37d5e --- /dev/null +++ b/misc/cgo/test/issue43639/a.go @@ -0,0 +1,8 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue43639 + +// #cgo CFLAGS: -W -Wall -Werror +import "C" diff --git a/misc/cgo/test/issue52611.go b/misc/cgo/test/issue52611.go new file mode 100644 index 0000000..2b59d52 --- /dev/null +++ b/misc/cgo/test/issue52611.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 52611: inconsistent compiler behaviour when compiling a C.struct. +// No runtime test; just make sure it compiles. + +package cgotest + +import ( + _ "misc/cgo/test/issue52611a" + _ "misc/cgo/test/issue52611b" +) diff --git a/misc/cgo/test/issue52611a/a.go b/misc/cgo/test/issue52611a/a.go new file mode 100644 index 0000000..0764688 --- /dev/null +++ b/misc/cgo/test/issue52611a/a.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue52611a + +/* +typedef struct Foo { + int X; +} Foo; +*/ +import "C" + +func GetX1(foo *C.struct_Foo) int32 { + return int32(foo.X) +} diff --git a/misc/cgo/test/issue52611a/b.go b/misc/cgo/test/issue52611a/b.go new file mode 100644 index 0000000..74a50c5 --- /dev/null +++ b/misc/cgo/test/issue52611a/b.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue52611a + +import "C" + +func GetX2(foo *C.struct_Foo) int32 { + return int32(foo.X) +} diff --git a/misc/cgo/test/issue52611b/a.go b/misc/cgo/test/issue52611b/a.go new file mode 100644 index 0000000..730b52f --- /dev/null +++ b/misc/cgo/test/issue52611b/a.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue52611b + +import "C" + +func GetX1(bar *C.struct_Bar) int32 { + return int32(bar.X) +} diff --git a/misc/cgo/test/issue52611b/b.go b/misc/cgo/test/issue52611b/b.go new file mode 100644 index 0000000..d304175 --- /dev/null +++ b/misc/cgo/test/issue52611b/b.go @@ -0,0 +1,16 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue52611b + +/* +typedef struct Bar { + int X; +} Bar; +*/ +import "C" + +func GetX2(bar *C.struct_Bar) int32 { + return int32(bar.X) +} diff --git a/misc/cgo/test/issue5548_c.c b/misc/cgo/test/issue5548_c.c new file mode 100644 index 0000000..8411526 --- /dev/null +++ b/misc/cgo/test/issue5548_c.c @@ -0,0 +1,24 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "_cgo_export.h" + +static void clobber_stack() { + volatile char a[1024]; + int i; + for(i = 0; i < sizeof a; i++) + a[i] = 0xff; +} + +static int call_go() { + GoString s; + s.p = "test"; + s.n = 4; + return issue5548FromC(s, 42); +} + +int issue5548_in_c() { + clobber_stack(); + return call_go(); +} diff --git a/misc/cgo/test/issue5740a.c b/misc/cgo/test/issue5740a.c new file mode 100644 index 0000000..a6a7d0c --- /dev/null +++ b/misc/cgo/test/issue5740a.c @@ -0,0 +1,9 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +static int volatile val = 2; + +int test5740a() { + return val; +} diff --git a/misc/cgo/test/issue5740b.c b/misc/cgo/test/issue5740b.c new file mode 100644 index 0000000..c2ff5fb --- /dev/null +++ b/misc/cgo/test/issue5740b.c @@ -0,0 +1,9 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +static int volatile val = 3; + +int test5740b() { + return val; +} diff --git a/misc/cgo/test/issue6833_c.c b/misc/cgo/test/issue6833_c.c new file mode 100644 index 0000000..c94c2c6 --- /dev/null +++ b/misc/cgo/test/issue6833_c.c @@ -0,0 +1,10 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "_cgo_export.h" + +unsigned long long +issue6833Func(unsigned int aui, unsigned long long aull) { + return GoIssue6833Func(aui, aull); +} diff --git a/misc/cgo/test/issue6907export_c.c b/misc/cgo/test/issue6907export_c.c new file mode 100644 index 0000000..9b1a4fc --- /dev/null +++ b/misc/cgo/test/issue6907export_c.c @@ -0,0 +1,11 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <string.h> + +#include "_cgo_export.h" + +int CheckIssue6907C(_GoString_ s) { + return CheckIssue6907Go(s); +} diff --git a/misc/cgo/test/issue6997_linux.c b/misc/cgo/test/issue6997_linux.c new file mode 100644 index 0000000..de803d2 --- /dev/null +++ b/misc/cgo/test/issue6997_linux.c @@ -0,0 +1,28 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !android + +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +static pthread_t thread; + +static void* threadfunc(void* dummy) { + while(1) { + sleep(1); + } +} + +int StartThread() { + return pthread_create(&thread, NULL, &threadfunc, NULL); +} + +int CancelThread() { + void *r; + pthread_cancel(thread); + pthread_join(thread, &r); + return (r == PTHREAD_CANCELED); +} diff --git a/misc/cgo/test/issue6997_linux.go b/misc/cgo/test/issue6997_linux.go new file mode 100644 index 0000000..4acc8c1 --- /dev/null +++ b/misc/cgo/test/issue6997_linux.go @@ -0,0 +1,45 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !android +// +build !android + +// Test that pthread_cancel works as expected +// (NPTL uses SIGRTMIN to implement thread cancellation) +// See https://golang.org/issue/6997 +package cgotest + +/* +#cgo CFLAGS: -pthread +#cgo LDFLAGS: -pthread +extern int StartThread(); +extern int CancelThread(); +*/ +import "C" + +import ( + "testing" + "time" +) + +func test6997(t *testing.T) { + r := C.StartThread() + if r != 0 { + t.Error("pthread_create failed") + } + c := make(chan C.int) + go func() { + time.Sleep(500 * time.Millisecond) + c <- C.CancelThread() + }() + + select { + case r = <-c: + if r == 0 { + t.Error("pthread finished but wasn't canceled??") + } + case <-time.After(30 * time.Second): + t.Error("hung in pthread_cancel/pthread_join") + } +} diff --git a/misc/cgo/test/issue7234_test.go b/misc/cgo/test/issue7234_test.go new file mode 100644 index 0000000..c191a1a --- /dev/null +++ b/misc/cgo/test/issue7234_test.go @@ -0,0 +1,21 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import "testing" + +// This test actually doesn't have anything to do with cgo. It is a +// test of https://golang.org/issue/7234, a compiler/linker bug in +// handling string constants when using -linkmode=external. The test +// is in this directory because we routinely test -linkmode=external +// here. + +var v7234 = [...]string{"runtime/cgo"} + +func Test7234(t *testing.T) { + if v7234[0] != "runtime/cgo" { + t.Errorf("bad string constant %q", v7234[0]) + } +} diff --git a/misc/cgo/test/issue8148.c b/misc/cgo/test/issue8148.c new file mode 100644 index 0000000..927b434 --- /dev/null +++ b/misc/cgo/test/issue8148.c @@ -0,0 +1,11 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "_cgo_export.h" + +int get8148(void) { + T t; + t.i = 42; + return issue8148Callback(&t); +} diff --git a/misc/cgo/test/issue8148.go b/misc/cgo/test/issue8148.go new file mode 100644 index 0000000..aee9003 --- /dev/null +++ b/misc/cgo/test/issue8148.go @@ -0,0 +1,24 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 8148. A typedef of an unnamed struct didn't work when used +// with an exported Go function. No runtime test; just make sure it +// compiles. + +package cgotest + +/* +typedef struct { int i; } T; +int get8148(void); +*/ +import "C" + +//export issue8148Callback +func issue8148Callback(t *C.T) C.int { + return t.i +} + +func Issue8148() int { + return int(C.get8148()) +} diff --git a/misc/cgo/test/issue8331.h b/misc/cgo/test/issue8331.h new file mode 100644 index 0000000..8065be0 --- /dev/null +++ b/misc/cgo/test/issue8331.h @@ -0,0 +1,7 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +typedef struct { + int i; +} issue8331; diff --git a/misc/cgo/test/issue8517.go b/misc/cgo/test/issue8517.go new file mode 100644 index 0000000..7316ab0 --- /dev/null +++ b/misc/cgo/test/issue8517.go @@ -0,0 +1,14 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows +// +build !windows + +package cgotest + +import "testing" + +func test8517(t *testing.T) { + t.Skip("skipping windows only test") +} diff --git a/misc/cgo/test/issue8517_windows.c b/misc/cgo/test/issue8517_windows.c new file mode 100644 index 0000000..a0b94c1 --- /dev/null +++ b/misc/cgo/test/issue8517_windows.c @@ -0,0 +1,24 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "windows.h" + +extern void testHandleLeaksCallback(); + +DWORD WINAPI testHandleLeaksFunc(LPVOID lpThreadParameter) +{ + int i; + for(i = 0; i < 100; i++) { + testHandleLeaksCallback(); + } + return 0; +} + +void testHandleLeaks() +{ + HANDLE h; + h = CreateThread(NULL, 0, &testHandleLeaksFunc, 0, 0, NULL); + WaitForSingleObject(h, INFINITE); + CloseHandle(h); +} diff --git a/misc/cgo/test/issue8517_windows.go b/misc/cgo/test/issue8517_windows.go new file mode 100644 index 0000000..3782631 --- /dev/null +++ b/misc/cgo/test/issue8517_windows.go @@ -0,0 +1,45 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +//void testHandleLeaks(); +import "C" + +import ( + "syscall" + "testing" + "unsafe" +) + +var issue8517counter int + +var ( + kernel32 = syscall.MustLoadDLL("kernel32.dll") + getProcessHandleCount = kernel32.MustFindProc("GetProcessHandleCount") +) + +func processHandleCount(t *testing.T) int { + const current_process = ^uintptr(0) + var c uint32 + r, _, err := getProcessHandleCount.Call(current_process, uintptr(unsafe.Pointer(&c))) + if r == 0 { + t.Fatal(err) + } + return int(c) +} + +func test8517(t *testing.T) { + c1 := processHandleCount(t) + C.testHandleLeaks() + c2 := processHandleCount(t) + if c1+issue8517counter <= c2 { + t.Fatalf("too many handles leaked: issue8517counter=%v c1=%v c2=%v", issue8517counter, c1, c2) + } +} + +//export testHandleLeaksCallback +func testHandleLeaksCallback() { + issue8517counter++ +} diff --git a/misc/cgo/test/issue8694.go b/misc/cgo/test/issue8694.go new file mode 100644 index 0000000..19071ce --- /dev/null +++ b/misc/cgo/test/issue8694.go @@ -0,0 +1,41 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !android +// +build !android + +package cgotest + +/* +#include <complex.h> + +complex float complexFloatSquared(complex float a) { return a*a; } +complex double complexDoubleSquared(complex double a) { return a*a; } +*/ +import "C" + +import ( + "runtime" + "testing" +) + +func test8694(t *testing.T) { + if runtime.GOARCH == "arm" { + t.Skip("test8694 is disabled on ARM because 5l cannot handle thumb library.") + } + // Really just testing that this compiles, but check answer anyway. + x := C.complexfloat(2 + 3i) + x2 := x * x + cx2 := C.complexFloatSquared(x) + if cx2 != x2 { + t.Errorf("C.complexFloatSquared(%v) = %v, want %v", x, cx2, x2) + } + + y := C.complexdouble(2 + 3i) + y2 := y * y + cy2 := C.complexDoubleSquared(y) + if cy2 != y2 { + t.Errorf("C.complexDoubleSquared(%v) = %v, want %v", y, cy2, y2) + } +} diff --git a/misc/cgo/test/issue8756.go b/misc/cgo/test/issue8756.go new file mode 100644 index 0000000..08099dc --- /dev/null +++ b/misc/cgo/test/issue8756.go @@ -0,0 +1,17 @@ +package cgotest + +/* +#cgo LDFLAGS: -lm +#include <math.h> +*/ +import "C" +import ( + "testing" + + "misc/cgo/test/issue8756" +) + +func test8756(t *testing.T) { + issue8756.Pow() + C.pow(1, 2) +} diff --git a/misc/cgo/test/issue8756/issue8756.go b/misc/cgo/test/issue8756/issue8756.go new file mode 100644 index 0000000..5f6b777 --- /dev/null +++ b/misc/cgo/test/issue8756/issue8756.go @@ -0,0 +1,11 @@ +package issue8756 + +/* +#cgo LDFLAGS: -lm +#include <math.h> +*/ +import "C" + +func Pow() { + C.pow(1, 2) +} diff --git a/misc/cgo/test/issue8811.c b/misc/cgo/test/issue8811.c new file mode 100644 index 0000000..41b3c7c --- /dev/null +++ b/misc/cgo/test/issue8811.c @@ -0,0 +1,8 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +int issue8811Initialized = 0; + +void issue8811Init() { +} diff --git a/misc/cgo/test/issue8828.go b/misc/cgo/test/issue8828.go new file mode 100644 index 0000000..3b5765d --- /dev/null +++ b/misc/cgo/test/issue8828.go @@ -0,0 +1,16 @@ +// compile + +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 8828: compiling a file with -compiler=gccgo fails if a .c file +// has the same name as compiled directory. + +package cgotest + +import "misc/cgo/test/issue8828" + +func p() { + issue8828.Bar() +} diff --git a/misc/cgo/test/issue8828/issue8828.c b/misc/cgo/test/issue8828/issue8828.c new file mode 100644 index 0000000..27ec23a --- /dev/null +++ b/misc/cgo/test/issue8828/issue8828.c @@ -0,0 +1,7 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +void foo() +{ +} diff --git a/misc/cgo/test/issue8828/trivial.go b/misc/cgo/test/issue8828/trivial.go new file mode 100644 index 0000000..e7b9a4e --- /dev/null +++ b/misc/cgo/test/issue8828/trivial.go @@ -0,0 +1,8 @@ +package issue8828 + +//void foo(); +import "C" + +func Bar() { + C.foo() +} diff --git a/misc/cgo/test/issue9026.go b/misc/cgo/test/issue9026.go new file mode 100644 index 0000000..a30196c --- /dev/null +++ b/misc/cgo/test/issue9026.go @@ -0,0 +1,9 @@ +package cgotest + +import ( + "testing" + + "misc/cgo/test/issue9026" +) + +func test9026(t *testing.T) { issue9026.Test(t) } diff --git a/misc/cgo/test/issue9026/issue9026.go b/misc/cgo/test/issue9026/issue9026.go new file mode 100644 index 0000000..ff269ca --- /dev/null +++ b/misc/cgo/test/issue9026/issue9026.go @@ -0,0 +1,36 @@ +package issue9026 + +// This file appears in its own package since the assertion tests the +// per-package counter used to create fresh identifiers. + +/* +typedef struct { int i; } git_merge_file_input; + +typedef struct { int j; } git_merge_file_options; + +void git_merge_file( + git_merge_file_input *in, + git_merge_file_options *opts) {} +*/ +import "C" +import ( + "fmt" + "testing" +) + +func Test(t *testing.T) { + var in C.git_merge_file_input + var opts *C.git_merge_file_options + C.git_merge_file(&in, opts) + + // Test that the generated type names are deterministic. + // (Previously this would fail about 10% of the time.) + // + // Brittle: the assertion may fail spuriously when the algorithm + // changes, but should remain stable otherwise. + got := fmt.Sprintf("%T %T", in, opts) + want := "issue9026._Ctype_struct___0 *issue9026._Ctype_struct___1" + if got != want { + t.Errorf("Non-deterministic type names: got %s, want %s", got, want) + } +} diff --git a/misc/cgo/test/issue9400/asm_386.s b/misc/cgo/test/issue9400/asm_386.s new file mode 100644 index 0000000..96b8b60 --- /dev/null +++ b/misc/cgo/test/issue9400/asm_386.s @@ -0,0 +1,27 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gc + +#include "textflag.h" + +TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0 + MOVL $·Baton(SB), BX + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADDL $(1024 * 8), SP + + // Ask signaller to setgid + MOVL $1, (BX) + + // Wait for setgid completion +loop: + PAUSE + MOVL (BX), AX + CMPL AX, $0 + JNE loop + + // Restore stack + SUBL $(1024 * 8), SP + RET diff --git a/misc/cgo/test/issue9400/asm_amd64x.s b/misc/cgo/test/issue9400/asm_amd64x.s new file mode 100644 index 0000000..99509bc --- /dev/null +++ b/misc/cgo/test/issue9400/asm_amd64x.s @@ -0,0 +1,27 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64 amd64p32 +// +build gc + +#include "textflag.h" + +TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0 + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADDQ $(1024 * 8), SP + + // Ask signaller to setgid + MOVL $1, ·Baton(SB) + + // Wait for setgid completion +loop: + PAUSE + MOVL ·Baton(SB), AX + CMPL AX, $0 + JNE loop + + // Restore stack + SUBQ $(1024 * 8), SP + RET diff --git a/misc/cgo/test/issue9400/asm_arm.s b/misc/cgo/test/issue9400/asm_arm.s new file mode 100644 index 0000000..cc92856 --- /dev/null +++ b/misc/cgo/test/issue9400/asm_arm.s @@ -0,0 +1,39 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gc + +#include "textflag.h" + +TEXT cas<>(SB),NOSPLIT,$0 + MOVW $0xffff0fc0, R15 // R15 is PC + +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 + // Save link register + MOVW R14, R4 + + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADD $(1024 * 8), R13 + + // Ask signaller to setgid + MOVW $·Baton(SB), R2 +storeloop: + MOVW 0(R2), R0 + MOVW $1, R1 + BL cas<>(SB) + BCC storeloop + + // Wait for setgid completion +loop: + MOVW $0, R0 + MOVW $0, R1 + BL cas<>(SB) + BCC loop + + // Restore stack + SUB $(1024 * 8), R13 + + MOVW R4, R14 + RET diff --git a/misc/cgo/test/issue9400/asm_arm64.s b/misc/cgo/test/issue9400/asm_arm64.s new file mode 100644 index 0000000..2565793 --- /dev/null +++ b/misc/cgo/test/issue9400/asm_arm64.s @@ -0,0 +1,39 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gc + +#include "textflag.h" + +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 + // Save link register + MOVD R30, R9 + + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADD $(1024 * 8), RSP + + // Ask signaller to setgid + MOVD $·Baton(SB), R0 + MOVD $1, R1 +storeloop: + LDAXRW (R0), R2 + STLXRW R1, (R0), R3 + CBNZ R3, storeloop + + // Wait for setgid completion + MOVW $0, R1 + MOVW $0, R2 +loop: + LDAXRW (R0), R3 + CMPW R1, R3 + BNE loop + STLXRW R2, (R0), R3 + CBNZ R3, loop + + // Restore stack + SUB $(1024 * 8), RSP + + MOVD R9, R30 + RET diff --git a/misc/cgo/test/issue9400/asm_loong64.s b/misc/cgo/test/issue9400/asm_loong64.s new file mode 100644 index 0000000..c242fc6 --- /dev/null +++ b/misc/cgo/test/issue9400/asm_loong64.s @@ -0,0 +1,28 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADDV $(1024*8), R3 + + // Ask signaller to setgid + MOVW $1, R12 + DBAR + MOVW R12, ·Baton(SB) + DBAR + + // Wait for setgid completion +loop: + DBAR + MOVW ·Baton(SB), R12 + OR R13, R13, R13 // hint that we're in a spin loop + BNE R12, loop + DBAR + + // Restore stack + ADDV $(-1024*8), R3 + RET diff --git a/misc/cgo/test/issue9400/asm_mips64x.s b/misc/cgo/test/issue9400/asm_mips64x.s new file mode 100644 index 0000000..693231d --- /dev/null +++ b/misc/cgo/test/issue9400/asm_mips64x.s @@ -0,0 +1,33 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build mips64 mips64le +// +build gc + +#include "textflag.h" + +#define SYNC WORD $0xf + +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADDV $(1024*8), R29 + + // Ask signaller to setgid + MOVW $1, R1 + SYNC + MOVW R1, ·Baton(SB) + SYNC + + // Wait for setgid completion +loop: + SYNC + MOVW ·Baton(SB), R1 + OR R2, R2, R2 // hint that we're in a spin loop + BNE R1, loop + SYNC + + // Restore stack + ADDV $(-1024*8), R29 + RET diff --git a/misc/cgo/test/issue9400/asm_mipsx.s b/misc/cgo/test/issue9400/asm_mipsx.s new file mode 100644 index 0000000..63261bb --- /dev/null +++ b/misc/cgo/test/issue9400/asm_mipsx.s @@ -0,0 +1,31 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build mips mipsle +// +build gc + +#include "textflag.h" + +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADDU $(1024*8), R29 + + // Ask signaller to setgid + MOVW $1, R1 + SYNC + MOVW R1, ·Baton(SB) + SYNC + + // Wait for setgid completion +loop: + SYNC + MOVW ·Baton(SB), R1 + OR R2, R2, R2 // hint that we're in a spin loop + BNE R1, loop + SYNC + + // Restore stack + ADDU $(-1024*8), R29 + RET diff --git a/misc/cgo/test/issue9400/asm_ppc64x.s b/misc/cgo/test/issue9400/asm_ppc64x.s new file mode 100644 index 0000000..b5613fb --- /dev/null +++ b/misc/cgo/test/issue9400/asm_ppc64x.s @@ -0,0 +1,32 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ppc64 ppc64le +// +build gc + +#include "textflag.h" + +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADD $(1024 * 8), R1 + + // Ask signaller to setgid + MOVW $1, R3 + SYNC + MOVW R3, ·Baton(SB) + + // Wait for setgid completion +loop: + SYNC + MOVW ·Baton(SB), R3 + CMP R3, $0 + // Hint that we're in a spin loop + OR R1, R1, R1 + BNE loop + ISYNC + + // Restore stack + SUB $(1024 * 8), R1 + RET diff --git a/misc/cgo/test/issue9400/asm_riscv64.s b/misc/cgo/test/issue9400/asm_riscv64.s new file mode 100644 index 0000000..244dada --- /dev/null +++ b/misc/cgo/test/issue9400/asm_riscv64.s @@ -0,0 +1,31 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build riscv64 +// +build gc + +#include "textflag.h" + +TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0 + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADD $(1024*8), X2 + + // Ask signaller to setgid + MOV $1, X5 + FENCE + MOVW X5, ·Baton(SB) + FENCE + + // Wait for setgid completion +loop: + FENCE + MOVW ·Baton(SB), X5 + OR X6, X6, X6 // hint that we're in a spin loop + BNE ZERO, X5, loop + FENCE + + // Restore stack + ADD $(-1024*8), X2 + RET diff --git a/misc/cgo/test/issue9400/asm_s390x.s b/misc/cgo/test/issue9400/asm_s390x.s new file mode 100644 index 0000000..4856492 --- /dev/null +++ b/misc/cgo/test/issue9400/asm_s390x.s @@ -0,0 +1,26 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gc + +#include "textflag.h" + +TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0 + // Rewind stack pointer so anything that happens on the stack + // will clobber the test pattern created by the caller + ADD $(1024 * 8), R15 + + // Ask signaller to setgid + MOVD $·Baton(SB), R5 + MOVW $1, 0(R5) + + // Wait for setgid completion +loop: + SYNC + MOVW ·Baton(SB), R3 + CMPBNE R3, $0, loop + + // Restore stack + SUB $(1024 * 8), R15 + RET diff --git a/misc/cgo/test/issue9400/gccgo.go b/misc/cgo/test/issue9400/gccgo.go new file mode 100644 index 0000000..a9b62b0 --- /dev/null +++ b/misc/cgo/test/issue9400/gccgo.go @@ -0,0 +1,26 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gccgo + +package issue9400 + +import ( + "runtime" + "sync/atomic" +) + +// The test for the gc compiler resets the stack pointer so that the +// stack gets modified. We don't have a way to do that for gccgo +// without writing more assembly code, which we haven't bothered to +// do. So this is not much of a test. + +var Baton int32 + +func RewindAndSetgid() { + atomic.StoreInt32(&Baton, 1) + for atomic.LoadInt32(&Baton) != 0 { + runtime.Gosched() + } +} diff --git a/misc/cgo/test/issue9400/stubs.go b/misc/cgo/test/issue9400/stubs.go new file mode 100644 index 0000000..e431c5a --- /dev/null +++ b/misc/cgo/test/issue9400/stubs.go @@ -0,0 +1,11 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gc + +package issue9400 + +var Baton int32 + +func RewindAndSetgid() diff --git a/misc/cgo/test/issue9400_linux.go b/misc/cgo/test/issue9400_linux.go new file mode 100644 index 0000000..38fa9dd --- /dev/null +++ b/misc/cgo/test/issue9400_linux.go @@ -0,0 +1,67 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that SIGSETXID runs on signal stack, since it's likely to +// overflow if it runs on the Go stack. + +package cgotest + +/* +#include <sys/types.h> +#include <unistd.h> +*/ +import "C" + +import ( + "runtime" + "runtime/debug" + "sync/atomic" + "testing" + + "misc/cgo/test/issue9400" +) + +func test9400(t *testing.T) { + // We synchronize through a shared variable, so we need two procs + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) + + // Start signaller + atomic.StoreInt32(&issue9400.Baton, 0) + go func() { + // Wait for RewindAndSetgid + for atomic.LoadInt32(&issue9400.Baton) == 0 { + runtime.Gosched() + } + // Broadcast SIGSETXID + runtime.LockOSThread() + C.setgid(0) + // Indicate that signalling is done + atomic.StoreInt32(&issue9400.Baton, 0) + }() + + // Grow the stack and put down a test pattern + const pattern = 0x123456789abcdef + var big [1024]uint64 // len must match assembly + for i := range big { + big[i] = pattern + } + + // Disable GC for the duration of the test. + // This avoids a potential GC deadlock when spinning in uninterruptable ASM below #49695. + defer debug.SetGCPercent(debug.SetGCPercent(-1)) + // SetGCPercent waits until the mark phase is over, but the runtime + // also preempts at the start of the sweep phase, so make sure that's + // done too. See #49695. + runtime.GC() + + // Temporarily rewind the stack and trigger SIGSETXID + issue9400.RewindAndSetgid() + + // Check test pattern + for i := range big { + if big[i] != pattern { + t.Fatalf("entry %d of test pattern is wrong; %#x != %#x", i, big[i], uint64(pattern)) + } + } +} diff --git a/misc/cgo/test/issue9510.go b/misc/cgo/test/issue9510.go new file mode 100644 index 0000000..325b0cf --- /dev/null +++ b/misc/cgo/test/issue9510.go @@ -0,0 +1,24 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that we can link together two different cgo packages that both +// use the same libgcc function. + +package cgotest + +import ( + "runtime" + "testing" + + "misc/cgo/test/issue9510a" + "misc/cgo/test/issue9510b" +) + +func test9510(t *testing.T) { + if runtime.GOARCH == "arm" { + t.Skip("skipping because libgcc may be a Thumb library") + } + issue9510a.F(1, 1) + issue9510b.F(1, 1) +} diff --git a/misc/cgo/test/issue9510a/a.go b/misc/cgo/test/issue9510a/a.go new file mode 100644 index 0000000..1a5224b --- /dev/null +++ b/misc/cgo/test/issue9510a/a.go @@ -0,0 +1,15 @@ +package issue9510a + +/* +static double csquare(double a, double b) { + __complex__ double d; + __real__ d = a; + __imag__ d = b; + return __real__ (d * d); +} +*/ +import "C" + +func F(a, b float64) float64 { + return float64(C.csquare(C.double(a), C.double(b))) +} diff --git a/misc/cgo/test/issue9510b/b.go b/misc/cgo/test/issue9510b/b.go new file mode 100644 index 0000000..5016b39 --- /dev/null +++ b/misc/cgo/test/issue9510b/b.go @@ -0,0 +1,15 @@ +package issue9510b + +/* +static double csquare(double a, double b) { + __complex__ double d; + __real__ d = a; + __imag__ d = b; + return __real__ (d * d); +} +*/ +import "C" + +func F(a, b float64) float64 { + return float64(C.csquare(C.double(a), C.double(b))) +} diff --git a/misc/cgo/test/setgid2_linux.go b/misc/cgo/test/setgid2_linux.go new file mode 100644 index 0000000..438f5ae --- /dev/null +++ b/misc/cgo/test/setgid2_linux.go @@ -0,0 +1,35 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Stress test setgid and thread creation. A thread +// can get a SIGSETXID signal early on at thread +// initialization, causing crash. See issue 53374. + +package cgotest + +/* +#include <sys/types.h> +#include <unistd.h> +*/ +import "C" + +import ( + "runtime" + "testing" +) + +func testSetgidStress(t *testing.T) { + const N = 50 + ch := make(chan int, N) + for i := 0; i < N; i++ { + go func() { + C.setgid(0) + ch <- 1 + runtime.LockOSThread() // so every goroutine uses a new thread + }() + } + for i := 0; i < N; i++ { + <-ch + } +} diff --git a/misc/cgo/test/setgid_linux.go b/misc/cgo/test/setgid_linux.go new file mode 100644 index 0000000..7c64946 --- /dev/null +++ b/misc/cgo/test/setgid_linux.go @@ -0,0 +1,49 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that setgid does not hang on Linux. +// See https://golang.org/issue/3871 for details. + +package cgotest + +/* +#include <sys/types.h> +#include <unistd.h> +*/ +import "C" + +import ( + "os" + "os/signal" + "syscall" + "testing" + "time" +) + +func runTestSetgid() bool { + c := make(chan bool) + go func() { + C.setgid(0) + c <- true + }() + select { + case <-c: + return true + case <-time.After(5 * time.Second): + return false + } + +} + +func testSetgid(t *testing.T) { + if !runTestSetgid() { + t.Error("setgid hung") + } + + // Now try it again after using signal.Notify. + signal.Notify(make(chan os.Signal, 1), syscall.SIGINT) + if !runTestSetgid() { + t.Error("setgid hung after signal.Notify") + } +} diff --git a/misc/cgo/test/sigaltstack.go b/misc/cgo/test/sigaltstack.go new file mode 100644 index 0000000..6b37189 --- /dev/null +++ b/misc/cgo/test/sigaltstack.go @@ -0,0 +1,79 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows && !android +// +build !windows,!android + +// Test that the Go runtime still works if C code changes the signal stack. + +package cgotest + +/* +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _AIX +// On AIX, SIGSTKSZ is too small to handle Go sighandler. +#define CSIGSTKSZ 0x4000 +#else +#define CSIGSTKSZ SIGSTKSZ +#endif + +static stack_t oss; +static char signalStack[CSIGSTKSZ]; + +static void changeSignalStack(void) { + stack_t ss; + memset(&ss, 0, sizeof ss); + ss.ss_sp = signalStack; + ss.ss_flags = 0; + ss.ss_size = CSIGSTKSZ; + if (sigaltstack(&ss, &oss) < 0) { + perror("sigaltstack"); + abort(); + } +} + +static void restoreSignalStack(void) { +#if (defined(__x86_64__) || defined(__i386__)) && defined(__APPLE__) + // The Darwin C library enforces a minimum that the kernel does not. + // This is OK since we allocated this much space in mpreinit, + // it was just removed from the buffer by stackalloc. + oss.ss_size = MINSIGSTKSZ; +#endif + if (sigaltstack(&oss, NULL) < 0) { + perror("sigaltstack restore"); + abort(); + } +} + +static int zero(void) { + return 0; +} +*/ +import "C" + +import ( + "runtime" + "testing" +) + +func testSigaltstack(t *testing.T) { + switch { + case runtime.GOOS == "solaris", runtime.GOOS == "illumos", runtime.GOOS == "ios" && runtime.GOARCH == "arm64": + t.Skipf("switching signal stack not implemented on %s/%s", runtime.GOOS, runtime.GOARCH) + } + + C.changeSignalStack() + defer C.restoreSignalStack() + defer func() { + if recover() == nil { + t.Error("did not see expected panic") + } + }() + v := 1 / int(C.zero()) + t.Errorf("unexpected success of division by zero == %d", v) +} diff --git a/misc/cgo/test/sigprocmask.c b/misc/cgo/test/sigprocmask.c new file mode 100644 index 0000000..e77ba5b --- /dev/null +++ b/misc/cgo/test/sigprocmask.c @@ -0,0 +1,51 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows + +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <pthread.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> + +extern void IntoGoAndBack(); + +int CheckBlocked() { + sigset_t mask; + sigprocmask(SIG_BLOCK, NULL, &mask); + return sigismember(&mask, SIGIO); +} + +static void* sigthreadfunc(void* unused) { + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGIO); + sigprocmask(SIG_BLOCK, &mask, NULL); + IntoGoAndBack(); + return NULL; +} + +int RunSigThread() { + int tries; + pthread_t thread; + int r; + struct timespec ts; + + for (tries = 0; tries < 20; tries++) { + r = pthread_create(&thread, NULL, &sigthreadfunc, NULL); + if (r == 0) { + return pthread_join(thread, NULL); + } + if (r != EAGAIN) { + return r; + } + ts.tv_sec = 0; + ts.tv_nsec = (tries + 1) * 1000 * 1000; // Milliseconds. + nanosleep(&ts, NULL); + } + return EAGAIN; +} diff --git a/misc/cgo/test/sigprocmask.go b/misc/cgo/test/sigprocmask.go new file mode 100644 index 0000000..983734c --- /dev/null +++ b/misc/cgo/test/sigprocmask.go @@ -0,0 +1,41 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows +// +build !windows + +package cgotest + +/* +#cgo CFLAGS: -pthread +#cgo LDFLAGS: -pthread +extern int RunSigThread(); +extern int CheckBlocked(); +*/ +import "C" +import ( + "os" + "os/signal" + "syscall" + "testing" +) + +var blocked bool + +//export IntoGoAndBack +func IntoGoAndBack() { + // Verify that SIGIO stays blocked on the C thread + // even when unblocked for signal.Notify(). + signal.Notify(make(chan os.Signal), syscall.SIGIO) + blocked = C.CheckBlocked() != 0 +} + +func testSigprocmask(t *testing.T) { + if r := C.RunSigThread(); r != 0 { + t.Errorf("pthread_create/pthread_join failed: %d", r) + } + if !blocked { + t.Error("Go runtime unblocked SIGIO") + } +} diff --git a/misc/cgo/test/test.go b/misc/cgo/test/test.go new file mode 100644 index 0000000..9d9b14e --- /dev/null +++ b/misc/cgo/test/test.go @@ -0,0 +1,2303 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test cases for cgo. +// Both the import "C" prologue and the main file are sorted by issue number. +// This file contains C definitions (not just declarations) +// and so it must NOT contain any //export directives on Go functions. +// See testx.go for exports. + +package cgotest + +/* +#include <complex.h> +#include <math.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <errno.h> +#cgo LDFLAGS: -lm + +#ifndef WIN32 +#include <pthread.h> +#include <signal.h> +#endif + +// alignment tests + +typedef unsigned char Uint8; +typedef unsigned short Uint16; + +typedef enum { + MOD1 = 0x0000, + MODX = 0x8000 +} SDLMod; + +typedef enum { + A1 = 1, + B1 = 322, + SDLK_LAST +} SDLKey; + +typedef struct SDL_keysym { + Uint8 scancode; + SDLKey sym; + SDLMod mod; + Uint16 unicode; +} SDL_keysym; + +typedef struct SDL_KeyboardEvent { + Uint8 typ; + Uint8 which; + Uint8 state; + SDL_keysym keysym; +} SDL_KeyboardEvent; + +void makeEvent(SDL_KeyboardEvent *event) { + unsigned char *p; + int i; + + p = (unsigned char*)event; + for (i=0; i<sizeof *event; i++) { + p[i] = i; + } +} + +int same(SDL_KeyboardEvent* e, Uint8 typ, Uint8 which, Uint8 state, Uint8 scan, SDLKey sym, SDLMod mod, Uint16 uni) { + return e->typ == typ && e->which == which && e->state == state && e->keysym.scancode == scan && e->keysym.sym == sym && e->keysym.mod == mod && e->keysym.unicode == uni; +} + +void cTest(SDL_KeyboardEvent *event) { + printf("C: %#x %#x %#x %#x %#x %#x %#x\n", event->typ, event->which, event->state, + event->keysym.scancode, event->keysym.sym, event->keysym.mod, event->keysym.unicode); + fflush(stdout); +} + +// api + +const char *greeting = "hello, world"; + +// basic test cases + +#define SHIFT(x, y) ((x)<<(y)) +#define KILO SHIFT(1, 10) +#define UINT32VAL 0xc008427bU + +enum E { + Enum1 = 1, + Enum2 = 2, +}; + +typedef unsigned char cgo_uuid_t[20]; + +void uuid_generate(cgo_uuid_t x) { + x[0] = 0; +} + +struct S { + int x; +}; + +const char *cstr = "abcefghijklmnopqrstuvwxyzABCEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + +extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter); + +enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; } + +int add(int x, int y) { + return x+y; +}; + +// Following mimicks vulkan complex definitions for benchmarking cgocheck overhead. + +typedef uint32_t VkFlags; +typedef VkFlags VkDeviceQueueCreateFlags; +typedef uint32_t VkStructureType; + +typedef struct VkDeviceQueueCreateInfo { + VkStructureType sType; + const void* pNext; + VkDeviceQueueCreateFlags flags; + uint32_t queueFamilyIndex; + uint32_t queueCount; + const float* pQueuePriorities; +} VkDeviceQueueCreateInfo; + +typedef struct VkPhysicalDeviceFeatures { + uint32_t bools[56]; +} VkPhysicalDeviceFeatures; + +typedef struct VkDeviceCreateInfo { + VkStructureType sType; + const void* pNext; + VkFlags flags; + uint32_t queueCreateInfoCount; + const VkDeviceQueueCreateInfo* pQueueCreateInfos; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; + const VkPhysicalDeviceFeatures* pEnabledFeatures; +} VkDeviceCreateInfo; + +void handleComplexPointer(VkDeviceCreateInfo *a0) {} +void handleComplexPointer8( + VkDeviceCreateInfo *a0, VkDeviceCreateInfo *a1, VkDeviceCreateInfo *a2, VkDeviceCreateInfo *a3, + VkDeviceCreateInfo *a4, VkDeviceCreateInfo *a5, VkDeviceCreateInfo *a6, VkDeviceCreateInfo *a7 +) {} + +// complex alignment + +struct { + float x; + _Complex float y; +} cplxAlign = { 3.14, 2.17 }; + +// constants and pointer checking + +#define CheckConstVal 0 + +typedef struct { + int *p; +} CheckConstStruct; + +static void CheckConstFunc(CheckConstStruct *p, int e) {} + +// duplicate symbol + +int base_symbol = 0; +#define alias_one base_symbol +#define alias_two base_symbol + +// function pointer variables + +typedef int (*intFunc) (); + +int +bridge_int_func(intFunc f) +{ + return f(); +} + +int fortytwo() +{ + return 42; +} + +// issue 1222 +typedef union { + long align; +} xxpthread_mutex_t; +struct ibv_async_event { + union { + int x; + } element; +}; +struct ibv_context { + xxpthread_mutex_t mutex; +}; + +// issue 1635 +// Mac OS X's gcc will generate scattered relocation 2/1 for +// this function on Darwin/386, and 8l couldn't handle it. +// this example is in issue 1635 +void scatter() { + void *p = scatter; + printf("scatter = %p\n", p); +} + +// Adding this explicit extern declaration makes this a test for +// https://gcc.gnu.org/PR68072 aka https://golang.org/issue/13344 . +// It used to cause a cgo error when building with GCC 6. +extern int hola; + +// this example is in issue 3253 +int hola = 0; +int testHola() { return hola; } + +// issue 3250 +#ifdef WIN32 +void testSendSIG() {} +#else +static void *thread(void *p) { + const int M = 100; + int i; + (void)p; + for (i = 0; i < M; i++) { + pthread_kill(pthread_self(), SIGCHLD); + usleep(rand() % 20 + 5); + } + return NULL; +} +void testSendSIG() { + const int N = 20; + int i; + pthread_t tid[N]; + for (i = 0; i < N; i++) { + usleep(rand() % 200 + 100); + pthread_create(&tid[i], 0, thread, NULL); + } + for (i = 0; i < N; i++) + pthread_join(tid[i], 0); +} +#endif + +// issue 3261 +// libgcc on ARM might be compiled as thumb code, but our 5l +// can't handle that, so we have to disable this test on arm. +#ifdef __ARMEL__ +int vabs(int x) { + puts("testLibgcc is disabled on ARM because 5l cannot handle thumb library."); + return (x < 0) ? -x : x; +} +#elif defined(__arm64__) && defined(__clang__) +int vabs(int x) { + puts("testLibgcc is disabled on ARM64 with clang due to lack of libgcc."); + return (x < 0) ? -x : x; +} +#else +int __absvsi2(int); // dummy prototype for libgcc function +// we shouldn't name the function abs, as gcc might use +// the builtin one. +int vabs(int x) { return __absvsi2(x); } +#endif + + +// issue 3729 +// access errno from void C function +const char _expA = 0x42; +const float _expB = 3.14159; +const short _expC = 0x55aa; +const int _expD = 0xdeadbeef; + +#ifdef WIN32 +void g(void) {} +void g2(int x, char a, float b, short c, int d) {} +#else + +void g(void) { + errno = E2BIG; +} + +// try to pass some non-trivial arguments to function g2 +void g2(int x, char a, float b, short c, int d) { + if (a == _expA && b == _expB && c == _expC && d == _expD) + errno = x; + else + errno = -1; +} +#endif + +// issue 3945 +// Test that cgo reserves enough stack space during cgo call. +// See https://golang.org/issue/3945 for details. +void say() { + printf("%s from C\n", "hello"); +} + +// issue 4054 part 1 - other half in testx.go + +typedef enum { + A = 0, + B, + C, + D, + E, + F, + G, + H, + II, + J, +} issue4054a; + +// issue 4339 +// We've historically permitted #include <>, so test it here. Issue 29333. +// Also see issue 41059. +#include <issue4339.h> + +// issue 4417 +// cmd/cgo: bool alignment/padding issue. +// bool alignment is wrong and causing wrong arguments when calling functions. +static int c_bool(bool a, bool b, int c, bool d, bool e) { + return c; +} + +// issue 4857 +#cgo CFLAGS: -Werror +const struct { int a; } *issue4857() { return (void *)0; } + +// issue 5224 +// Test that the #cgo CFLAGS directive works, +// with and without platform filters. +#cgo CFLAGS: -DCOMMON_VALUE=123 +#cgo windows CFLAGS: -DIS_WINDOWS=1 +#cgo !windows CFLAGS: -DIS_WINDOWS=0 +int common = COMMON_VALUE; +int is_windows = IS_WINDOWS; + +// issue 5227 +// linker incorrectly treats common symbols and +// leaves them undefined. + +typedef struct { + int Count; +} Fontinfo; + +Fontinfo SansTypeface; + +extern void init(); + +Fontinfo loadfont() { + Fontinfo f = {0}; + return f; +} + +void init() { + SansTypeface = loadfont(); +} + +// issue 5242 +// Cgo incorrectly computed the alignment of structs +// with no Go accessible fields as 0, and then panicked on +// modulo-by-zero computations. + +// issue 50987 +// disable arm64 GCC warnings +#cgo CFLAGS: -Wno-psabi -Wno-unknown-warning-option + +typedef struct { +} foo; + +typedef struct { + int x : 1; +} bar; + +int issue5242(foo f, bar b) { + return 5242; +} + +// issue 5337 +// Verify that we can withstand SIGPROF received on foreign threads + +#ifdef WIN32 +void test5337() {} +#else +static void *thread1(void *p) { + (void)p; + pthread_kill(pthread_self(), SIGPROF); + return NULL; +} +void test5337() { + pthread_t tid; + pthread_create(&tid, 0, thread1, NULL); + pthread_join(tid, 0); +} +#endif + +// issue 5603 + +const long long issue5603exp = 0x12345678; +long long issue5603foo0() { return issue5603exp; } +long long issue5603foo1(void *p) { return issue5603exp; } +long long issue5603foo2(void *p, void *q) { return issue5603exp; } +long long issue5603foo3(void *p, void *q, void *r) { return issue5603exp; } +long long issue5603foo4(void *p, void *q, void *r, void *s) { return issue5603exp; } + +// issue 5740 + +int test5740a(void), test5740b(void); + +// issue 5986 +static void output5986() +{ + int current_row = 0, row_count = 0; + double sum_squares = 0; + double d; + do { + if (current_row == 10) { + current_row = 0; + } + ++row_count; + } + while (current_row++ != 1); + d = sqrt(sum_squares / row_count); + printf("sqrt is: %g\n", d); +} + +// issue 6128 +// Test handling of #defined names in clang. +// NOTE: Must use hex, or else a shortcut for decimals +// in cgo avoids trying to pass this to clang. +#define X 0x1 + +// issue 6472 +typedef struct +{ + struct + { + int x; + } y[16]; +} z; + +// issue 6612 +// Test new scheme for deciding whether C.name is an expression, type, constant. +// Clang silences some warnings when the name is a #defined macro, so test those too +// (even though we now use errors exclusively, not warnings). + +void myfunc(void) {} +int myvar = 5; +const char *mytext = "abcdef"; +typedef int mytype; +enum { + myenum = 1234, +}; + +#define myfunc_def myfunc +#define myvar_def myvar +#define mytext_def mytext +#define mytype_def mytype +#define myenum_def myenum +#define myint_def 12345 +#define myfloat_def 1.5 +#define mystring_def "hello" + +// issue 6907 +char* Issue6907CopyString(_GoString_ s) { + size_t n; + const char *p; + char *r; + + n = _GoStringLen(s); + p = _GoStringPtr(s); + r = malloc(n + 1); + memmove(r, p, n); + r[n] = '\0'; + return r; +} + +// issue 7560 +typedef struct { + char x; + long y; +} __attribute__((__packed__)) misaligned; + +int +offset7560(void) +{ + return (uintptr_t)&((misaligned*)0)->y; +} + +// issue 7786 +// No runtime test, just make sure that typedef and struct/union/class are interchangeable at compile time. + +struct test7786; +typedef struct test7786 typedef_test7786; +void f7786(struct test7786 *ctx) {} +void g7786(typedef_test7786 *ctx) {} + +typedef struct body7786 typedef_body7786; +struct body7786 { int x; }; +void b7786(struct body7786 *ctx) {} +void c7786(typedef_body7786 *ctx) {} + +typedef union union7786 typedef_union7786; +void u7786(union union7786 *ctx) {} +void v7786(typedef_union7786 *ctx) {} + +// issue 8092 +// Test that linker defined symbols (e.g., text, data) don't +// conflict with C symbols. +char text[] = "text"; +char data[] = "data"; +char *ctext(void) { return text; } +char *cdata(void) { return data; } + +// issue 8428 +// Cgo inconsistently translated zero size arrays. + +struct issue8428one { + char b; + char rest[]; +}; + +struct issue8428two { + void *p; + char b; + char rest[0]; + char pad; +}; + +struct issue8428three { + char w[1][2][3][0]; + char x[2][3][0][1]; + char y[3][0][1][2]; + char z[0][1][2][3]; +}; + +// issue 8331 part 1 - part 2 in testx.go +// A typedef of an unnamed struct is the same struct when +// #include'd twice. No runtime test; just make sure it compiles. +#include "issue8331.h" + +// issue 8368 and 8441 +// Recursive struct definitions didn't work. +// No runtime test; just make sure it compiles. +typedef struct one one; +typedef struct two two; +struct one { + two *x; +}; +struct two { + one *x; +}; + +// issue 8811 + +extern int issue8811Initialized; +extern void issue8811Init(); + +void issue8811Execute() { + if(!issue8811Initialized) + issue8811Init(); +} + +// issue 8945 + +typedef void (*PFunc8945)(); +PFunc8945 func8945; + +// issue 9557 + +struct issue9557_t { + int a; +} test9557bar = { 42 }; +struct issue9557_t *issue9557foo = &test9557bar; + +// issue 10303 +// Pointers passed to C were not marked as escaping (bug in cgo). + +typedef int *intptr; + +void setintstar(int *x) { + *x = 1; +} + +void setintptr(intptr x) { + *x = 1; +} + +void setvoidptr(void *x) { + *(int*)x = 1; +} + +typedef struct Struct Struct; +struct Struct { + int *P; +}; + +void setstruct(Struct s) { + *s.P = 1; +} + +// issue 11925 +// Structs with zero-length trailing fields are now padded by the Go compiler. + +struct a11925 { + int i; + char a[0]; + char b[0]; +}; + +struct b11925 { + int i; + char a[0]; + char b[]; +}; + +// issue 12030 +void issue12030conv(char *buf, double x) { + sprintf(buf, "d=%g", x); +} + +// issue 14838 + +int check_cbytes(char *b, size_t l) { + int i; + for (i = 0; i < l; i++) { + if (b[i] != i) { + return 0; + } + } + return 1; +} + +// issue 17065 +// Test that C symbols larger than a page play nicely with the race detector. +int ii[65537]; + +// issue 17537 +// The void* cast introduced by cgo to avoid problems +// with const/volatile qualifiers breaks C preprocessor macros that +// emulate functions. + +typedef struct { + int i; +} S17537; + +int I17537(S17537 *p); + +#define I17537(p) ((p)->i) + +// Calling this function used to fail without the cast. +const int F17537(const char **p) { + return **p; +} + +// issue 17723 +// API compatibility checks + +typedef char *cstring_pointer; +static void cstring_pointer_fun(cstring_pointer dummy) { } +const char *api_hello = "hello!"; + +// Calling this function used to trigger an error from the C compiler +// (issue 18298). +void F18298(const void *const *p) { +} + +// Test that conversions between typedefs work as they used to. +typedef const void *T18298_1; +struct S18298 { int i; }; +typedef const struct S18298 *T18298_2; +void G18298(T18298_1 t) { +} + +// issue 18126 +// cgo check of void function returning errno. +void Issue18126C(void **p) {} + +// issue 18720 + +#define HELLO "hello" +#define WORLD "world" +#define HELLO_WORLD HELLO "\000" WORLD + +struct foo { char c; }; +#define SIZE_OF(x) sizeof(x) +#define SIZE_OF_FOO SIZE_OF(struct foo) +#define VAR1 VAR +#define VAR var +int var = 5; + +#define ADDR &var + +#define CALL fn() +int fn(void) { + return ++var; +} + +// issue 20129 + +int issue20129 = 0; +typedef void issue20129Void; +issue20129Void issue20129Foo() { + issue20129 = 1; +} +typedef issue20129Void issue20129Void2; +issue20129Void2 issue20129Bar() { + issue20129 = 2; +} + +// issue 20369 +#define XUINT64_MAX 18446744073709551615ULL + +// issue 21668 +// Fail to guess the kind of the constant "x". +// No runtime test; just make sure it compiles. +const int x21668 = 42; + +// issue 21708 +#define CAST_TO_INT64 (int64_t)(-1) + +// issue 21809 +// Compile C `typedef` to go type aliases. + +typedef long MySigned_t; +// tests alias-to-alias +typedef MySigned_t MySigned2_t; +long takes_long(long x) { return x * x; } +MySigned_t takes_typedef(MySigned_t x) { return x * x; } + +// issue 22906 + +// It's going to be hard to include a whole real JVM to test this. +// So we'll simulate a really easy JVM using just the parts we need. +// This is the relevant part of jni.h. + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +typedef jobject jweak; + +// Note: jvalue is already a non-pointer type due to it being a C union. + +// issue 22958 + +typedef struct { + unsigned long long f8 : 8; + unsigned long long f16 : 16; + unsigned long long f24 : 24; + unsigned long long f32 : 32; + unsigned long long f40 : 40; + unsigned long long f48 : 48; + unsigned long long f56 : 56; + unsigned long long f64 : 64; +} issue22958Type; + +// issue 23356 +int a(void) { return 5; }; +int r(void) { return 3; }; + +// issue 23720 +typedef int *issue23720A; +typedef const int *issue23720B; +void issue23720F(issue23720B a) {} + +// issue 24206 +#if defined(__linux__) && defined(__x86_64__) +#include <sys/mman.h> +// Returns string with null byte at the last valid address +char* dangerousString1() { + int pageSize = 4096; + char *data = mmap(0, 2 * pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0); + mprotect(data + pageSize,pageSize,PROT_NONE); + int start = pageSize - 123 - 1; // last 123 bytes of first page + 1 null byte + int i = start; + for (; i < pageSize; i++) { + data[i] = 'x'; + } + data[pageSize -1 ] = 0; + return data+start; +} + +char* dangerousString2() { + int pageSize = 4096; + char *data = mmap(0, 3 * pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0); + mprotect(data + 2 * pageSize,pageSize,PROT_NONE); + int start = pageSize - 123 - 1; // last 123 bytes of first page + 1 null byte + int i = start; + for (; i < 2 * pageSize; i++) { + data[i] = 'x'; + } + data[2*pageSize -1 ] = 0; + return data+start; +} +#else +char *dangerousString1() { return NULL; } +char *dangerousString2() { return NULL; } +#endif + +// issue 26066 +const unsigned long long int issue26066 = (const unsigned long long) -1; + +// issue 26517 +// Introduce two pointer types which are distinct, but have the same +// base type. Make sure that both of those pointer types get resolved +// correctly. Before the fix for 26517 if one of these pointer types +// was resolved before the other one was processed, the second one +// would never be resolved. +// Before this issue was fixed this test failed on Windows, +// where va_list expands to a named char* type. +typedef va_list TypeOne; +typedef char *TypeTwo; + +// issue 28540 + +static void twoargs1(void *p, int n) {} +static void *twoargs2() { return 0; } +static int twoargs3(void * p) { return 0; } + +// issue 28545 +// Failed to add type conversion for negative constant. + +static void issue28545F(char **p, int n, complex double a) {} + +// issue 28772 part 1 - part 2 in testx.go +// Failed to add type conversion for Go constant set to C constant. +// No runtime test; just make sure it compiles. + +#define issue28772Constant 1 + +// issue 28896 +// cgo was incorrectly adding padding after a packed struct. +typedef struct { + void *f1; + uint32_t f2; +} __attribute__((__packed__)) innerPacked; + +typedef struct { + innerPacked g1; + uint64_t g2; +} outerPacked; + +typedef struct { + void *f1; + uint32_t f2; +} innerUnpacked; + +typedef struct { + innerUnpacked g1; + uint64_t g2; +} outerUnpacked; + +size_t offset(int x) { + switch (x) { + case 0: + return offsetof(innerPacked, f2); + case 1: + return offsetof(outerPacked, g2); + case 2: + return offsetof(innerUnpacked, f2); + case 3: + return offsetof(outerUnpacked, g2); + default: + abort(); + } +} + +// issue 29748 + +typedef struct { char **p; } S29748; +static int f29748(S29748 *p) { return 0; } + +// issue 29781 +// Error with newline inserted into constant expression. +// Compilation test only, nothing to run. + +static void issue29781F(char **p, int n) {} +#define ISSUE29781C 0 + +// issue 31093 +static uint16_t issue31093F(uint16_t v) { return v; } + +// issue 32579 +typedef struct S32579 { unsigned char data[1]; } S32579; + +// issue 37033, cgo.Handle +extern void GoFunc37033(uintptr_t handle); +void cFunc37033(uintptr_t handle) { GoFunc37033(handle); } + +// issue 38649 +// Test that #define'd type aliases work. +#define netbsd_gid unsigned int + +// issue 40494 +// Inconsistent handling of tagged enum and union types. +enum Enum40494 { X_40494 }; +union Union40494 { int x; }; +void issue40494(enum Enum40494 e, union Union40494* up) {} + +// Issue 45451, bad handling of go:notinheap types. +typedef struct issue45451Undefined issue45451; + +// Issue 49633, example of cgo.Handle with void*. +extern void GoFunc49633(void*); +void cfunc49633(void *context) { GoFunc49633(context); } + +*/ +import "C" + +import ( + "context" + "fmt" + "math" + "math/rand" + "os" + "os/signal" + "reflect" + "runtime" + "runtime/cgo" + "sync" + "syscall" + "testing" + "time" + "unsafe" +) + +// alignment + +func testAlign(t *testing.T) { + var evt C.SDL_KeyboardEvent + C.makeEvent(&evt) + if C.same(&evt, evt.typ, evt.which, evt.state, evt.keysym.scancode, evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) == 0 { + t.Error("*** bad alignment") + C.cTest(&evt) + t.Errorf("Go: %#x %#x %#x %#x %#x %#x %#x\n", + evt.typ, evt.which, evt.state, evt.keysym.scancode, + evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) + t.Error(evt) + } +} + +// api + +const greeting = "hello, world" + +type testPair struct { + Name string + Got, Want interface{} +} + +var testPairs = []testPair{ + {"GoString", C.GoString(C.greeting), greeting}, + {"GoStringN", C.GoStringN(C.greeting, 5), greeting[:5]}, + {"GoBytes", C.GoBytes(unsafe.Pointer(C.greeting), 5), []byte(greeting[:5])}, +} + +func testHelpers(t *testing.T) { + for _, pair := range testPairs { + if !reflect.DeepEqual(pair.Got, pair.Want) { + t.Errorf("%s: got %#v, want %#v", pair.Name, pair.Got, pair.Want) + } + } +} + +// basic test cases + +const EINVAL = C.EINVAL /* test #define */ + +var KILO = C.KILO + +func uuidgen() { + var uuid C.cgo_uuid_t + C.uuid_generate(&uuid[0]) +} + +func Strtol(s string, base int) (int, error) { + p := C.CString(s) + n, err := C.strtol(p, nil, C.int(base)) + C.free(unsafe.Pointer(p)) + return int(n), err +} + +func Atol(s string) int { + p := C.CString(s) + n := C.atol(p) + C.free(unsafe.Pointer(p)) + return int(n) +} + +func testConst(t *testing.T) { + C.myConstFunc(nil, 0, nil) +} + +func testEnum(t *testing.T) { + if C.Enum1 != 1 || C.Enum2 != 2 { + t.Error("bad enum", C.Enum1, C.Enum2) + } +} + +func testNamedEnum(t *testing.T) { + e := new(C.enum_E) + + *e = C.Enum1 + if *e != 1 { + t.Error("bad enum", C.Enum1) + } + + *e = C.Enum2 + if *e != 2 { + t.Error("bad enum", C.Enum2) + } +} + +func testCastToEnum(t *testing.T) { + e := C.enum_E(C.Enum1) + if e != 1 { + t.Error("bad enum", C.Enum1) + } + + e = C.enum_E(C.Enum2) + if e != 2 { + t.Error("bad enum", C.Enum2) + } +} + +func testAtol(t *testing.T) { + l := Atol("123") + if l != 123 { + t.Error("Atol 123: ", l) + } +} + +func testErrno(t *testing.T) { + p := C.CString("no-such-file") + m := C.CString("r") + f, err := C.fopen(p, m) + C.free(unsafe.Pointer(p)) + C.free(unsafe.Pointer(m)) + if err == nil { + C.fclose(f) + t.Fatalf("C.fopen: should fail") + } + if err != syscall.ENOENT { + t.Fatalf("C.fopen: unexpected error: %v", err) + } +} + +func testMultipleAssign(t *testing.T) { + p := C.CString("234") + n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10) + if runtime.GOOS == "openbsd" { + // Bug in OpenBSD strtol(3) - base > 36 succeeds. + if (n != 0 && n != 239089) || m != 234 { + t.Fatal("Strtol x2: ", n, m) + } + } else if n != 0 || m != 234 { + t.Fatal("Strtol x2: ", n, m) + } + C.free(unsafe.Pointer(p)) +} + +var ( + cuint = (C.uint)(0) + culong C.ulong + cchar C.char +) + +type Context struct { + ctx *C.struct_ibv_context +} + +func benchCgoCall(b *testing.B) { + b.Run("add-int", func(b *testing.B) { + const x = C.int(2) + const y = C.int(3) + + for i := 0; i < b.N; i++ { + C.add(x, y) + } + }) + + b.Run("one-pointer", func(b *testing.B) { + var a0 C.VkDeviceCreateInfo + for i := 0; i < b.N; i++ { + C.handleComplexPointer(&a0) + } + }) + b.Run("eight-pointers", func(b *testing.B) { + var a0, a1, a2, a3, a4, a5, a6, a7 C.VkDeviceCreateInfo + for i := 0; i < b.N; i++ { + C.handleComplexPointer8(&a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7) + } + }) + b.Run("eight-pointers-nil", func(b *testing.B) { + var a0, a1, a2, a3, a4, a5, a6, a7 *C.VkDeviceCreateInfo + for i := 0; i < b.N; i++ { + C.handleComplexPointer8(a0, a1, a2, a3, a4, a5, a6, a7) + } + }) + b.Run("eight-pointers-array", func(b *testing.B) { + var a [8]C.VkDeviceCreateInfo + for i := 0; i < b.N; i++ { + C.handleComplexPointer8(&a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7]) + } + }) + b.Run("eight-pointers-slice", func(b *testing.B) { + a := make([]C.VkDeviceCreateInfo, 8) + for i := 0; i < b.N; i++ { + C.handleComplexPointer8(&a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7]) + } + }) +} + +// Benchmark measuring overhead from Go to C and back to Go (via a callback) +func benchCallback(b *testing.B) { + var x = false + for i := 0; i < b.N; i++ { + nestedCall(func() { x = true }) + } + if !x { + b.Fatal("nestedCall was not invoked") + } +} + +var sinkString string + +func benchGoString(b *testing.B) { + for i := 0; i < b.N; i++ { + sinkString = C.GoString(C.cstr) + } + const want = "abcefghijklmnopqrstuvwxyzABCEFGHIJKLMNOPQRSTUVWXYZ1234567890" + if sinkString != want { + b.Fatalf("%q != %q", sinkString, want) + } +} + +// Static (build-time) test that syntax traversal visits all operands of s[i:j:k]. +func sliceOperands(array [2000]int) { + _ = array[C.KILO:C.KILO:C.KILO] // no type error +} + +// set in cgo_thread_lock.go init +var testThreadLockFunc = func(*testing.T) {} + +// complex alignment + +func TestComplexAlign(t *testing.T) { + if C.cplxAlign.x != 3.14 { + t.Errorf("got %v, expected 3.14", C.cplxAlign.x) + } + if C.cplxAlign.y != 2.17 { + t.Errorf("got %v, expected 2.17", C.cplxAlign.y) + } +} + +// constants and pointer checking + +func testCheckConst(t *testing.T) { + // The test is that this compiles successfully. + p := C.malloc(C.size_t(unsafe.Sizeof(C.int(0)))) + defer C.free(p) + C.CheckConstFunc(&C.CheckConstStruct{(*C.int)(p)}, C.CheckConstVal) +} + +// duplicate symbol + +func duplicateSymbols() { + fmt.Printf("%v %v %v\n", C.base_symbol, C.alias_one, C.alias_two) +} + +// environment + +// This is really an os package test but here for convenience. +func testSetEnv(t *testing.T) { + if runtime.GOOS == "windows" { + // Go uses SetEnvironmentVariable on windows. However, + // C runtime takes a *copy* at process startup of the + // OS environment, and stores it in environ/envp. + // It is this copy that getenv/putenv manipulate. + t.Logf("skipping test") + return + } + const key = "CGO_OS_TEST_KEY" + const val = "CGO_OS_TEST_VALUE" + os.Setenv(key, val) + keyc := C.CString(key) + defer C.free(unsafe.Pointer(keyc)) + v := C.getenv(keyc) + if uintptr(unsafe.Pointer(v)) == 0 { + t.Fatal("getenv returned NULL") + } + vs := C.GoString(v) + if vs != val { + t.Fatalf("getenv() = %q; want %q", vs, val) + } +} + +// function pointer variables + +func callBridge(f C.intFunc) int { + return int(C.bridge_int_func(f)) +} + +func callCBridge(f C.intFunc) C.int { + return C.bridge_int_func(f) +} + +func testFpVar(t *testing.T) { + const expected = 42 + f := C.intFunc(C.fortytwo) + res1 := C.bridge_int_func(f) + if r1 := int(res1); r1 != expected { + t.Errorf("got %d, want %d", r1, expected) + } + res2 := callCBridge(f) + if r2 := int(res2); r2 != expected { + t.Errorf("got %d, want %d", r2, expected) + } + r3 := callBridge(f) + if r3 != expected { + t.Errorf("got %d, want %d", r3, expected) + } +} + +// issue 1222 +type AsyncEvent struct { + event C.struct_ibv_async_event +} + +// issue 1635 + +func test1635(t *testing.T) { + C.scatter() + if v := C.hola; v != 0 { + t.Fatalf("C.hola is %d, should be 0", v) + } + if v := C.testHola(); v != 0 { + t.Fatalf("C.testHola() is %d, should be 0", v) + } +} + +// issue 2470 + +func testUnsignedInt(t *testing.T) { + a := (int64)(C.UINT32VAL) + b := (int64)(0xc008427b) + if a != b { + t.Errorf("Incorrect unsigned int - got %x, want %x", a, b) + } +} + +// issue 3250 + +func test3250(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("not applicable on windows") + } + + t.Skip("skipped, see golang.org/issue/5885") + var ( + thres = 1 + sig = syscall_dot_SIGCHLD + ) + type result struct { + n int + sig os.Signal + } + var ( + sigCh = make(chan os.Signal, 10) + waitStart = make(chan struct{}) + waitDone = make(chan result) + ) + + signal.Notify(sigCh, sig) + + go func() { + n := 0 + alarm := time.After(time.Second * 3) + for { + select { + case <-waitStart: + waitStart = nil + case v := <-sigCh: + n++ + if v != sig || n > thres { + waitDone <- result{n, v} + return + } + case <-alarm: + waitDone <- result{n, sig} + return + } + } + }() + + waitStart <- struct{}{} + C.testSendSIG() + r := <-waitDone + if r.sig != sig { + t.Fatalf("received signal %v, but want %v", r.sig, sig) + } + t.Logf("got %d signals\n", r.n) + if r.n <= thres { + t.Fatalf("expected more than %d", thres) + } +} + +// issue 3261 + +func testLibgcc(t *testing.T) { + var table = []struct { + in, out C.int + }{ + {0, 0}, + {1, 1}, + {-42, 42}, + {1000300, 1000300}, + {1 - 1<<31, 1<<31 - 1}, + } + for _, v := range table { + if o := C.vabs(v.in); o != v.out { + t.Fatalf("abs(%d) got %d, should be %d", v.in, o, v.out) + return + } + } +} + +// issue 3729 + +func test3729(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping on windows") + } + + _, e := C.g() + if e != syscall.E2BIG { + t.Errorf("got %q, expect %q", e, syscall.E2BIG) + } + _, e = C.g2(C.EINVAL, C._expA, C._expB, C._expC, C._expD) + if e != syscall.EINVAL { + t.Errorf("got %q, expect %q", e, syscall.EINVAL) + } +} + +// issue 3945 + +func testPrintf(t *testing.T) { + C.say() +} + +// issue 4054 + +var issue4054a = []int{C.A, C.B, C.C, C.D, C.E, C.F, C.G, C.H, C.I, C.J} + +// issue 4339 + +func test4339(t *testing.T) { + C.handle4339(&C.exported4339) +} + +// issue 4417 + +func testBoolAlign(t *testing.T) { + b := C.c_bool(true, true, 10, true, false) + if b != 10 { + t.Fatalf("found %d expected 10\n", b) + } + b = C.c_bool(true, true, 5, true, true) + if b != 5 { + t.Fatalf("found %d expected 5\n", b) + } + b = C.c_bool(true, true, 3, true, false) + if b != 3 { + t.Fatalf("found %d expected 3\n", b) + } + b = C.c_bool(false, false, 1, true, false) + if b != 1 { + t.Fatalf("found %d expected 1\n", b) + } + b = C.c_bool(false, true, 200, true, false) + if b != 200 { + t.Fatalf("found %d expected 200\n", b) + } +} + +// issue 4857 + +func test4857() { + _ = C.issue4857() +} + +// issue 5224 + +func testCflags(t *testing.T) { + is_windows := C.is_windows == 1 + if is_windows != (runtime.GOOS == "windows") { + t.Errorf("is_windows: %v, runtime.GOOS: %s", is_windows, runtime.GOOS) + } + if C.common != 123 { + t.Errorf("common: %v (expected 123)", C.common) + } +} + +// issue 5227 + +func test5227(t *testing.T) { + C.init() +} + +func selectfont() C.Fontinfo { + return C.SansTypeface +} + +// issue 5242 + +func test5242(t *testing.T) { + if got := C.issue5242(C.foo{}, C.bar{}); got != 5242 { + t.Errorf("got %v", got) + } +} + +func test5603(t *testing.T) { + var x [5]int64 + exp := int64(C.issue5603exp) + x[0] = int64(C.issue5603foo0()) + x[1] = int64(C.issue5603foo1(nil)) + x[2] = int64(C.issue5603foo2(nil, nil)) + x[3] = int64(C.issue5603foo3(nil, nil, nil)) + x[4] = int64(C.issue5603foo4(nil, nil, nil, nil)) + for i, v := range x { + if v != exp { + t.Errorf("issue5603foo%d() returns %v, expected %v", i, v, exp) + } + } +} + +// issue 5337 + +func test5337(t *testing.T) { + C.test5337() +} + +// issue 5740 + +func test5740(t *testing.T) { + if v := C.test5740a() + C.test5740b(); v != 5 { + t.Errorf("expected 5, got %v", v) + } +} + +// issue 5986 + +func test5986(t *testing.T) { + C.output5986() +} + +// issue 6128 + +func test6128() { + // nothing to run, just make sure this compiles. + _ = C.X +} + +// issue 6390 + +func test6390(t *testing.T) { + p1 := C.malloc(1024) + if p1 == nil { + t.Fatalf("C.malloc(1024) returned nil") + } + p2 := C.malloc(0) + if p2 == nil { + t.Fatalf("C.malloc(0) returned nil") + } + C.free(p1) + C.free(p2) +} + +func test6472() { + // nothing to run, just make sure this compiles + s := new(C.z) + println(s.y[0].x) +} + +// issue 6506 + +func test6506() { + // nothing to run, just make sure this compiles + var x C.size_t + + C.calloc(x, x) + C.malloc(x) + C.realloc(nil, x) + C.memcpy(nil, nil, x) + C.memcmp(nil, nil, x) + C.memmove(nil, nil, x) + C.strncpy(nil, nil, x) + C.strncmp(nil, nil, x) + C.strncat(nil, nil, x) + x = C.strxfrm(nil, nil, x) + C.memchr(nil, 0, x) + x = C.strcspn(nil, nil) + x = C.strspn(nil, nil) + C.memset(nil, 0, x) + x = C.strlen(nil) + _ = x +} + +// issue 6612 + +func testNaming(t *testing.T) { + C.myfunc() + C.myfunc_def() + if v := C.myvar; v != 5 { + t.Errorf("C.myvar = %d, want 5", v) + } + if v := C.myvar_def; v != 5 { + t.Errorf("C.myvar_def = %d, want 5", v) + } + if s := C.GoString(C.mytext); s != "abcdef" { + t.Errorf("C.mytext = %q, want %q", s, "abcdef") + } + if s := C.GoString(C.mytext_def); s != "abcdef" { + t.Errorf("C.mytext_def = %q, want %q", s, "abcdef") + } + if c := C.myenum; c != 1234 { + t.Errorf("C.myenum = %v, want 1234", c) + } + if c := C.myenum_def; c != 1234 { + t.Errorf("C.myenum_def = %v, want 1234", c) + } + { + const c = C.myenum + if c != 1234 { + t.Errorf("C.myenum as const = %v, want 1234", c) + } + } + { + const c = C.myenum_def + if c != 1234 { + t.Errorf("C.myenum as const = %v, want 1234", c) + } + } + if c := C.myint_def; c != 12345 { + t.Errorf("C.myint_def = %v, want 12345", c) + } + { + const c = C.myint_def + if c != 12345 { + t.Errorf("C.myint as const = %v, want 12345", c) + } + } + + if c := C.myfloat_def; c != 1.5 { + t.Errorf("C.myint_def = %v, want 1.5", c) + } + { + const c = C.myfloat_def + if c != 1.5 { + t.Errorf("C.myint as const = %v, want 1.5", c) + } + } + + if s := C.mystring_def; s != "hello" { + t.Errorf("C.mystring_def = %q, want %q", s, "hello") + } +} + +// issue 6907 + +func test6907(t *testing.T) { + want := "yarn" + if got := C.GoString(C.Issue6907CopyString(want)); got != want { + t.Errorf("C.GoString(C.Issue6907CopyString(%q)) == %q, want %q", want, got, want) + } +} + +// issue 7560 + +func test7560(t *testing.T) { + // some mingw don't implement __packed__ correctly. + if C.offset7560() != 1 { + t.Skip("C compiler did not pack struct") + } + + // C.misaligned should have x but then a padding field to get to the end of the struct. + // There should not be a field named 'y'. + var v C.misaligned + rt := reflect.TypeOf(&v).Elem() + if rt.NumField() != 2 || rt.Field(0).Name != "x" || rt.Field(1).Name != "_" { + t.Errorf("unexpected fields in C.misaligned:\n") + for i := 0; i < rt.NumField(); i++ { + t.Logf("%+v\n", rt.Field(i)) + } + } +} + +// issue 7786 + +func f() { + var x1 *C.typedef_test7786 + var x2 *C.struct_test7786 + x1 = x2 + x2 = x1 + C.f7786(x1) + C.f7786(x2) + C.g7786(x1) + C.g7786(x2) + + var b1 *C.typedef_body7786 + var b2 *C.struct_body7786 + b1 = b2 + b2 = b1 + C.b7786(b1) + C.b7786(b2) + C.c7786(b1) + C.c7786(b2) + + var u1 *C.typedef_union7786 + var u2 *C.union_union7786 + u1 = u2 + u2 = u1 + C.u7786(u1) + C.u7786(u2) + C.v7786(u1) + C.v7786(u2) +} + +// issue 8092 + +func test8092(t *testing.T) { + tests := []struct { + s string + a, b *C.char + }{ + {"text", &C.text[0], C.ctext()}, + {"data", &C.data[0], C.cdata()}, + } + for _, test := range tests { + if test.a != test.b { + t.Errorf("%s: pointer mismatch: %v != %v", test.s, test.a, test.b) + } + if got := C.GoString(test.a); got != test.s { + t.Errorf("%s: points at %#v, want %#v", test.s, got, test.s) + } + } +} + +// issues 8368 and 8441 + +func issue8368(one *C.struct_one, two *C.struct_two) { +} + +func issue8441(one *C.one, two *C.two) { + issue8441(two.x, one.x) +} + +// issue 8428 + +var _ = C.struct_issue8428one{ + b: C.char(0), + // The trailing rest field is not available in cgo. + // See issue 11925. + // rest: [0]C.char{}, +} + +var _ = C.struct_issue8428two{ + p: unsafe.Pointer(nil), + b: C.char(0), + rest: [0]C.char{}, +} + +var _ = C.struct_issue8428three{ + w: [1][2][3][0]C.char{}, + x: [2][3][0][1]C.char{}, + y: [3][0][1][2]C.char{}, + z: [0][1][2][3]C.char{}, +} + +// issue 8811 + +func test8811(t *testing.T) { + C.issue8811Execute() +} + +// issue 9557 + +func test9557(t *testing.T) { + // implicitly dereference a Go variable + foo := C.issue9557foo + if v := foo.a; v != 42 { + t.Fatalf("foo.a expected 42, but got %d", v) + } + + // explicitly dereference a C variable + if v := (*C.issue9557foo).a; v != 42 { + t.Fatalf("(*C.issue9557foo).a expected 42, but is %d", v) + } + + // implicitly dereference a C variable + if v := C.issue9557foo.a; v != 42 { + t.Fatalf("C.issue9557foo.a expected 42, but is %d", v) + } +} + +// issue 8331 part 1 + +func issue8331a() C.issue8331 { + return issue8331Var +} + +// issue 10303 + +func test10303(t *testing.T, n int) { + if runtime.Compiler == "gccgo" { + t.Skip("gccgo permits C pointers on the stack") + } + + // Run at a few different stack depths just to avoid an unlucky pass + // due to variables ending up on different pages. + if n > 0 { + test10303(t, n-1) + } + if t.Failed() { + return + } + var x, y, z, v, si C.int + var s C.Struct + C.setintstar(&x) + C.setintptr(&y) + C.setvoidptr(unsafe.Pointer(&v)) + s.P = &si + C.setstruct(s) + + if uintptr(unsafe.Pointer(&x))&^0xfff == uintptr(unsafe.Pointer(&z))&^0xfff { + t.Error("C int* argument on stack") + } + if uintptr(unsafe.Pointer(&y))&^0xfff == uintptr(unsafe.Pointer(&z))&^0xfff { + t.Error("C intptr argument on stack") + } + if uintptr(unsafe.Pointer(&v))&^0xfff == uintptr(unsafe.Pointer(&z))&^0xfff { + t.Error("C void* argument on stack") + } + if uintptr(unsafe.Pointer(&si))&^0xfff == uintptr(unsafe.Pointer(&z))&^0xfff { + t.Error("C struct field pointer on stack") + } +} + +// issue 11925 + +func test11925(t *testing.T) { + if C.sizeof_struct_a11925 != unsafe.Sizeof(C.struct_a11925{}) { + t.Errorf("size of a changed: C %d, Go %d", C.sizeof_struct_a11925, unsafe.Sizeof(C.struct_a11925{})) + } + if C.sizeof_struct_b11925 != unsafe.Sizeof(C.struct_b11925{}) { + t.Errorf("size of b changed: C %d, Go %d", C.sizeof_struct_b11925, unsafe.Sizeof(C.struct_b11925{})) + } +} + +// issue 12030 + +func test12030(t *testing.T) { + buf := (*C.char)(C.malloc(256)) + defer C.free(unsafe.Pointer(buf)) + for _, f := range []float64{1.0, 2.0, 3.14} { + C.issue12030conv(buf, C.double(f)) + got := C.GoString(buf) + if want := fmt.Sprintf("d=%g", f); got != want { + t.Fatalf("C.sprintf failed for %g: %q != %q", f, got, want) + } + } +} + +// issue 13402 + +var _ C.complexfloat +var _ C.complexdouble + +// issue 13930 +// Test that cgo's multiple-value special form for +// C function calls works in variable declaration statements. + +var _, _ = C.abs(0) + +// issue 14838 + +func test14838(t *testing.T) { + data := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} + cData := C.CBytes(data) + defer C.free(cData) + + if C.check_cbytes((*C.char)(cData), C.size_t(len(data))) == 0 { + t.Fatalf("mismatched data: expected %v, got %v", data, (*(*[10]byte)(unsafe.Pointer(cData)))[:]) + } +} + +// issue 17065 + +var sink C.int + +func test17065(t *testing.T) { + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + t.Skip("broken on darwin; issue 17065") + } + for i := range C.ii { + sink = C.ii[i] + } +} + +// issue 17537 + +func test17537(t *testing.T) { + v := C.S17537{i: 17537} + if got, want := C.I17537(&v), C.int(17537); got != want { + t.Errorf("got %d, want %d", got, want) + } + + p := (*C.char)(C.malloc(1)) + *p = 17 + if got, want := C.F17537(&p), C.int(17); got != want { + t.Errorf("got %d, want %d", got, want) + } + + C.F18298(nil) + var v18298 C.T18298_2 + C.G18298(C.T18298_1(v18298)) +} + +// issue 17723 + +func testAPI() { + var cs *C.char + cs = C.CString("hello") + defer C.free(unsafe.Pointer(cs)) + var s string + s = C.GoString((*C.char)(C.api_hello)) + s = C.GoStringN((*C.char)(C.api_hello), C.int(6)) + var b []byte + b = C.GoBytes(unsafe.Pointer(C.api_hello), C.int(6)) + _, _ = s, b + C.cstring_pointer_fun(nil) +} + +// issue 18126 + +func test18126(t *testing.T) { + p := C.malloc(1) + _, err := C.Issue18126C(&p) + C.free(p) + _ = err +} + +// issue 18720 + +func test18720(t *testing.T) { + if got, want := C.HELLO_WORLD, "hello\000world"; got != want { + t.Errorf("C.HELLO_WORLD == %q, expected %q", got, want) + } + + if got, want := C.VAR1, C.int(5); got != want { + t.Errorf("C.VAR1 == %v, expected %v", got, want) + } + + if got, want := *C.ADDR, C.int(5); got != want { + t.Errorf("*C.ADDR == %v, expected %v", got, want) + } + + if got, want := C.CALL, C.int(6); got != want { + t.Errorf("C.CALL == %v, expected %v", got, want) + } + + if got, want := C.CALL, C.int(7); got != want { + t.Errorf("C.CALL == %v, expected %v", got, want) + } + + // Issue 20125. + if got, want := C.SIZE_OF_FOO, 1; got != want { + t.Errorf("C.SIZE_OF_FOO == %v, expected %v", got, want) + } +} + +// issue 20129 + +func test20129(t *testing.T) { + if C.issue20129 != 0 { + t.Fatal("test is broken") + } + C.issue20129Foo() + if C.issue20129 != 1 { + t.Errorf("got %v but expected %v", C.issue20129, 1) + } + C.issue20129Bar() + if C.issue20129 != 2 { + t.Errorf("got %v but expected %v", C.issue20129, 2) + } +} + +// issue 20369 + +func test20369(t *testing.T) { + if C.XUINT64_MAX != math.MaxUint64 { + t.Fatalf("got %v, want %v", uint64(C.XUINT64_MAX), uint64(math.MaxUint64)) + } +} + +// issue 21668 + +var issue21668_X = C.x21668 + +// issue 21708 + +func test21708(t *testing.T) { + if got, want := C.CAST_TO_INT64, -1; got != want { + t.Errorf("C.CAST_TO_INT64 == %v, expected %v", got, want) + } +} + +// issue 21809 + +func test21809(t *testing.T) { + longVar := C.long(3) + typedefVar := C.MySigned_t(4) + typedefTypedefVar := C.MySigned2_t(5) + + // all three should be considered identical to `long` + if ret := C.takes_long(longVar); ret != 9 { + t.Errorf("got %v but expected %v", ret, 9) + } + if ret := C.takes_long(typedefVar); ret != 16 { + t.Errorf("got %v but expected %v", ret, 16) + } + if ret := C.takes_long(typedefTypedefVar); ret != 25 { + t.Errorf("got %v but expected %v", ret, 25) + } + + // They should also be identical to the typedef'd type + if ret := C.takes_typedef(longVar); ret != 9 { + t.Errorf("got %v but expected %v", ret, 9) + } + if ret := C.takes_typedef(typedefVar); ret != 16 { + t.Errorf("got %v but expected %v", ret, 16) + } + if ret := C.takes_typedef(typedefTypedefVar); ret != 25 { + t.Errorf("got %v but expected %v", ret, 25) + } +} + +// issue 22906 + +func test22906(t *testing.T) { + var x1 C.jobject = 0 // Note: 0, not nil. That makes sure we use uintptr for these types. + _ = x1 + var x2 C.jclass = 0 + _ = x2 + var x3 C.jthrowable = 0 + _ = x3 + var x4 C.jstring = 0 + _ = x4 + var x5 C.jarray = 0 + _ = x5 + var x6 C.jbooleanArray = 0 + _ = x6 + var x7 C.jbyteArray = 0 + _ = x7 + var x8 C.jcharArray = 0 + _ = x8 + var x9 C.jshortArray = 0 + _ = x9 + var x10 C.jintArray = 0 + _ = x10 + var x11 C.jlongArray = 0 + _ = x11 + var x12 C.jfloatArray = 0 + _ = x12 + var x13 C.jdoubleArray = 0 + _ = x13 + var x14 C.jobjectArray = 0 + _ = x14 + var x15 C.jweak = 0 + _ = x15 +} + +// issue 22958 +// Nothing to run, just make sure this compiles. +var Vissue22958 C.issue22958Type + +func test23356(t *testing.T) { + if got, want := C.a(), C.int(5); got != want { + t.Errorf("C.a() == %v, expected %v", got, want) + } + if got, want := C.r(), C.int(3); got != want { + t.Errorf("C.r() == %v, expected %v", got, want) + } +} + +// issue 23720 + +func Issue23720F() { + var x C.issue23720A + C.issue23720F(x) +} + +// issue 24206 + +func test24206(t *testing.T) { + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH) + } + + if l := len(C.GoString(C.dangerousString1())); l != 123 { + t.Errorf("Incorrect string length - got %d, want 123", l) + } + if l := len(C.GoString(C.dangerousString2())); l != 4096+123 { + t.Errorf("Incorrect string length - got %d, want %d", l, 4096+123) + } +} + +// issue 25143 + +func issue25143sum(ns ...C.int) C.int { + total := C.int(0) + for _, n := range ns { + total += n + } + return total +} + +func test25143(t *testing.T) { + if got, want := issue25143sum(1, 2, 3), C.int(6); got != want { + t.Errorf("issue25143sum(1, 2, 3) == %v, expected %v", got, want) + } +} + +// issue 26066 +// Wrong type of constant with GCC 8 and newer. + +func test26066(t *testing.T) { + var i = int64(C.issue26066) + if i != -1 { + t.Errorf("got %d, want -1", i) + } +} + +// issue 26517 +var a C.TypeOne +var b C.TypeTwo + +// issue 27660 +// Stress the interaction between the race detector and cgo in an +// attempt to reproduce the memory corruption described in #27660. +// The bug was very timing sensitive; at the time of writing this +// test would only trigger the bug about once out of every five runs. + +func test27660(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ints := make([]int, 100) + locks := make([]sync.Mutex, 100) + // Slowly create threads so that ThreadSanitizer is forced to + // frequently resize its SyncClocks. + for i := 0; i < 100; i++ { + go func() { + for ctx.Err() == nil { + // Sleep in C for long enough that it is likely that the runtime + // will retake this goroutine's currently wired P. + C.usleep(1000 /* 1ms */) + runtime.Gosched() // avoid starvation (see #28701) + } + }() + go func() { + // Trigger lots of synchronization and memory reads/writes to + // increase the likelihood that the race described in #27660 + // results in corruption of ThreadSanitizer's internal state + // and thus an assertion failure or segfault. + i := 0 + for ctx.Err() == nil { + j := rand.Intn(100) + locks[j].Lock() + ints[j]++ + locks[j].Unlock() + // needed for gccgo, to avoid creation of an + // unpreemptible "fast path" in this loop. Choice + // of (1<<24) is somewhat arbitrary. + if i%(1<<24) == 0 { + runtime.Gosched() + } + i++ + + } + }() + time.Sleep(time.Millisecond) + } +} + +// issue 28540 + +func twoargsF() { + v := []string{} + C.twoargs1(C.twoargs2(), C.twoargs3(unsafe.Pointer(&v))) +} + +// issue 28545 + +func issue28545G(p **C.char) { + C.issue28545F(p, -1, (0)) + C.issue28545F(p, 2+3, complex(1, 1)) + C.issue28545F(p, issue28772Constant, issue28772Constant2) +} + +// issue 28772 part 1 - part 2 in testx.go + +const issue28772Constant = C.issue28772Constant + +// issue 28896 + +func offset(i int) uintptr { + var pi C.innerPacked + var po C.outerPacked + var ui C.innerUnpacked + var uo C.outerUnpacked + switch i { + case 0: + return unsafe.Offsetof(pi.f2) + case 1: + return unsafe.Offsetof(po.g2) + case 2: + return unsafe.Offsetof(ui.f2) + case 3: + return unsafe.Offsetof(uo.g2) + default: + panic("can't happen") + } +} + +func test28896(t *testing.T) { + for i := 0; i < 4; i++ { + c := uintptr(C.offset(C.int(i))) + g := offset(i) + if c != g { + t.Errorf("%d: C: %d != Go %d", i, c, g) + } + } +} + +// issue 29383 +// cgo's /*line*/ comments failed when inserted after '/', +// because the result looked like a "//" comment. +// No runtime test; just make sure it compiles. + +func Issue29383(n, size uint) int { + if ^C.size_t(0)/C.size_t(n) < C.size_t(size) { + return 0 + } + return 0 +} + +// issue 29748 +// Error handling a struct initializer that requires pointer checking. +// Compilation test only, nothing to run. + +var Vissue29748 = C.f29748(&C.S29748{ + nil, +}) + +func Fissue299748() { + C.f29748(&C.S29748{ + nil, + }) +} + +// issue 29781 + +var issue29781X struct{ X int } + +func issue29781F(...int) int { return 0 } + +func issue29781G() { + var p *C.char + C.issue29781F(&p, C.ISSUE29781C+1) + C.issue29781F(nil, (C.int)( + 0)) + C.issue29781F(&p, (C.int)(0)) + C.issue29781F(&p, (C.int)( + 0)) + C.issue29781F(&p, (C.int)(issue29781X. + X)) +} + +// issue 30065 + +func test30065(t *testing.T) { + var a [256]byte + b := []byte("a") + C.memcpy(unsafe.Pointer(&a), unsafe.Pointer(&b[0]), 1) + if a[0] != 'a' { + t.Errorf("&a failed: got %c, want %c", a[0], 'a') + } + + b = []byte("b") + C.memcpy(unsafe.Pointer(&a[0]), unsafe.Pointer(&b[0]), 1) + if a[0] != 'b' { + t.Errorf("&a[0] failed: got %c, want %c", a[0], 'b') + } + + d := make([]byte, 256) + b = []byte("c") + C.memcpy(unsafe.Pointer(&d[0]), unsafe.Pointer(&b[0]), 1) + if d[0] != 'c' { + t.Errorf("&d[0] failed: got %c, want %c", d[0], 'c') + } +} + +// issue 31093 +// No runtime test; just make sure it compiles. + +func Issue31093() { + C.issue31093F(C.ushort(0)) +} + +// issue 32579 + +func test32579(t *testing.T) { + var s [1]C.struct_S32579 + C.memset(unsafe.Pointer(&s[0].data[0]), 1, 1) + if s[0].data[0] != 1 { + t.Errorf("&s[0].data[0] failed: got %d, want %d", s[0].data[0], 1) + } +} + +// issue 37033, check if cgo.Handle works properly + +func testHandle(t *testing.T) { + ch := make(chan int) + + for i := 0; i < 42; i++ { + h := cgo.NewHandle(ch) + go func() { + C.cFunc37033(C.uintptr_t(h)) + }() + if v := <-ch; issue37033 != v { + t.Fatalf("unexpected receiving value: got %d, want %d", v, issue37033) + } + h.Delete() + } +} + +// issue 38649 + +var issue38649 C.netbsd_gid = 42 + +// issue 39877 + +var issue39877 *C.void = nil + +// issue 40494 +// No runtime test; just make sure it compiles. + +func Issue40494() { + C.issue40494(C.enum_Enum40494(C.X_40494), (*C.union_Union40494)(nil)) +} + +// Issue 45451. +func test45451(t *testing.T) { + var u *C.issue45451 + typ := reflect.ValueOf(u).Type().Elem() + + // The type is undefined in C so allocating it should panic. + defer func() { + if r := recover(); r == nil { + t.Error("expected panic") + } + }() + + _ = reflect.New(typ) + t.Errorf("reflect.New(%v) should have panicked", typ) +} + +// issue 52542 + +func func52542[T ~[]C.int]() {} + +type type52542[T ~*C.float] struct{} diff --git a/misc/cgo/test/test26213.go b/misc/cgo/test/test26213.go new file mode 100644 index 0000000..f8149cd --- /dev/null +++ b/misc/cgo/test/test26213.go @@ -0,0 +1,15 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import ( + "testing" + + "misc/cgo/test/issue26213" +) + +func test26213(t *testing.T) { + issue26213.Test26213(t) +} diff --git a/misc/cgo/test/test_unix.go b/misc/cgo/test/test_unix.go new file mode 100644 index 0000000..831b9ca --- /dev/null +++ b/misc/cgo/test/test_unix.go @@ -0,0 +1,12 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows +// +build !windows + +package cgotest + +import "syscall" + +var syscall_dot_SIGCHLD = syscall.SIGCHLD diff --git a/misc/cgo/test/test_windows.go b/misc/cgo/test/test_windows.go new file mode 100644 index 0000000..7bfb33a --- /dev/null +++ b/misc/cgo/test/test_windows.go @@ -0,0 +1,9 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +import "syscall" + +var syscall_dot_SIGCHLD syscall.Signal diff --git a/misc/cgo/test/testx.c b/misc/cgo/test/testx.c new file mode 100644 index 0000000..1258e32 --- /dev/null +++ b/misc/cgo/test/testx.c @@ -0,0 +1,24 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "_cgo_export.h" + +void lockOSThreadC(void) { + lockOSThreadCallback(); +} + +void issue7978c(uint32_t *sync) { + while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 0) + ; + __atomic_add_fetch(sync, 1, __ATOMIC_SEQ_CST); + while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 2) + ; + issue7978cb(); + __atomic_add_fetch(sync, 1, __ATOMIC_SEQ_CST); + while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 6) + ; +} + +void f7665(void) { +} diff --git a/misc/cgo/test/testx.go b/misc/cgo/test/testx.go new file mode 100644 index 0000000..6a8e97d --- /dev/null +++ b/misc/cgo/test/testx.go @@ -0,0 +1,583 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test cases for cgo. +// Both the import "C" prologue and the main file are sorted by issue number. +// This file contains //export directives on Go functions +// and so it must NOT contain C definitions (only declarations). +// See test.go for C definitions. + +package cgotest + +import ( + "runtime" + "runtime/cgo" + "runtime/debug" + "strings" + "sync" + "sync/atomic" + "testing" + "unsafe" +) + +/* +// threads +extern void doAdd(int, int); + +// issue 1328 +void IntoC(void); + +// issue 1560 +extern void Issue1560InC(void); + +// twoSleep returns the absolute start time of the first sleep +// in ms. +long long twoSleep(int); + +// issue 3775 +void lockOSThreadC(void); +int usleep(unsigned usec); + +// issue 4054 part 2 - part 1 in test.go +typedef enum { + A = 0, + B, + C, + D, + E, + F, + G, + H, + II, + J, +} issue4054b; + +// issue 5548 + +extern int issue5548_in_c(void); + +// issue 6833 + +extern unsigned long long issue6833Func(unsigned int, unsigned long long); + +// issue 6907 + +extern int CheckIssue6907C(_GoString_); + +// issue 7665 + +extern void f7665(void); + +// issue 7978 +// Stack tracing didn't work during cgo code after calling a Go +// callback. Make sure GC works and the stack trace is correct. + +#include <stdint.h> + +// use ugly atomic variable sync since that doesn't require calling back into +// Go code or OS dependencies +void issue7978c(uint32_t *sync); + +// issue 8331 part 2 - part 1 in test.go +// A typedef of an unnamed struct is the same struct when +// #include'd twice. No runtime test; just make sure it compiles. +#include "issue8331.h" + +// issue 8945 + +typedef void (*PFunc8945)(); +extern PFunc8945 func8945; // definition is in test.go + +// issue 20910 +void callMulti(void); + +// issue 28772 part 2 - part 1 in issuex.go +#define issue28772Constant2 2 + + +// issue 31891 +typedef struct { + long obj; +} Issue31891A; + +typedef struct { + long obj; +} Issue31891B; + +void callIssue31891(void); + +typedef struct { + int i; +} Issue38408, *PIssue38408; + +extern void cfunc49633(void*); // definition is in test.go +*/ +import "C" + +// exports + +//export ReturnIntLong +func ReturnIntLong() (int, C.long) { + return 1, 2 +} + +//export gc +func gc() { + runtime.GC() +} + +// threads + +var sum struct { + sync.Mutex + i int +} + +//export Add +func Add(x int) { + defer func() { + recover() + }() + sum.Lock() + sum.i += x + sum.Unlock() + var p *int + *p = 2 +} + +func testCthread(t *testing.T) { + if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" { + t.Skip("the iOS exec wrapper is unable to properly handle the panic from Add") + } + sum.i = 0 + C.doAdd(10, 6) + + want := 10 * (10 - 1) / 2 * 6 + if sum.i != want { + t.Fatalf("sum=%d, want %d", sum.i, want) + } +} + +// issue 1328 + +//export BackIntoGo +func BackIntoGo() { + x := 1 + + for i := 0; i < 10000; i++ { + xvariadic(x) + if x != 1 { + panic("x is not 1?") + } + } +} + +func xvariadic(x ...interface{}) { +} + +func test1328(t *testing.T) { + C.IntoC() +} + +// issue 1560 +// Test that C functions and Go functions run in parallel. + +var ( + issue1560 int32 + + issue1560Ch = make(chan bool, 2) +) + +//export Issue1560FromC +func Issue1560FromC() { + for atomic.LoadInt32(&issue1560) != 1 { + runtime.Gosched() + } + atomic.AddInt32(&issue1560, 1) + for atomic.LoadInt32(&issue1560) != 3 { + runtime.Gosched() + } + issue1560Ch <- true +} + +func Issue1560FromGo() { + atomic.AddInt32(&issue1560, 1) + for atomic.LoadInt32(&issue1560) != 2 { + runtime.Gosched() + } + atomic.AddInt32(&issue1560, 1) + issue1560Ch <- true +} + +func test1560(t *testing.T) { + go Issue1560FromGo() + go C.Issue1560InC() + <-issue1560Ch + <-issue1560Ch +} + +// issue 2462 + +//export exportbyte +func exportbyte() byte { + return 0 +} + +//export exportbool +func exportbool() bool { + return false +} + +//export exportrune +func exportrune() rune { + return 0 +} + +//export exporterror +func exporterror() error { + return nil +} + +//export exportint +func exportint() int { + return 0 +} + +//export exportuint +func exportuint() uint { + return 0 +} + +//export exportuintptr +func exportuintptr() uintptr { + return (uintptr)(0) +} + +//export exportint8 +func exportint8() int8 { + return 0 +} + +//export exportuint8 +func exportuint8() uint8 { + return 0 +} + +//export exportint16 +func exportint16() int16 { + return 0 +} + +//export exportuint16 +func exportuint16() uint16 { + return 0 +} + +//export exportint32 +func exportint32() int32 { + return 0 +} + +//export exportuint32 +func exportuint32() uint32 { + return 0 +} + +//export exportint64 +func exportint64() int64 { + return 0 +} + +//export exportuint64 +func exportuint64() uint64 { + return 0 +} + +//export exportfloat32 +func exportfloat32() float32 { + return 0 +} + +//export exportfloat64 +func exportfloat64() float64 { + return 0 +} + +//export exportcomplex64 +func exportcomplex64() complex64 { + return 0 +} + +//export exportcomplex128 +func exportcomplex128() complex128 { + return 0 +} + +// issue 3741 + +//export exportSliceIn +func exportSliceIn(s []byte) bool { + return len(s) == cap(s) +} + +//export exportSliceOut +func exportSliceOut() []byte { + return []byte{1} +} + +//export exportSliceInOut +func exportSliceInOut(s []byte) []byte { + return s +} + +// issue 3775 + +func init() { + if runtime.GOOS == "android" { + return + } + // Same as test3775 but run during init so that + // there are two levels of internal runtime lock + // (1 for init, 1 for cgo). + // This would have been broken by CL 11663043. + C.lockOSThreadC() +} + +func test3775(t *testing.T) { + if runtime.GOOS == "android" { + return + } + // Used to panic because of the UnlockOSThread below. + C.lockOSThreadC() +} + +//export lockOSThreadCallback +func lockOSThreadCallback() { + runtime.LockOSThread() + runtime.UnlockOSThread() + go C.usleep(10000) + runtime.Gosched() +} + +// issue 4054 part 2 - part 1 in test.go + +var issue4054b = []int{C.A, C.B, C.C, C.D, C.E, C.F, C.G, C.H, C.II, C.J} + +//export issue5548FromC +func issue5548FromC(s string, i int) int { + if len(s) == 4 && s == "test" && i == 42 { + return 12345 + } + println("got", len(s), i) + return 9876 +} + +func test5548(t *testing.T) { + if x := C.issue5548_in_c(); x != 12345 { + t.Errorf("issue5548_in_c = %d, want %d", x, 12345) + } +} + +// issue 6833 + +//export GoIssue6833Func +func GoIssue6833Func(aui uint, aui64 uint64) uint64 { + return aui64 + uint64(aui) +} + +func test6833(t *testing.T) { + ui := 7 + ull := uint64(0x4000300020001000) + v := uint64(C.issue6833Func(C.uint(ui), C.ulonglong(ull))) + exp := uint64(ui) + ull + if v != exp { + t.Errorf("issue6833Func() returns %x, expected %x", v, exp) + } +} + +// issue 6907 + +const CString = "C string" + +//export CheckIssue6907Go +func CheckIssue6907Go(s string) C.int { + if s == CString { + return 1 + } + return 0 +} + +func test6907Go(t *testing.T) { + if got := C.CheckIssue6907C(CString); got != 1 { + t.Errorf("C.CheckIssue6907C() == %d, want %d", got, 1) + } +} + +// issue 7665 + +var bad7665 unsafe.Pointer = C.f7665 +var good7665 uintptr = uintptr(C.f7665) + +func test7665(t *testing.T) { + if bad7665 == nil || uintptr(bad7665) != good7665 { + t.Errorf("ptrs = %p, %#x, want same non-nil pointer", bad7665, good7665) + } +} + +// issue 7978 + +var issue7978sync uint32 + +func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) { + runtime.GC() + buf := make([]byte, 65536) + trace := string(buf[:runtime.Stack(buf, true)]) + for _, goroutine := range strings.Split(trace, "\n\n") { + if strings.Contains(goroutine, "test.issue7978go") { + trace := strings.Split(goroutine, "\n") + // look for the expected function in the stack + for i := 0; i < depth; i++ { + if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) { + t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine) + return + } + if strings.Contains(trace[1+2*i], wantFunc) { + return + } + } + t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine) + return + } + } + t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace) +} + +func issue7978wait(store uint32, wait uint32) { + if store != 0 { + atomic.StoreUint32(&issue7978sync, store) + } + for atomic.LoadUint32(&issue7978sync) != wait { + runtime.Gosched() + } +} + +//export issue7978cb +func issue7978cb() { + // Force a stack growth from the callback to put extra + // pressure on the runtime. See issue #17785. + growStack(64) + issue7978wait(3, 4) +} + +func growStack(n int) int { + var buf [128]int + if n == 0 { + return 0 + } + return buf[growStack(n-1)] +} + +func issue7978go() { + C.issue7978c((*C.uint32_t)(&issue7978sync)) + issue7978wait(7, 8) +} + +func test7978(t *testing.T) { + if runtime.Compiler == "gccgo" { + t.Skip("gccgo can not do stack traces of C code") + } + debug.SetTraceback("2") + issue7978sync = 0 + go issue7978go() + // test in c code, before callback + issue7978wait(0, 1) + issue7978check(t, "_Cfunc_issue7978c(", "", 1) + // test in go code, during callback + issue7978wait(2, 3) + issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3) + // test in c code, after callback + issue7978wait(4, 5) + issue7978check(t, "_Cfunc_issue7978c(", "_cgoexpwrap", 1) + // test in go code, after return from cgo + issue7978wait(6, 7) + issue7978check(t, "test.issue7978go(", "", 3) + atomic.StoreUint32(&issue7978sync, 8) +} + +// issue 8331 part 2 + +var issue8331Var C.issue8331 + +// issue 8945 + +//export Test8945 +func Test8945() { + _ = C.func8945 +} + +// issue 20910 + +//export multi +func multi() (*C.char, C.int) { + return C.CString("multi"), 0 +} + +func test20910(t *testing.T) { + C.callMulti() +} + +// issue 28772 part 2 + +const issue28772Constant2 = C.issue28772Constant2 + +// issue 31891 + +//export useIssue31891A +func useIssue31891A(c *C.Issue31891A) {} + +//export useIssue31891B +func useIssue31891B(c *C.Issue31891B) {} + +func test31891(t *testing.T) { + C.callIssue31891() +} + +// issue 37033, check if cgo.Handle works properly + +var issue37033 = 42 + +//export GoFunc37033 +func GoFunc37033(handle C.uintptr_t) { + h := cgo.Handle(handle) + ch := h.Value().(chan int) + ch <- issue37033 +} + +// issue 38408 +// A typedef pointer can be used as the element type. +// No runtime test; just make sure it compiles. +var _ C.PIssue38408 = &C.Issue38408{i: 1} + +// issue 49633, example use of cgo.Handle with void* + +type data49633 struct { + msg string +} + +//export GoFunc49633 +func GoFunc49633(context unsafe.Pointer) { + h := *(*cgo.Handle)(context) + v := h.Value().(*data49633) + v.msg = "hello" +} + +func test49633(t *testing.T) { + v := &data49633{} + h := cgo.NewHandle(v) + defer h.Delete() + C.cfunc49633(unsafe.Pointer(&h)) + if v.msg != "hello" { + t.Errorf("msg = %q, want 'hello'", v.msg) + } +} diff --git a/misc/cgo/test/typeparam.go b/misc/cgo/test/typeparam.go new file mode 100644 index 0000000..5f766c2 --- /dev/null +++ b/misc/cgo/test/typeparam.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotest + +// #include <stddef.h> +import "C" + +func generic[T, U any](t T, u U) {} + +func useGeneric() { + const zero C.size_t = 0 + + generic(zero, zero) + generic[C.size_t, C.size_t](0, 0) +} diff --git a/misc/cgo/testcarchive/carchive_test.go b/misc/cgo/testcarchive/carchive_test.go new file mode 100644 index 0000000..30e12be --- /dev/null +++ b/misc/cgo/testcarchive/carchive_test.go @@ -0,0 +1,1240 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package carchive_test + +import ( + "bufio" + "bytes" + "debug/elf" + "flag" + "fmt" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "strconv" + "strings" + "sync" + "syscall" + "testing" + "time" + "unicode" +) + +// Program to run. +var bin []string + +// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)). +var cc []string + +// ".exe" on Windows. +var exeSuffix string + +var GOOS, GOARCH, GOPATH string +var libgodir string + +var testWork bool // If true, preserve temporary directories. + +func TestMain(m *testing.M) { + flag.BoolVar(&testWork, "testwork", false, "if true, log and preserve the test's temporary working directory") + flag.Parse() + if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { + fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n") + os.Exit(0) + } + if runtime.GOOS == "linux" { + if _, err := os.Stat("/etc/alpine-release"); err == nil { + fmt.Printf("SKIP - skipping failing test on alpine - go.dev/issue/19938\n") + os.Exit(0) + } + } + + log.SetFlags(log.Lshortfile) + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { + // We need a writable GOPATH in which to run the tests. + // Construct one in a temporary directory. + var err error + GOPATH, err = os.MkdirTemp("", "carchive_test") + if err != nil { + log.Panic(err) + } + if testWork { + log.Println(GOPATH) + } else { + defer os.RemoveAll(GOPATH) + } + os.Setenv("GOPATH", GOPATH) + + // Copy testdata into GOPATH/src/testarchive, along with a go.mod file + // declaring the same path. + modRoot := filepath.Join(GOPATH, "src", "testcarchive") + if err := overlayDir(modRoot, "testdata"); err != nil { + log.Panic(err) + } + if err := os.Chdir(modRoot); err != nil { + log.Panic(err) + } + os.Setenv("PWD", modRoot) + if err := os.WriteFile("go.mod", []byte("module testcarchive\n"), 0666); err != nil { + log.Panic(err) + } + + GOOS = goEnv("GOOS") + GOARCH = goEnv("GOARCH") + bin = cmdToRun("./testp") + + ccOut := goEnv("CC") + cc = []string{string(ccOut)} + + out := goEnv("GOGCCFLAGS") + quote := '\000' + start := 0 + lastSpace := true + backslash := false + s := string(out) + for i, c := range s { + if quote == '\000' && unicode.IsSpace(c) { + if !lastSpace { + cc = append(cc, s[start:i]) + lastSpace = true + } + } else { + if lastSpace { + start = i + lastSpace = false + } + if quote == '\000' && !backslash && (c == '"' || c == '\'') { + quote = c + backslash = false + } else if !backslash && quote == c { + quote = '\000' + } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { + backslash = true + } else { + backslash = false + } + } + } + if !lastSpace { + cc = append(cc, s[start:]) + } + + if GOOS == "aix" { + // -Wl,-bnoobjreorder is mandatory to keep the same layout + // in .text section. + cc = append(cc, "-Wl,-bnoobjreorder") + } + libbase := GOOS + "_" + GOARCH + if runtime.Compiler == "gccgo" { + libbase = "gccgo_" + libgodir + "_fPIC" + } else { + switch GOOS { + case "darwin", "ios": + if GOARCH == "arm64" { + libbase += "_shared" + } + case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos": + libbase += "_shared" + } + } + libgodir = filepath.Join(GOPATH, "pkg", libbase, "testcarchive") + cc = append(cc, "-I", libgodir) + + // Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc. + cc = cc[:len(cc):len(cc)] + + if GOOS == "windows" { + exeSuffix = ".exe" + } + + return m.Run() +} + +func goEnv(key string) string { + out, err := exec.Command("go", "env", key).Output() + if err != nil { + if ee, ok := err.(*exec.ExitError); ok { + fmt.Fprintf(os.Stderr, "%s", ee.Stderr) + } + log.Panicf("go env %s failed:\n%s\n", key, err) + } + return strings.TrimSpace(string(out)) +} + +func cmdToRun(name string) []string { + execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec" + executor, err := exec.LookPath(execScript) + if err != nil { + return []string{name} + } + return []string{executor, name} +} + +// genHeader writes a C header file for the C-exported declarations found in .go +// source files in dir. +// +// TODO(golang.org/issue/35715): This should be simpler. +func genHeader(t *testing.T, header, dir string) { + t.Helper() + + // The 'cgo' command generates a number of additional artifacts, + // but we're only interested in the header. + // Shunt the rest of the outputs to a temporary directory. + objDir, err := os.MkdirTemp(GOPATH, "_obj") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(objDir) + + files, err := filepath.Glob(filepath.Join(dir, "*.go")) + if err != nil { + t.Fatal(err) + } + + cmd := exec.Command("go", "tool", "cgo", + "-objdir", objDir, + "-exportheader", header) + cmd.Args = append(cmd.Args, files...) + t.Log(cmd.Args) + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } +} + +func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { + t.Helper() + cmd := exec.Command(buildcmd[0], buildcmd[1:]...) + cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode + t.Log(buildcmd) + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + if !testWork { + defer func() { + os.Remove(libgoa) + os.Remove(libgoh) + }() + } + + ccArgs := append(cc, "-o", exe, "main.c") + if GOOS == "windows" { + ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm") + } else { + ccArgs = append(ccArgs, "main_unix.c", libgoa) + } + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + t.Log(ccArgs) + if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + if !testWork { + defer os.Remove(exe) + } + + binArgs := append(cmdToRun(exe), "arg1", "arg2") + cmd = exec.Command(binArgs[0], binArgs[1:]...) + if runtime.Compiler == "gccgo" { + cmd.Env = append(cmd.Environ(), "GCCGO=1") + } + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + checkLineComments(t, libgoh) +} + +var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`) + +// checkLineComments checks that the export header generated by +// -buildmode=c-archive doesn't have any absolute paths in the #line +// comments. We don't want those paths because they are unhelpful for +// the user and make the files change based on details of the location +// of GOPATH. +func checkLineComments(t *testing.T, hdrname string) { + hdr, err := os.ReadFile(hdrname) + if err != nil { + if !os.IsNotExist(err) { + t.Error(err) + } + return + } + if line := badLineRegexp.Find(hdr); line != nil { + t.Errorf("bad #line directive with absolute path in %s: %q", hdrname, line) + } +} + +// checkArchive verifies that the created library looks OK. +// We just check a couple of things now, we can add more checks as needed. +func checkArchive(t *testing.T, arname string) { + t.Helper() + + switch GOOS { + case "aix", "darwin", "ios", "windows": + // We don't have any checks for non-ELF libraries yet. + if _, err := os.Stat(arname); err != nil { + t.Errorf("archive %s does not exist: %v", arname, err) + } + default: + checkELFArchive(t, arname) + } +} + +// checkELFArchive checks an ELF archive. +func checkELFArchive(t *testing.T, arname string) { + t.Helper() + + f, err := os.Open(arname) + if err != nil { + t.Errorf("archive %s does not exist: %v", arname, err) + return + } + defer f.Close() + + // TODO(iant): put these in a shared package? But where? + const ( + magic = "!<arch>\n" + fmag = "`\n" + + namelen = 16 + datelen = 12 + uidlen = 6 + gidlen = 6 + modelen = 8 + sizelen = 10 + fmaglen = 2 + hdrlen = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen + ) + + type arhdr struct { + name string + date string + uid string + gid string + mode string + size string + fmag string + } + + var magbuf [len(magic)]byte + if _, err := io.ReadFull(f, magbuf[:]); err != nil { + t.Errorf("%s: archive too short", arname) + return + } + if string(magbuf[:]) != magic { + t.Errorf("%s: incorrect archive magic string %q", arname, magbuf) + } + + off := int64(len(magic)) + for { + if off&1 != 0 { + var b [1]byte + if _, err := f.Read(b[:]); err != nil { + if err == io.EOF { + break + } + t.Errorf("%s: error skipping alignment byte at %d: %v", arname, off, err) + } + off++ + } + + var hdrbuf [hdrlen]byte + if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { + if err == io.EOF { + break + } + t.Errorf("%s: error reading archive header at %d: %v", arname, off, err) + return + } + + var hdr arhdr + hdrslice := hdrbuf[:] + set := func(len int, ps *string) { + *ps = string(bytes.TrimSpace(hdrslice[:len])) + hdrslice = hdrslice[len:] + } + set(namelen, &hdr.name) + set(datelen, &hdr.date) + set(uidlen, &hdr.uid) + set(gidlen, &hdr.gid) + set(modelen, &hdr.mode) + set(sizelen, &hdr.size) + hdr.fmag = string(hdrslice[:fmaglen]) + hdrslice = hdrslice[fmaglen:] + if len(hdrslice) != 0 { + t.Fatalf("internal error: len(hdrslice) == %d", len(hdrslice)) + } + + if hdr.fmag != fmag { + t.Errorf("%s: invalid fmagic value %q at %d", arname, hdr.fmag, off) + return + } + + size, err := strconv.ParseInt(hdr.size, 10, 64) + if err != nil { + t.Errorf("%s: error parsing size %q at %d: %v", arname, hdr.size, off, err) + return + } + + off += hdrlen + + switch hdr.name { + case "__.SYMDEF", "/", "/SYM64/": + // The archive symbol map. + case "//", "ARFILENAMES/": + // The extended name table. + default: + // This should be an ELF object. + checkELFArchiveObject(t, arname, off, io.NewSectionReader(f, off, size)) + } + + off += size + if _, err := f.Seek(off, io.SeekStart); err != nil { + t.Errorf("%s: failed to seek to %d: %v", arname, off, err) + } + } +} + +// checkELFArchiveObject checks an object in an ELF archive. +func checkELFArchiveObject(t *testing.T, arname string, off int64, obj io.ReaderAt) { + t.Helper() + + ef, err := elf.NewFile(obj) + if err != nil { + t.Errorf("%s: failed to open ELF file at %d: %v", arname, off, err) + return + } + defer ef.Close() + + // Verify section types. + for _, sec := range ef.Sections { + want := elf.SHT_NULL + switch sec.Name { + case ".text", ".data": + want = elf.SHT_PROGBITS + case ".bss": + want = elf.SHT_NOBITS + case ".symtab": + want = elf.SHT_SYMTAB + case ".strtab": + want = elf.SHT_STRTAB + case ".init_array": + want = elf.SHT_INIT_ARRAY + case ".fini_array": + want = elf.SHT_FINI_ARRAY + case ".preinit_array": + want = elf.SHT_PREINIT_ARRAY + } + if want != elf.SHT_NULL && sec.Type != want { + t.Errorf("%s: incorrect section type in elf file at %d for section %q: got %v want %v", arname, off, sec.Name, sec.Type, want) + } + } +} + +func TestInstall(t *testing.T) { + if !testWork { + defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) + } + + libgoa := "libgo.a" + if runtime.Compiler == "gccgo" { + libgoa = "liblibgo.a" + } + + // Generate the p.h header file. + // + // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that + // would also attempt to install transitive standard-library dependencies to + // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may + // be running this test in a GOROOT owned by root.) + genHeader(t, "p.h", "./p") + + testInstall(t, "./testp1"+exeSuffix, + filepath.Join(libgodir, libgoa), + filepath.Join(libgodir, "libgo.h"), + "go", "install", "-buildmode=c-archive", "./libgo") + + // Test building libgo other than installing it. + // Header files are now present. + testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h", + "go", "build", "-buildmode=c-archive", filepath.Join(".", "libgo", "libgo.go")) + + testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h", + "go", "build", "-buildmode=c-archive", "-o", "libgo.a", "./libgo") +} + +func TestEarlySignalHandler(t *testing.T) { + switch GOOS { + case "darwin", "ios": + switch GOARCH { + case "arm64": + t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) + } + case "windows": + t.Skip("skipping signal test on Windows") + } + + if !testWork { + defer func() { + os.Remove("libgo2.a") + os.Remove("libgo2.h") + os.Remove("testp" + exeSuffix) + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") + + ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a") + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + darwin := "0" + if runtime.GOOS == "darwin" { + darwin = "1" + } + cmd = exec.Command(bin[0], append(bin[1:], darwin)...) + + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } +} + +func TestSignalForwarding(t *testing.T) { + checkSignalForwardingTest(t) + buildSignalForwardingTest(t) + + cmd := exec.Command(bin[0], append(bin[1:], "1")...) + + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + expectSignal(t, err, syscall.SIGSEGV, 0) + + // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. + if runtime.GOOS != "darwin" && runtime.GOOS != "ios" { + // Test SIGPIPE forwarding + cmd = exec.Command(bin[0], append(bin[1:], "3")...) + + out, err = cmd.CombinedOutput() + if len(out) > 0 { + t.Logf("%s", out) + } + expectSignal(t, err, syscall.SIGPIPE, 0) + } +} + +func TestSignalForwardingExternal(t *testing.T) { + if GOOS == "freebsd" || GOOS == "aix" { + t.Skipf("skipping on %s/%s; signal always goes to the Go runtime", GOOS, GOARCH) + } else if GOOS == "darwin" && GOARCH == "amd64" { + t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH) + } + checkSignalForwardingTest(t) + buildSignalForwardingTest(t) + + // We want to send the process a signal and see if it dies. + // Normally the signal goes to the C thread, the Go signal + // handler picks it up, sees that it is running in a C thread, + // and the program dies. Unfortunately, occasionally the + // signal is delivered to a Go thread, which winds up + // discarding it because it was sent by another program and + // there is no Go handler for it. To avoid this, run the + // program several times in the hopes that it will eventually + // fail. + const tries = 20 + for i := 0; i < tries; i++ { + err := runSignalForwardingTest(t, "2") + if err == nil { + continue + } + + // If the signal is delivered to a C thread, as expected, + // the Go signal handler will disable itself and re-raise + // the signal, causing the program to die with SIGSEGV. + // + // It is also possible that the signal will be + // delivered to a Go thread, such as a GC thread. + // Currently when the Go runtime sees that a SIGSEGV was + // sent from a different program, it first tries to send + // the signal to the os/signal API. If nothing is looking + // for (or explicitly ignoring) SIGSEGV, then it crashes. + // Because the Go runtime is invoked via a c-archive, + // it treats this as GOTRACEBACK=crash, meaning that it + // dumps a stack trace for all goroutines, which it does + // by raising SIGQUIT. The effect is that we will see the + // program die with SIGQUIT in that case, not SIGSEGV. + if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) { + return + } + } + + t.Errorf("program succeeded unexpectedly %d times", tries) +} + +func TestSignalForwardingGo(t *testing.T) { + // This test fails on darwin-amd64 because of the special + // handling of user-generated SIGSEGV signals in fixsigcode in + // runtime/signal_darwin_amd64.go. + if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" { + t.Skip("not supported on darwin-amd64") + } + + checkSignalForwardingTest(t) + buildSignalForwardingTest(t) + err := runSignalForwardingTest(t, "4") + + // Occasionally the signal will be delivered to a C thread, + // and the program will crash with SIGSEGV. + expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV) +} + +// checkSignalForwardingTest calls t.Skip if the SignalForwarding test +// doesn't work on this platform. +func checkSignalForwardingTest(t *testing.T) { + switch GOOS { + case "darwin", "ios": + switch GOARCH { + case "arm64": + t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) + } + case "windows": + t.Skip("skipping signal test on Windows") + } +} + +// buildSignalForwardingTest builds the executable used by the various +// signal forwarding tests. +func buildSignalForwardingTest(t *testing.T) { + if !testWork { + t.Cleanup(func() { + os.Remove("libgo2.a") + os.Remove("libgo2.h") + os.Remove("testp" + exeSuffix) + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }) + } + + t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2") + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") + out, err := cmd.CombinedOutput() + if len(out) > 0 { + t.Logf("%s", out) + } + if err != nil { + t.Fatal(err) + } + + checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") + + ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + t.Log(ccArgs) + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + if len(out) > 0 { + t.Logf("%s", out) + } + if err != nil { + t.Fatal(err) + } +} + +func runSignalForwardingTest(t *testing.T, arg string) error { + t.Logf("%v %s", bin, arg) + cmd := exec.Command(bin[0], append(bin[1:], arg)...) + + var out strings.Builder + cmd.Stdout = &out + + stderr, err := cmd.StderrPipe() + if err != nil { + t.Fatal(err) + } + defer stderr.Close() + + r := bufio.NewReader(stderr) + + err = cmd.Start() + if err != nil { + t.Fatal(err) + } + + // Wait for trigger to ensure that process is started. + ok, err := r.ReadString('\n') + + // Verify trigger. + if err != nil || ok != "OK\n" { + t.Fatal("Did not receive OK signal") + } + + var wg sync.WaitGroup + wg.Add(1) + var errsb strings.Builder + go func() { + defer wg.Done() + io.Copy(&errsb, r) + }() + + // Give the program a chance to enter the function. + // If the program doesn't get there the test will still + // pass, although it doesn't quite test what we intended. + // This is fine as long as the program normally makes it. + time.Sleep(time.Millisecond) + + cmd.Process.Signal(syscall.SIGSEGV) + + err = cmd.Wait() + + s := out.String() + if len(s) > 0 { + t.Log(s) + } + wg.Wait() + s = errsb.String() + if len(s) > 0 { + t.Log(s) + } + + return err +} + +// expectSignal checks that err, the exit status of a test program, +// shows a failure due to a specific signal or two. Returns whether we +// found an expected signal. +func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool { + t.Helper() + if err == nil { + t.Error("test program succeeded unexpectedly") + } else if ee, ok := err.(*exec.ExitError); !ok { + t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) + } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { + t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) + } else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) { + if sig2 == 0 { + t.Errorf("got %q; expected signal %q", ee, sig1) + } else { + t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2) + } + } else { + return true + } + return false +} + +func TestOsSignal(t *testing.T) { + switch GOOS { + case "windows": + t.Skip("skipping signal test on Windows") + } + + if !testWork { + defer func() { + os.Remove("libgo3.a") + os.Remove("libgo3.h") + os.Remove("testp" + exeSuffix) + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3") + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + checkLineComments(t, "libgo3.h") + checkArchive(t, "libgo3.a") + + ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a") + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } +} + +func TestSigaltstack(t *testing.T) { + switch GOOS { + case "windows": + t.Skip("skipping signal test on Windows") + } + + if !testWork { + defer func() { + os.Remove("libgo4.a") + os.Remove("libgo4.h") + os.Remove("testp" + exeSuffix) + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4") + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + checkLineComments(t, "libgo4.h") + checkArchive(t, "libgo4.a") + + ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a") + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } +} + +const testar = `#!/usr/bin/env bash +while [[ $1 == -* ]] >/dev/null; do + shift +done +echo "testar" > $1 +echo "testar" > PWD/testar.ran +` + +func TestExtar(t *testing.T) { + switch GOOS { + case "windows": + t.Skip("skipping signal test on Windows") + } + if runtime.Compiler == "gccgo" { + t.Skip("skipping -extar test when using gccgo") + } + if runtime.GOOS == "ios" { + t.Skip("shell scripts are not executable on iOS hosts") + } + + if !testWork { + defer func() { + os.Remove("libgo4.a") + os.Remove("libgo4.h") + os.Remove("testar") + os.Remove("testar.ran") + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } + + os.Remove("testar") + dir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + s := strings.Replace(testar, "PWD", dir, 1) + if err := os.WriteFile("testar", []byte(s), 0777); err != nil { + t.Fatal(err) + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "./libgo4") + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + checkLineComments(t, "libgo4.h") + + if _, err := os.Stat("testar.ran"); err != nil { + if os.IsNotExist(err) { + t.Error("testar does not exist after go build") + } else { + t.Errorf("error checking testar: %v", err) + } + } +} + +func TestPIE(t *testing.T) { + switch GOOS { + case "windows", "darwin", "ios", "plan9": + t.Skipf("skipping PIE test on %s", GOOS) + } + + libgoa := "libgo.a" + if runtime.Compiler == "gccgo" { + libgoa = "liblibgo.a" + } + + if !testWork { + defer func() { + os.Remove("testp" + exeSuffix) + os.Remove(libgoa) + os.RemoveAll(filepath.Join(GOPATH, "pkg")) + }() + } + + // Generate the p.h header file. + // + // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that + // would also attempt to install transitive standard-library dependencies to + // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may + // be running this test in a GOROOT owned by root.) + genHeader(t, "p.h", "./p") + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "./libgo") + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", libgoa) + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + binArgs := append(bin, "arg1", "arg2") + cmd = exec.Command(binArgs[0], binArgs[1:]...) + if runtime.Compiler == "gccgo" { + cmd.Env = append(os.Environ(), "GCCGO=1") + } + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + if GOOS != "aix" { + f, err := elf.Open("testp" + exeSuffix) + if err != nil { + t.Fatal("elf.Open failed: ", err) + } + defer f.Close() + if hasDynTag(t, f, elf.DT_TEXTREL) { + t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix) + } + } +} + +func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool { + ds := f.SectionByType(elf.SHT_DYNAMIC) + if ds == nil { + t.Error("no SHT_DYNAMIC section") + return false + } + d, err := ds.Data() + if err != nil { + t.Errorf("can't read SHT_DYNAMIC contents: %v", err) + return false + } + for len(d) > 0 { + var t elf.DynTag + switch f.Class { + case elf.ELFCLASS32: + t = elf.DynTag(f.ByteOrder.Uint32(d[:4])) + d = d[8:] + case elf.ELFCLASS64: + t = elf.DynTag(f.ByteOrder.Uint64(d[:8])) + d = d[16:] + } + if t == tag { + return true + } + } + return false +} + +func TestSIGPROF(t *testing.T) { + switch GOOS { + case "windows", "plan9": + t.Skipf("skipping SIGPROF test on %s", GOOS) + case "darwin", "ios": + t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS) + } + + t.Parallel() + + if !testWork { + defer func() { + os.Remove("testp6" + exeSuffix) + os.Remove("libgo6.a") + os.Remove("libgo6.h") + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6") + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { + t.Fatal(err) + } + checkLineComments(t, "libgo6.h") + checkArchive(t, "libgo6.a") + + ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a") + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + if err != nil { + t.Fatal(err) + } + + argv := cmdToRun("./testp6") + cmd = exec.Command(argv[0], argv[1:]...) + out, err = cmd.CombinedOutput() + t.Logf("%v\n%s", argv, out) + if err != nil { + t.Fatal(err) + } +} + +// TestCompileWithoutShared tests that if we compile code without the +// -shared option, we can put it into an archive. When we use the go +// tool with -buildmode=c-archive, it passes -shared to the compiler, +// so we override that. The go tool doesn't work this way, but Bazel +// will likely do it in the future. And it ought to work. This test +// was added because at one time it did not work on PPC Linux. +func TestCompileWithoutShared(t *testing.T) { + // For simplicity, reuse the signal forwarding test. + checkSignalForwardingTest(t) + + if !testWork { + defer func() { + os.Remove("libgo2.a") + os.Remove("libgo2.h") + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2") + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { + t.Fatal(err) + } + checkLineComments(t, "libgo2.h") + checkArchive(t, "libgo2.a") + + exe := "./testnoshared" + exeSuffix + + // In some cases, -no-pie is needed here, but not accepted everywhere. First try + // if -no-pie is accepted. See #22126. + ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a") + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + + // If -no-pie unrecognized, try -nopie if this is possibly clang + if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") { + ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a") + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + } + + // Don't use either -no-pie or -nopie + if err != nil && bytes.Contains(out, []byte("unrecognized")) { + ccArgs = append(cc, "-o", exe, "main5.c", "libgo2.a") + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + } + if err != nil { + t.Fatal(err) + } + if !testWork { + defer os.Remove(exe) + } + + binArgs := append(cmdToRun(exe), "1") + out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", binArgs, out) + expectSignal(t, err, syscall.SIGSEGV, 0) + + // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. + if runtime.GOOS != "darwin" && runtime.GOOS != "ios" { + binArgs := append(cmdToRun(exe), "3") + out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", binArgs, out) + expectSignal(t, err, syscall.SIGPIPE, 0) + } +} + +// Test that installing a second time recreates the header file. +func TestCachedInstall(t *testing.T) { + if !testWork { + defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) + } + + h := filepath.Join(libgodir, "libgo.h") + + buildcmd := []string{"go", "install", "-buildmode=c-archive", "./libgo"} + + cmd := exec.Command(buildcmd[0], buildcmd[1:]...) + cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode + t.Log(buildcmd) + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + if _, err := os.Stat(h); err != nil { + t.Errorf("libgo.h not installed: %v", err) + } + + if err := os.Remove(h); err != nil { + t.Fatal(err) + } + + cmd = exec.Command(buildcmd[0], buildcmd[1:]...) + cmd.Env = append(cmd.Environ(), "GO111MODULE=off") + t.Log(buildcmd) + if out, err := cmd.CombinedOutput(); err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + if _, err := os.Stat(h); err != nil { + t.Errorf("libgo.h not installed in second run: %v", err) + } +} + +// Issue 35294. +func TestManyCalls(t *testing.T) { + t.Parallel() + + if !testWork { + defer func() { + os.Remove("testp7" + exeSuffix) + os.Remove("libgo7.a") + os.Remove("libgo7.h") + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7") + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { + t.Fatal(err) + } + checkLineComments(t, "libgo7.h") + checkArchive(t, "libgo7.a") + + ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a") + if runtime.Compiler == "gccgo" { + ccArgs = append(ccArgs, "-lgo") + } + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + if err != nil { + t.Fatal(err) + } + + argv := cmdToRun("./testp7") + cmd = exec.Command(argv[0], argv[1:]...) + sb := new(strings.Builder) + cmd.Stdout = sb + cmd.Stderr = sb + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + + timer := time.AfterFunc(time.Minute, + func() { + t.Error("test program timed out") + cmd.Process.Kill() + }, + ) + defer timer.Stop() + + err = cmd.Wait() + t.Logf("%v\n%s", cmd.Args, sb) + if err != nil { + t.Error(err) + } +} + +// Issue 49288. +func TestPreemption(t *testing.T) { + if runtime.Compiler == "gccgo" { + t.Skip("skipping asynchronous preemption test with gccgo") + } + + t.Parallel() + + if !testWork { + defer func() { + os.Remove("testp8" + exeSuffix) + os.Remove("libgo8.a") + os.Remove("libgo8.h") + }() + } + + cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8") + out, err := cmd.CombinedOutput() + t.Logf("%v\n%s", cmd.Args, out) + if err != nil { + t.Fatal(err) + } + checkLineComments(t, "libgo8.h") + checkArchive(t, "libgo8.a") + + ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a") + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + t.Logf("%v\n%s", ccArgs, out) + if err != nil { + t.Fatal(err) + } + + argv := cmdToRun("./testp8") + cmd = exec.Command(argv[0], argv[1:]...) + sb := new(strings.Builder) + cmd.Stdout = sb + cmd.Stderr = sb + if err := cmd.Start(); err != nil { + t.Fatal(err) + } + + timer := time.AfterFunc(time.Minute, + func() { + t.Error("test program timed out") + cmd.Process.Kill() + }, + ) + defer timer.Stop() + + err = cmd.Wait() + t.Logf("%v\n%s", cmd.Args, sb) + if err != nil { + t.Error(err) + } +} diff --git a/misc/cgo/testcarchive/overlaydir_test.go b/misc/cgo/testcarchive/overlaydir_test.go new file mode 100644 index 0000000..67974c5 --- /dev/null +++ b/misc/cgo/testcarchive/overlaydir_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package carchive_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/testcarchive/testdata/libgo/libgo.go b/misc/cgo/testcarchive/testdata/libgo/libgo.go new file mode 100644 index 0000000..37b30c1 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/libgo/libgo.go @@ -0,0 +1,53 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "syscall" + "time" + + _ "testcarchive/p" +) + +import "C" + +var initCh = make(chan int, 1) +var ranMain bool + +func init() { + // emulate an exceedingly slow package initialization function + time.Sleep(100 * time.Millisecond) + initCh <- 42 +} + +func main() { ranMain = true } + +//export DidInitRun +func DidInitRun() bool { + select { + case x := <-initCh: + if x != 42 { + // Just in case initCh was not correctly made. + println("want init value of 42, got: ", x) + syscall.Exit(2) + } + return true + default: + return false + } +} + +//export DidMainRun +func DidMainRun() bool { return ranMain } + +//export CheckArgs +func CheckArgs() { + if len(os.Args) != 3 || os.Args[1] != "arg1" || os.Args[2] != "arg2" { + fmt.Printf("CheckArgs: want [_, arg1, arg2], got: %v\n", os.Args) + os.Exit(2) + } +} diff --git a/misc/cgo/testcarchive/testdata/libgo2/libgo2.go b/misc/cgo/testcarchive/testdata/libgo2/libgo2.go new file mode 100644 index 0000000..35c89ae --- /dev/null +++ b/misc/cgo/testcarchive/testdata/libgo2/libgo2.go @@ -0,0 +1,86 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +// Raise SIGPIPE. +static void CRaiseSIGPIPE() { + int fds[2]; + + if (pipe(fds) == -1) { + perror("pipe"); + exit(EXIT_FAILURE); + } + // Close the reader end + close(fds[0]); + // Write to the writer end to provoke a SIGPIPE + if (write(fds[1], "some data", 9) != -1) { + fprintf(stderr, "write to a closed pipe succeeded\n"); + exit(EXIT_FAILURE); + } + close(fds[1]); +} +*/ +import "C" + +import ( + "fmt" + "os" + "runtime" +) + +// RunGoroutines starts some goroutines that don't do anything. +// The idea is to get some threads going, so that a signal will be delivered +// to a thread started by Go. +//export RunGoroutines +func RunGoroutines() { + for i := 0; i < 4; i++ { + go func() { + runtime.LockOSThread() + select {} + }() + } +} + +// Block blocks the current thread while running Go code. +//export Block +func Block() { + select {} +} + +var P *byte + +// TestSEGV makes sure that an invalid address turns into a run-time Go panic. +//export TestSEGV +func TestSEGV() { + defer func() { + if recover() == nil { + fmt.Fprintln(os.Stderr, "no panic from segv") + os.Exit(1) + } + }() + *P = 0 + fmt.Fprintln(os.Stderr, "continued after segv") + os.Exit(1) +} + +// Noop ensures that the Go runtime is initialized. +//export Noop +func Noop() { +} + +// Raise SIGPIPE. +//export GoRaiseSIGPIPE +func GoRaiseSIGPIPE() { + C.CRaiseSIGPIPE() +} + +func main() { +} diff --git a/misc/cgo/testcarchive/testdata/libgo3/libgo3.go b/misc/cgo/testcarchive/testdata/libgo3/libgo3.go new file mode 100644 index 0000000..3725f7a --- /dev/null +++ b/misc/cgo/testcarchive/testdata/libgo3/libgo3.go @@ -0,0 +1,56 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "C" + +import ( + "os" + "os/signal" + "syscall" + "time" +) + +// The channel used to read SIGIO signals. +var sigioChan chan os.Signal + +// CatchSIGIO starts catching SIGIO signals. +//export CatchSIGIO +func CatchSIGIO() { + sigioChan = make(chan os.Signal, 1) + signal.Notify(sigioChan, syscall.SIGIO) +} + +// ResetSIGIO stops catching SIGIO signals. +//export ResetSIGIO +func ResetSIGIO() { + signal.Reset(syscall.SIGIO) +} + +// SawSIGIO reports whether we saw a SIGIO. +//export SawSIGIO +func SawSIGIO() C.int { + select { + case <-sigioChan: + return 1 + case <-time.After(5 * time.Second): + return 0 + } +} + +// ProvokeSIGPIPE provokes a kernel-initiated SIGPIPE. +//export ProvokeSIGPIPE +func ProvokeSIGPIPE() { + r, w, err := os.Pipe() + if err != nil { + panic(err) + } + r.Close() + defer w.Close() + w.Write([]byte("some data")) +} + +func main() { +} diff --git a/misc/cgo/testcarchive/testdata/libgo4/libgo4.go b/misc/cgo/testcarchive/testdata/libgo4/libgo4.go new file mode 100644 index 0000000..8cc1895 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/libgo4/libgo4.go @@ -0,0 +1,52 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <signal.h> +#include <pthread.h> + +// Raise SIGIO. +static void CRaiseSIGIO(pthread_t* p) { + pthread_kill(*p, SIGIO); +} +*/ +import "C" + +import ( + "os" + "os/signal" + "sync/atomic" + "syscall" +) + +var sigioCount int32 + +// Catch SIGIO. +//export GoCatchSIGIO +func GoCatchSIGIO() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGIO) + go func() { + for range c { + atomic.AddInt32(&sigioCount, 1) + } + }() +} + +// Raise SIGIO. +//export GoRaiseSIGIO +func GoRaiseSIGIO(p *C.pthread_t) { + C.CRaiseSIGIO(p) +} + +// Return the number of SIGIO signals seen. +//export SIGIOCount +func SIGIOCount() C.int { + return C.int(atomic.LoadInt32(&sigioCount)) +} + +func main() { +} diff --git a/misc/cgo/testcarchive/testdata/libgo6/sigprof.go b/misc/cgo/testcarchive/testdata/libgo6/sigprof.go new file mode 100644 index 0000000..31527c5 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/libgo6/sigprof.go @@ -0,0 +1,25 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "io" + "runtime/pprof" +) + +import "C" + +//export go_start_profile +func go_start_profile() { + pprof.StartCPUProfile(io.Discard) +} + +//export go_stop_profile +func go_stop_profile() { + pprof.StopCPUProfile() +} + +func main() { +} diff --git a/misc/cgo/testcarchive/testdata/libgo7/sink.go b/misc/cgo/testcarchive/testdata/libgo7/sink.go new file mode 100644 index 0000000..d61638b --- /dev/null +++ b/misc/cgo/testcarchive/testdata/libgo7/sink.go @@ -0,0 +1,17 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "C" + +var sink []byte + +//export GoFunction7 +func GoFunction7() { + sink = make([]byte, 4096) +} + +func main() { +} diff --git a/misc/cgo/testcarchive/testdata/libgo8/a.go b/misc/cgo/testcarchive/testdata/libgo8/a.go new file mode 100644 index 0000000..718418e --- /dev/null +++ b/misc/cgo/testcarchive/testdata/libgo8/a.go @@ -0,0 +1,36 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "C" + +import ( + "os" + "runtime" + "sync/atomic" +) + +var started int32 + +// Start a goroutine that loops forever. +func init() { + runtime.GOMAXPROCS(1) + go func() { + for { + atomic.StoreInt32(&started, 1) + } + }() +} + +//export GoFunction8 +func GoFunction8() { + for atomic.LoadInt32(&started) == 0 { + runtime.Gosched() + } + os.Exit(0) +} + +func main() { +} diff --git a/misc/cgo/testcarchive/testdata/main.c b/misc/cgo/testcarchive/testdata/main.c new file mode 100644 index 0000000..163b539 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main.c @@ -0,0 +1,48 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "p.h" +#include "libgo.h" + +extern int install_handler(); +extern int check_handler(); + +int main(void) { + int32_t res; + + int r1 = install_handler(); + if (r1!=0) { + return r1; + } + + if (!DidInitRun()) { + fprintf(stderr, "ERROR: buildmode=c-archive init should run\n"); + return 2; + } + + if (DidMainRun()) { + fprintf(stderr, "ERROR: buildmode=c-archive should not run main\n"); + return 2; + } + + int r2 = check_handler(); + if (r2!=0) { + return r2; + } + + res = FromPkg(); + if (res != 1024) { + fprintf(stderr, "ERROR: FromPkg()=%d, want 1024\n", res); + return 2; + } + + CheckArgs(); + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/misc/cgo/testcarchive/testdata/main2.c b/misc/cgo/testcarchive/testdata/main2.c new file mode 100644 index 0000000..da35673 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main2.c @@ -0,0 +1,239 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test installing a signal handler before the Go code starts. +// This is a lot like misc/cgo/testcshared/main4.c. + +#include <setjmp.h> +#include <signal.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <sched.h> +#include <time.h> +#include <errno.h> + +#include "libgo2.h" + +static void die(const char* msg) { + perror(msg); + exit(EXIT_FAILURE); +} + +static volatile sig_atomic_t sigioSeen; +static volatile sig_atomic_t sigpipeSeen; + +// Use up some stack space. +static void recur(int i, char *p) { + char a[1024]; + + *p = '\0'; + if (i > 0) { + recur(i - 1, a); + } +} + +static void pipeHandler(int signo, siginfo_t* info, void* ctxt) { + sigpipeSeen = 1; +} + +// Signal handler that uses up more stack space than a goroutine will have. +static void ioHandler(int signo, siginfo_t* info, void* ctxt) { + char a[1024]; + + recur(4, a); + sigioSeen = 1; +} + +static jmp_buf jmp; +static char* nullPointer; + +// An arbitrary function which requires proper stack alignment; see +// http://golang.org/issue/17641. +static void callWithVarargs(void* dummy, ...) { + va_list args; + va_start(args, dummy); + va_end(args); +} + +// Signal handler for SIGSEGV on a C thread. +static void segvHandler(int signo, siginfo_t* info, void* ctxt) { + sigset_t mask; + int i; + + // Call an arbitrary function that requires the stack to be properly aligned. + callWithVarargs("dummy arg", 3.1415); + + if (sigemptyset(&mask) < 0) { + die("sigemptyset"); + } + if (sigaddset(&mask, SIGSEGV) < 0) { + die("sigaddset"); + } + i = sigprocmask(SIG_UNBLOCK, &mask, NULL); + if (i != 0) { + fprintf(stderr, "sigprocmask: %s\n", strerror(i)); + exit(EXIT_FAILURE); + } + + // Don't try this at home. + longjmp(jmp, signo); + + // We should never get here. + abort(); +} + +// Set up the signal handlers in a high priority constructor, +// so that they are installed before the Go code starts. + +static void init(void) __attribute__ ((constructor (200))); + +static void init() { + struct sigaction sa; + + memset(&sa, 0, sizeof sa); + sa.sa_sigaction = ioHandler; + if (sigemptyset(&sa.sa_mask) < 0) { + die("sigemptyset"); + } + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGIO, &sa, NULL) < 0) { + die("sigaction"); + } + + sa.sa_sigaction = segvHandler; + if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) { + die("sigaction"); + } + + sa.sa_sigaction = pipeHandler; + if (sigaction(SIGPIPE, &sa, NULL) < 0) { + die("sigaction"); + } +} + +int main(int argc, char** argv) { + int verbose; + sigset_t mask; + int i; + struct timespec ts; + int darwin; + + darwin = atoi(argv[1]); + + verbose = argc > 2; + + setvbuf(stdout, NULL, _IONBF, 0); + + // Call setsid so that we can use kill(0, SIGIO) below. + // Don't check the return value so that this works both from + // a job control shell and from a shell script. + setsid(); + + if (verbose) { + printf("calling RunGoroutines\n"); + } + + RunGoroutines(); + + // Block SIGIO in this thread to make it more likely that it + // will be delivered to a goroutine. + + if (verbose) { + printf("calling pthread_sigmask\n"); + } + + if (sigemptyset(&mask) < 0) { + die("sigemptyset"); + } + if (sigaddset(&mask, SIGIO) < 0) { + die("sigaddset"); + } + i = pthread_sigmask(SIG_BLOCK, &mask, NULL); + if (i != 0) { + fprintf(stderr, "pthread_sigmask: %s\n", strerror(i)); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling kill\n"); + } + + if (kill(0, SIGIO) < 0) { + die("kill"); + } + + if (verbose) { + printf("waiting for sigioSeen\n"); + } + + // Wait until the signal has been delivered. + i = 0; + while (!sigioSeen) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for SIGIO\n"); + exit(EXIT_FAILURE); + } + } + + if (verbose) { + printf("provoking SIGPIPE\n"); + } + + // SIGPIPE is never forwarded on Darwin, see golang.org/issue/33384. + if (!darwin) { + GoRaiseSIGPIPE(); + + if (verbose) { + printf("waiting for sigpipeSeen\n"); + } + + // Wait until the signal has been delivered. + i = 0; + while (!sigpipeSeen) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for SIGPIPE\n"); + exit(EXIT_FAILURE); + } + } + } + + if (verbose) { + printf("calling setjmp\n"); + } + + // Test that a SIGSEGV on this thread is delivered to us. + if (setjmp(jmp) == 0) { + if (verbose) { + printf("triggering SIGSEGV\n"); + } + + *nullPointer = '\0'; + + fprintf(stderr, "continued after address error\n"); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling TestSEGV\n"); + } + + TestSEGV(); + + printf("PASS\n"); + return 0; +} diff --git a/misc/cgo/testcarchive/testdata/main3.c b/misc/cgo/testcarchive/testdata/main3.c new file mode 100644 index 0000000..4d11d9c --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main3.c @@ -0,0 +1,210 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test os/signal.Notify and os/signal.Reset. +// This is a lot like misc/cgo/testcshared/main5.c. + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sched.h> +#include <unistd.h> +#include <pthread.h> + +#include "libgo3.h" + +static void die(const char* msg) { + perror(msg); + exit(EXIT_FAILURE); +} + +static volatile sig_atomic_t sigioSeen; + +static void ioHandler(int signo, siginfo_t* info, void* ctxt) { + sigioSeen = 1; +} + +// Set up the SIGPIPE signal handler in a high priority constructor, so +// that it is installed before the Go code starts. + +static void pipeHandler(int signo, siginfo_t* info, void* ctxt) { + const char *s = "unexpected SIGPIPE\n"; + write(2, s, strlen(s)); + exit(EXIT_FAILURE); +} + +static void init(void) __attribute__ ((constructor (200))); + +static void init() { + struct sigaction sa; + + memset(&sa, 0, sizeof sa); + sa.sa_sigaction = pipeHandler; + if (sigemptyset(&sa.sa_mask) < 0) { + die("sigemptyset"); + } + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGPIPE, &sa, NULL) < 0) { + die("sigaction"); + } +} + +static void *provokeSIGPIPE(void *arg) { + ProvokeSIGPIPE(); + return NULL; +} + +int main(int argc, char** argv) { + int verbose; + struct sigaction sa; + int i; + struct timespec ts; + int res; + pthread_t tid; + + verbose = argc > 2; + setvbuf(stdout, NULL, _IONBF, 0); + + if (verbose) { + printf("raising SIGPIPE\n"); + } + + // Test that the Go runtime handles SIGPIPE, even if we installed + // a non-default SIGPIPE handler before the runtime initializes. + ProvokeSIGPIPE(); + + // Test that SIGPIPE on a non-main thread is also handled by Go. + res = pthread_create(&tid, NULL, provokeSIGPIPE, NULL); + if (res != 0) { + fprintf(stderr, "pthread_create: %s\n", strerror(res)); + exit(EXIT_FAILURE); + } + + res = pthread_join(tid, NULL); + if (res != 0) { + fprintf(stderr, "pthread_join: %s\n", strerror(res)); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling sigaction\n"); + } + + memset(&sa, 0, sizeof sa); + sa.sa_sigaction = ioHandler; + if (sigemptyset(&sa.sa_mask) < 0) { + die("sigemptyset"); + } + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGIO, &sa, NULL) < 0) { + die("sigaction"); + } + + // At this point there should not be a Go signal handler + // installed for SIGIO. + + if (verbose) { + printf("raising SIGIO\n"); + } + + if (raise(SIGIO) < 0) { + die("raise"); + } + + if (verbose) { + printf("waiting for sigioSeen\n"); + } + + // Wait until the signal has been delivered. + i = 0; + while (!sigioSeen) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for signal\n"); + exit(EXIT_FAILURE); + } + } + + sigioSeen = 0; + + // Tell the Go code to catch SIGIO. + + if (verbose) { + printf("calling CatchSIGIO\n"); + } + + CatchSIGIO(); + + if (verbose) { + printf("raising SIGIO\n"); + } + + if (raise(SIGIO) < 0) { + die("raise"); + } + + if (verbose) { + printf("calling SawSIGIO\n"); + } + + if (!SawSIGIO()) { + fprintf(stderr, "Go handler did not see SIGIO\n"); + exit(EXIT_FAILURE); + } + + if (sigioSeen != 0) { + fprintf(stderr, "C handler saw SIGIO when only Go handler should have\n"); + exit(EXIT_FAILURE); + } + + // Tell the Go code to stop catching SIGIO. + + if (verbose) { + printf("calling ResetSIGIO\n"); + } + + ResetSIGIO(); + + if (verbose) { + printf("raising SIGIO\n"); + } + + if (raise(SIGIO) < 0) { + die("raise"); + } + + if (verbose) { + printf("calling SawSIGIO\n"); + } + + if (SawSIGIO()) { + fprintf(stderr, "Go handler saw SIGIO after Reset\n"); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("waiting for sigioSeen\n"); + } + + // Wait until the signal has been delivered. + i = 0; + while (!sigioSeen) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for signal\n"); + exit(EXIT_FAILURE); + } + } + + printf("PASS\n"); + return 0; +} diff --git a/misc/cgo/testcarchive/testdata/main4.c b/misc/cgo/testcarchive/testdata/main4.c new file mode 100644 index 0000000..04f7740 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main4.c @@ -0,0 +1,204 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test a C thread that calls sigaltstack and then calls Go code. + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sched.h> +#include <pthread.h> + +#include "libgo4.h" + +#ifdef _AIX +// On AIX, CSIGSTKSZ is too small to handle Go sighandler. +#define CSIGSTKSZ 0x4000 +#else +#define CSIGSTKSZ SIGSTKSZ +#endif + +static void die(const char* msg) { + perror(msg); + exit(EXIT_FAILURE); +} + +static int ok = 1; + +static void ioHandler(int signo, siginfo_t* info, void* ctxt) { +} + +// Set up the SIGIO signal handler in a high priority constructor, so +// that it is installed before the Go code starts. + +static void init(void) __attribute__ ((constructor (200))); + +static void init() { + struct sigaction sa; + + memset(&sa, 0, sizeof sa); + sa.sa_sigaction = ioHandler; + if (sigemptyset(&sa.sa_mask) < 0) { + die("sigemptyset"); + } + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + if (sigaction(SIGIO, &sa, NULL) < 0) { + die("sigaction"); + } +} + +// Test raising SIGIO on a C thread with an alternate signal stack +// when there is a Go signal handler for SIGIO. +static void* thread1(void* arg __attribute__ ((unused))) { + stack_t ss; + int i; + stack_t nss; + struct timespec ts; + + // Set up an alternate signal stack for this thread. + memset(&ss, 0, sizeof ss); + ss.ss_sp = malloc(CSIGSTKSZ); + if (ss.ss_sp == NULL) { + die("malloc"); + } + ss.ss_flags = 0; + ss.ss_size = CSIGSTKSZ; + if (sigaltstack(&ss, NULL) < 0) { + die("sigaltstack"); + } + + // Send ourselves a SIGIO. This will be caught by the Go + // signal handler which should forward to the C signal + // handler. + i = pthread_kill(pthread_self(), SIGIO); + if (i != 0) { + fprintf(stderr, "pthread_kill: %s\n", strerror(i)); + exit(EXIT_FAILURE); + } + + // Wait until the signal has been delivered. + i = 0; + while (SIGIOCount() == 0) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for signal\n"); + exit(EXIT_FAILURE); + } + } + + // We should still be on the same signal stack. + if (sigaltstack(NULL, &nss) < 0) { + die("sigaltstack check"); + } + if ((nss.ss_flags & SS_DISABLE) != 0) { + fprintf(stderr, "sigaltstack disabled on return from Go\n"); + ok = 0; + } else if (nss.ss_sp != ss.ss_sp) { + fprintf(stderr, "sigaltstack changed on return from Go\n"); + ok = 0; + } + + return NULL; +} + +// Test calling a Go function to raise SIGIO on a C thread with an +// alternate signal stack when there is a Go signal handler for SIGIO. +static void* thread2(void* arg __attribute__ ((unused))) { + stack_t ss; + int i; + int oldcount; + pthread_t tid; + struct timespec ts; + stack_t nss; + + // Set up an alternate signal stack for this thread. + memset(&ss, 0, sizeof ss); + ss.ss_sp = malloc(CSIGSTKSZ); + if (ss.ss_sp == NULL) { + die("malloc"); + } + ss.ss_flags = 0; + ss.ss_size = CSIGSTKSZ; + if (sigaltstack(&ss, NULL) < 0) { + die("sigaltstack"); + } + + oldcount = SIGIOCount(); + + // Call a Go function that will call a C function to send us a + // SIGIO. + tid = pthread_self(); + GoRaiseSIGIO(&tid); + + // Wait until the signal has been delivered. + i = 0; + while (SIGIOCount() == oldcount) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for signal\n"); + exit(EXIT_FAILURE); + } + } + + // We should still be on the same signal stack. + if (sigaltstack(NULL, &nss) < 0) { + die("sigaltstack check"); + } + if ((nss.ss_flags & SS_DISABLE) != 0) { + fprintf(stderr, "sigaltstack disabled on return from Go\n"); + ok = 0; + } else if (nss.ss_sp != ss.ss_sp) { + fprintf(stderr, "sigaltstack changed on return from Go\n"); + ok = 0; + } + + return NULL; +} + +int main(int argc, char **argv) { + pthread_t tid; + int i; + + // Tell the Go library to start looking for SIGIO. + GoCatchSIGIO(); + + i = pthread_create(&tid, NULL, thread1, NULL); + if (i != 0) { + fprintf(stderr, "pthread_create: %s\n", strerror(i)); + exit(EXIT_FAILURE); + } + + i = pthread_join(tid, NULL); + if (i != 0) { + fprintf(stderr, "pthread_join: %s\n", strerror(i)); + exit(EXIT_FAILURE); + } + + i = pthread_create(&tid, NULL, thread2, NULL); + if (i != 0) { + fprintf(stderr, "pthread_create: %s\n", strerror(i)); + exit(EXIT_FAILURE); + } + + i = pthread_join(tid, NULL); + if (i != 0) { + fprintf(stderr, "pthread_join: %s\n", strerror(i)); + exit(EXIT_FAILURE); + } + + if (!ok) { + exit(EXIT_FAILURE); + } + + printf("PASS\n"); + return 0; +} diff --git a/misc/cgo/testcarchive/testdata/main5.c b/misc/cgo/testcarchive/testdata/main5.c new file mode 100644 index 0000000..c64c246 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main5.c @@ -0,0 +1,105 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test for verifying that the Go runtime properly forwards +// signals when non-Go signals are raised. + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/select.h> + +#include "libgo2.h" + +int *nilp; + +int main(int argc, char** argv) { + int verbose; + int test; + + if (argc < 2) { + printf("Missing argument\n"); + return 1; + } + + test = atoi(argv[1]); + + verbose = (argc > 2); + + Noop(); + + switch (test) { + case 1: { + if (verbose) { + printf("attempting segfault\n"); + } + + *nilp = 0; + break; + } + + case 2: { + struct timeval tv; + + if (verbose) { + printf("attempting external signal test\n"); + } + + fprintf(stderr, "OK\n"); + fflush(stderr); + + // The program should be interrupted before + // this sleep finishes. We use select rather + // than sleep because in older versions of + // glibc the sleep function does some signal + // fiddling to handle SIGCHLD. If this + // program is fiddling signals just when the + // test program sends the signal, the signal + // may be delivered to a Go thread which will + // break this test. + tv.tv_sec = 60; + tv.tv_usec = 0; + select(0, NULL, NULL, NULL, &tv); + + break; + } + case 3: { + if (verbose) { + printf("attempting SIGPIPE\n"); + } + + int fd[2]; + if (pipe(fd) != 0) { + printf("pipe(2) failed\n"); + return 0; + } + // Close the reading end. + close(fd[0]); + // Expect that write(2) fails (EPIPE) + if (write(fd[1], "some data", 9) != -1) { + printf("write(2) unexpectedly succeeded\n"); + return 0; + } + printf("did not receive SIGPIPE\n"); + return 0; + } + case 4: { + fprintf(stderr, "OK\n"); + fflush(stderr); + + if (verbose) { + printf("calling Block\n"); + } + Block(); + } + default: + printf("Unknown test: %d\n", test); + return 0; + } + + printf("FAIL\n"); + return 0; +} diff --git a/misc/cgo/testcarchive/testdata/main6.c b/misc/cgo/testcarchive/testdata/main6.c new file mode 100644 index 0000000..2745eb9 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main6.c @@ -0,0 +1,34 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that using the Go profiler in a C program does not crash. + +#include <stddef.h> +#include <sys/time.h> + +#include "libgo6.h" + +int main(int argc, char **argv) { + struct timeval tvstart, tvnow; + int diff; + + gettimeofday(&tvstart, NULL); + + go_start_profile(); + + // Busy wait so we have something to profile. + // If we just sleep the profiling signal will never fire. + while (1) { + gettimeofday(&tvnow, NULL); + diff = (tvnow.tv_sec - tvstart.tv_sec) * 1000 * 1000 + (tvnow.tv_usec - tvstart.tv_usec); + + // Profile frequency is 100Hz so we should definitely + // get a signal in 50 milliseconds. + if (diff > 50 * 1000) + break; + } + + go_stop_profile(); + return 0; +} diff --git a/misc/cgo/testcarchive/testdata/main7.c b/misc/cgo/testcarchive/testdata/main7.c new file mode 100644 index 0000000..2c6d98d --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main7.c @@ -0,0 +1,18 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that lots of calls don't deadlock. + +#include <stdio.h> + +#include "libgo7.h" + +int main() { + int i; + + for (i = 0; i < 100000; i++) { + GoFunction7(); + } + return 0; +} diff --git a/misc/cgo/testcarchive/testdata/main8.c b/misc/cgo/testcarchive/testdata/main8.c new file mode 100644 index 0000000..95fb7a3 --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main8.c @@ -0,0 +1,16 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test preemption. + +#include <stdlib.h> + +#include "libgo8.h" + +int main() { + GoFunction8(); + + // That should have exited the program. + abort(); +} diff --git a/misc/cgo/testcarchive/testdata/main_unix.c b/misc/cgo/testcarchive/testdata/main_unix.c new file mode 100644 index 0000000..bd00f9d --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main_unix.c @@ -0,0 +1,59 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct sigaction sa; +struct sigaction osa; + +static void (*oldHandler)(int, siginfo_t*, void*); + +static void handler(int signo, siginfo_t* info, void* ctxt) { + if (oldHandler) { + oldHandler(signo, info, ctxt); + } +} + +int install_handler() { + // Install our own signal handler. + memset(&sa, 0, sizeof sa); + sa.sa_sigaction = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_ONSTACK | SA_SIGINFO; + memset(&osa, 0, sizeof osa); + sigemptyset(&osa.sa_mask); + if (sigaction(SIGSEGV, &sa, &osa) < 0) { + perror("sigaction"); + return 2; + } + if (osa.sa_handler == SIG_DFL) { + fprintf(stderr, "Go runtime did not install signal handler\n"); + return 2; + } + // gccgo does not set SA_ONSTACK for SIGSEGV. + if (getenv("GCCGO") == NULL && (osa.sa_flags&SA_ONSTACK) == 0) { + fprintf(stderr, "Go runtime did not install signal handler\n"); + return 2; + } + oldHandler = osa.sa_sigaction; + + return 0; +} + +int check_handler() { + if (sigaction(SIGSEGV, NULL, &sa) < 0) { + perror("sigaction check"); + return 2; + } + if (sa.sa_sigaction != handler) { + fprintf(stderr, "ERROR: wrong signal handler: %p != %p\n", sa.sa_sigaction, handler); + return 2; + } + return 0; +} + diff --git a/misc/cgo/testcarchive/testdata/main_windows.c b/misc/cgo/testcarchive/testdata/main_windows.c new file mode 100644 index 0000000..eded8af --- /dev/null +++ b/misc/cgo/testcarchive/testdata/main_windows.c @@ -0,0 +1,17 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + * Dummy implementations for Windows, because Windows doesn't + * support Unix-style signal handling. + */ + +int install_handler() { + return 0; +} + + +int check_handler() { + return 0; +} diff --git a/misc/cgo/testcarchive/testdata/p/p.go b/misc/cgo/testcarchive/testdata/p/p.go new file mode 100644 index 0000000..82b445c --- /dev/null +++ b/misc/cgo/testcarchive/testdata/p/p.go @@ -0,0 +1,10 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "C" + +//export FromPkg +func FromPkg() int32 { return 1024 } diff --git a/misc/cgo/testcshared/cshared_test.go b/misc/cgo/testcshared/cshared_test.go new file mode 100644 index 0000000..2b57249 --- /dev/null +++ b/misc/cgo/testcshared/cshared_test.go @@ -0,0 +1,914 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cshared_test + +import ( + "bufio" + "bytes" + "debug/elf" + "debug/pe" + "encoding/binary" + "flag" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "sync" + "testing" + "unicode" +) + +// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)). +var cc []string + +// ".exe" on Windows. +var exeSuffix string + +var GOOS, GOARCH, GOROOT string +var installdir, androiddir string +var libgoname string + +func TestMain(m *testing.M) { + os.Exit(testMain(m)) +} + +func testMain(m *testing.M) int { + log.SetFlags(log.Lshortfile) + flag.Parse() + if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { + fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n") + os.Exit(0) + } + if runtime.GOOS == "linux" { + if _, err := os.Stat("/etc/alpine-release"); err == nil { + fmt.Printf("SKIP - skipping failing test on alpine - go.dev/issue/19938\n") + os.Exit(0) + } + } + + GOOS = goEnv("GOOS") + GOARCH = goEnv("GOARCH") + GOROOT = goEnv("GOROOT") + + if _, err := os.Stat(GOROOT); os.IsNotExist(err) { + log.Fatalf("Unable able to find GOROOT at '%s'", GOROOT) + } + + androiddir = fmt.Sprintf("/data/local/tmp/testcshared-%d", os.Getpid()) + if runtime.GOOS != GOOS && GOOS == "android" { + args := append(adbCmd(), "exec-out", "mkdir", "-p", androiddir) + cmd := exec.Command(args[0], args[1:]...) + out, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("setupAndroid failed: %v\n%s\n", err, out) + } + defer cleanupAndroid() + } + + cc = []string{goEnv("CC")} + + out := goEnv("GOGCCFLAGS") + quote := '\000' + start := 0 + lastSpace := true + backslash := false + s := string(out) + for i, c := range s { + if quote == '\000' && unicode.IsSpace(c) { + if !lastSpace { + cc = append(cc, s[start:i]) + lastSpace = true + } + } else { + if lastSpace { + start = i + lastSpace = false + } + if quote == '\000' && !backslash && (c == '"' || c == '\'') { + quote = c + backslash = false + } else if !backslash && quote == c { + quote = '\000' + } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { + backslash = true + } else { + backslash = false + } + } + } + if !lastSpace { + cc = append(cc, s[start:]) + } + + switch GOOS { + case "darwin", "ios": + // For Darwin/ARM. + // TODO(crawshaw): can we do better? + cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...) + case "android": + cc = append(cc, "-pie") + } + libgodir := GOOS + "_" + GOARCH + switch GOOS { + case "darwin", "ios": + if GOARCH == "arm64" { + libgodir += "_shared" + } + case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos": + libgodir += "_shared" + } + cc = append(cc, "-I", filepath.Join("pkg", libgodir)) + + // Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc. + cc = cc[:len(cc):len(cc)] + + if GOOS == "windows" { + exeSuffix = ".exe" + } + + // Copy testdata into GOPATH/src/testcshared, along with a go.mod file + // declaring the same path. + + GOPATH, err := os.MkdirTemp("", "cshared_test") + if err != nil { + log.Panic(err) + } + defer os.RemoveAll(GOPATH) + os.Setenv("GOPATH", GOPATH) + + modRoot := filepath.Join(GOPATH, "src", "testcshared") + if err := overlayDir(modRoot, "testdata"); err != nil { + log.Panic(err) + } + if err := os.Chdir(modRoot); err != nil { + log.Panic(err) + } + os.Setenv("PWD", modRoot) + if err := os.WriteFile("go.mod", []byte("module testcshared\n"), 0666); err != nil { + log.Panic(err) + } + + defer func() { + if installdir != "" { + err := os.RemoveAll(installdir) + if err != nil { + log.Panic(err) + } + } + }() + + return m.Run() +} + +func goEnv(key string) string { + out, err := exec.Command("go", "env", key).Output() + if err != nil { + log.Printf("go env %s failed:\n%s", key, err) + log.Panicf("%s", err.(*exec.ExitError).Stderr) + } + return strings.TrimSpace(string(out)) +} + +func cmdToRun(name string) string { + return "./" + name + exeSuffix +} + +func adbCmd() []string { + cmd := []string{"adb"} + if flags := os.Getenv("GOANDROID_ADB_FLAGS"); flags != "" { + cmd = append(cmd, strings.Split(flags, " ")...) + } + return cmd +} + +func adbPush(t *testing.T, filename string) { + if runtime.GOOS == GOOS || GOOS != "android" { + return + } + args := append(adbCmd(), "push", filename, fmt.Sprintf("%s/%s", androiddir, filename)) + cmd := exec.Command(args[0], args[1:]...) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("adb command failed: %v\n%s\n", err, out) + } +} + +func adbRun(t *testing.T, env []string, adbargs ...string) string { + if GOOS != "android" { + t.Fatalf("trying to run adb command when operating system is not android.") + } + args := append(adbCmd(), "exec-out") + // Propagate LD_LIBRARY_PATH to the adb shell invocation. + for _, e := range env { + if strings.Contains(e, "LD_LIBRARY_PATH=") { + adbargs = append([]string{e}, adbargs...) + break + } + } + shellcmd := fmt.Sprintf("cd %s; %s", androiddir, strings.Join(adbargs, " ")) + args = append(args, shellcmd) + cmd := exec.Command(args[0], args[1:]...) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("adb command failed: %v\n%s\n", err, out) + } + return strings.Replace(string(out), "\r", "", -1) +} + +func run(t *testing.T, extraEnv []string, args ...string) string { + t.Helper() + cmd := exec.Command(args[0], args[1:]...) + if len(extraEnv) > 0 { + cmd.Env = append(os.Environ(), extraEnv...) + } + + if GOOS != "windows" { + // TestUnexportedSymbols relies on file descriptor 30 + // being closed when the program starts, so enforce + // that in all cases. (The first three descriptors are + // stdin/stdout/stderr, so we just need to make sure + // that cmd.ExtraFiles[27] exists and is nil.) + cmd.ExtraFiles = make([]*os.File, 28) + } + + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out) + } else { + t.Logf("run: %v", args) + } + return string(out) +} + +func runExe(t *testing.T, extraEnv []string, args ...string) string { + t.Helper() + if runtime.GOOS != GOOS && GOOS == "android" { + return adbRun(t, append(os.Environ(), extraEnv...), args...) + } + return run(t, extraEnv, args...) +} + +func runCC(t *testing.T, args ...string) string { + t.Helper() + // This function is run in parallel, so append to a copy of cc + // rather than cc itself. + return run(t, nil, append(append([]string(nil), cc...), args...)...) +} + +func createHeaders() error { + // The 'cgo' command generates a number of additional artifacts, + // but we're only interested in the header. + // Shunt the rest of the outputs to a temporary directory. + objDir, err := os.MkdirTemp("", "testcshared_obj") + if err != nil { + return err + } + defer os.RemoveAll(objDir) + + // Generate a C header file for p, which is a non-main dependency + // of main package libgo. + // + // TODO(golang.org/issue/35715): This should be simpler. + args := []string{"go", "tool", "cgo", + "-objdir", objDir, + "-exportheader", "p.h", + filepath.Join(".", "p", "p.go")} + cmd := exec.Command(args[0], args[1:]...) + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out) + } + + // Generate a C header file for libgo itself. + installdir, err = os.MkdirTemp("", "testcshared") + if err != nil { + return err + } + libgoname = "libgo.a" + + args = []string{"go", "build", "-buildmode=c-shared", "-o", filepath.Join(installdir, libgoname), "./libgo"} + cmd = exec.Command(args[0], args[1:]...) + out, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out) + } + + args = []string{"go", "build", "-buildmode=c-shared", + "-installsuffix", "testcshared", + "-o", libgoname, + filepath.Join(".", "libgo", "libgo.go")} + if GOOS == "windows" && strings.HasSuffix(args[6], ".a") { + args[6] = strings.TrimSuffix(args[6], ".a") + ".dll" + } + cmd = exec.Command(args[0], args[1:]...) + out, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out) + } + if GOOS == "windows" { + // We can't simply pass -Wl,--out-implib, because this relies on having imports from multiple packages, + // which results in the linkers output implib getting overwritten at each step. So instead build the + // import library the traditional way, using a def file. + err = os.WriteFile("libgo.def", + []byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n\t_cgo_dummy_export\n"), + 0644) + if err != nil { + return fmt.Errorf("unable to write def file: %v", err) + } + out, err = exec.Command(cc[0], append(cc[1:], "-print-prog-name=dlltool")...).CombinedOutput() + if err != nil { + return fmt.Errorf("unable to find dlltool path: %v\n%s\n", err, out) + } + dlltoolpath := strings.TrimSpace(string(out)) + if filepath.Ext(dlltoolpath) == "" { + // Some compilers report slash-separated paths without extensions + // instead of ordinary Windows paths. + // Try to find the canonical name for the path. + if lp, err := exec.LookPath(dlltoolpath); err == nil { + dlltoolpath = lp + } + } + + args := []string{dlltoolpath, "-D", args[6], "-l", libgoname, "-d", "libgo.def"} + + if filepath.Ext(dlltoolpath) == "" { + // This is an unfortunate workaround for + // https://github.com/mstorsjo/llvm-mingw/issues/205 in which + // we basically reimplement the contents of the dlltool.sh + // wrapper: https://git.io/JZFlU. + // TODO(thanm): remove this workaround once we can upgrade + // the compilers on the windows-arm64 builder. + dlltoolContents, err := os.ReadFile(args[0]) + if err != nil { + return fmt.Errorf("unable to read dlltool: %v\n", err) + } + if bytes.HasPrefix(dlltoolContents, []byte("#!/bin/sh")) && bytes.Contains(dlltoolContents, []byte("llvm-dlltool")) { + base, name := filepath.Split(args[0]) + args[0] = filepath.Join(base, "llvm-dlltool") + var machine string + switch prefix, _, _ := strings.Cut(name, "-"); prefix { + case "i686": + machine = "i386" + case "x86_64": + machine = "i386:x86-64" + case "armv7": + machine = "arm" + case "aarch64": + machine = "arm64" + } + if len(machine) > 0 { + args = append(args, "-m", machine) + } + } + } + + out, err = exec.Command(args[0], args[1:]...).CombinedOutput() + if err != nil { + return fmt.Errorf("unable to run dlltool to create import library: %v\n%s\n", err, out) + } + } + + if runtime.GOOS != GOOS && GOOS == "android" { + args = append(adbCmd(), "push", libgoname, fmt.Sprintf("%s/%s", androiddir, libgoname)) + cmd = exec.Command(args[0], args[1:]...) + out, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("adb command failed: %v\n%s\n", err, out) + } + } + + return nil +} + +var ( + headersOnce sync.Once + headersErr error +) + +func createHeadersOnce(t *testing.T) { + headersOnce.Do(func() { + headersErr = createHeaders() + }) + if headersErr != nil { + t.Helper() + t.Fatal(headersErr) + } +} + +func cleanupAndroid() { + if GOOS != "android" { + return + } + args := append(adbCmd(), "exec-out", "rm", "-rf", androiddir) + cmd := exec.Command(args[0], args[1:]...) + out, err := cmd.CombinedOutput() + if err != nil { + log.Panicf("cleanupAndroid failed: %v\n%s\n", err, out) + } +} + +// test0: exported symbols in shared lib are accessible. +func TestExportedSymbols(t *testing.T) { + t.Parallel() + + cmd := "testp0" + bin := cmdToRun(cmd) + + createHeadersOnce(t) + + runCC(t, "-I", installdir, "-o", cmd, "main0.c", libgoname) + adbPush(t, cmd) + + defer os.Remove(bin) + + out := runExe(t, []string{"LD_LIBRARY_PATH=."}, bin) + if strings.TrimSpace(out) != "PASS" { + t.Error(out) + } +} + +func checkNumberOfExportedFunctionsWindows(t *testing.T, exportAllSymbols bool) { + const prog = ` +package main + +import "C" + +//export GoFunc +func GoFunc() { + println(42) +} + +//export GoFunc2 +func GoFunc2() { + println(24) +} + +func main() { +} +` + + tmpdir := t.TempDir() + + srcfile := filepath.Join(tmpdir, "test.go") + objfile := filepath.Join(tmpdir, "test.dll") + if err := os.WriteFile(srcfile, []byte(prog), 0666); err != nil { + t.Fatal(err) + } + argv := []string{"build", "-buildmode=c-shared"} + if exportAllSymbols { + argv = append(argv, "-ldflags", "-extldflags=-Wl,--export-all-symbols") + } + argv = append(argv, "-o", objfile, srcfile) + out, err := exec.Command("go", argv...).CombinedOutput() + if err != nil { + t.Fatalf("build failure: %s\n%s\n", err, string(out)) + } + + f, err := pe.Open(objfile) + if err != nil { + t.Fatalf("pe.Open failed: %v", err) + } + defer f.Close() + section := f.Section(".edata") + if section == nil { + t.Skip(".edata section is not present") + } + + // TODO: deduplicate this struct from cmd/link/internal/ld/pe.go + type IMAGE_EXPORT_DIRECTORY struct { + _ [2]uint32 + _ [2]uint16 + _ [2]uint32 + NumberOfFunctions uint32 + NumberOfNames uint32 + _ [3]uint32 + } + var e IMAGE_EXPORT_DIRECTORY + if err := binary.Read(section.Open(), binary.LittleEndian, &e); err != nil { + t.Fatalf("binary.Read failed: %v", err) + } + + // Only the two exported functions and _cgo_dummy_export should be exported + expectedNumber := uint32(3) + + if exportAllSymbols { + if e.NumberOfFunctions <= expectedNumber { + t.Fatalf("missing exported functions: %v", e.NumberOfFunctions) + } + if e.NumberOfNames <= expectedNumber { + t.Fatalf("missing exported names: %v", e.NumberOfNames) + } + } else { + if e.NumberOfFunctions != expectedNumber { + t.Fatalf("got %d exported functions; want %d", e.NumberOfFunctions, expectedNumber) + } + if e.NumberOfNames != expectedNumber { + t.Fatalf("got %d exported names; want %d", e.NumberOfNames, expectedNumber) + } + } +} + +func TestNumberOfExportedFunctions(t *testing.T) { + if GOOS != "windows" { + t.Skip("skipping windows only test") + } + t.Parallel() + + t.Run("OnlyExported", func(t *testing.T) { + checkNumberOfExportedFunctionsWindows(t, false) + }) + t.Run("All", func(t *testing.T) { + checkNumberOfExportedFunctionsWindows(t, true) + }) +} + +// test1: shared library can be dynamically loaded and exported symbols are accessible. +func TestExportedSymbolsWithDynamicLoad(t *testing.T) { + t.Parallel() + + if GOOS == "windows" { + t.Logf("Skipping on %s", GOOS) + return + } + + cmd := "testp1" + bin := cmdToRun(cmd) + + createHeadersOnce(t) + + if GOOS != "freebsd" { + runCC(t, "-o", cmd, "main1.c", "-ldl") + } else { + runCC(t, "-o", cmd, "main1.c") + } + adbPush(t, cmd) + + defer os.Remove(bin) + + out := runExe(t, nil, bin, "./"+libgoname) + if strings.TrimSpace(out) != "PASS" { + t.Error(out) + } +} + +// test2: tests libgo2 which does not export any functions. +func TestUnexportedSymbols(t *testing.T) { + t.Parallel() + + if GOOS == "windows" { + t.Logf("Skipping on %s", GOOS) + return + } + + cmd := "testp2" + bin := cmdToRun(cmd) + libname := "libgo2.a" + + run(t, + nil, + "go", "build", + "-buildmode=c-shared", + "-installsuffix", "testcshared", + "-o", libname, "./libgo2", + ) + adbPush(t, libname) + + linkFlags := "-Wl,--no-as-needed" + if GOOS == "darwin" || GOOS == "ios" { + linkFlags = "" + } + + runCC(t, "-o", cmd, "main2.c", linkFlags, libname) + adbPush(t, cmd) + + defer os.Remove(libname) + defer os.Remove(bin) + + out := runExe(t, []string{"LD_LIBRARY_PATH=."}, bin) + + if strings.TrimSpace(out) != "PASS" { + t.Error(out) + } +} + +// test3: tests main.main is exported on android. +func TestMainExportedOnAndroid(t *testing.T) { + t.Parallel() + + switch GOOS { + case "android": + break + default: + t.Logf("Skipping on %s", GOOS) + return + } + + cmd := "testp3" + bin := cmdToRun(cmd) + + createHeadersOnce(t) + + runCC(t, "-o", cmd, "main3.c", "-ldl") + adbPush(t, cmd) + + defer os.Remove(bin) + + out := runExe(t, nil, bin, "./"+libgoname) + if strings.TrimSpace(out) != "PASS" { + t.Error(out) + } +} + +func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) { + libname := pkgname + ".a" + run(t, + nil, + "go", "build", + "-buildmode=c-shared", + "-installsuffix", "testcshared", + "-o", libname, pkgname, + ) + adbPush(t, libname) + if GOOS != "freebsd" { + runCC(t, "-pthread", "-o", cmd, cfile, "-ldl") + } else { + runCC(t, "-pthread", "-o", cmd, cfile) + } + adbPush(t, cmd) + + bin := cmdToRun(cmd) + + defer os.Remove(libname) + defer os.Remove(bin) + defer os.Remove(pkgname + ".h") + + out := runExe(t, nil, bin, "./"+libname) + if strings.TrimSpace(out) != "PASS" { + t.Error(run(t, nil, bin, libname, "verbose")) + } +} + +// test4: test signal handlers +func TestSignalHandlers(t *testing.T) { + t.Parallel() + if GOOS == "windows" { + t.Logf("Skipping on %s", GOOS) + return + } + testSignalHandlers(t, "./libgo4", "main4.c", "testp4") +} + +// test5: test signal handlers with os/signal.Notify +func TestSignalHandlersWithNotify(t *testing.T) { + t.Parallel() + if GOOS == "windows" { + t.Logf("Skipping on %s", GOOS) + return + } + testSignalHandlers(t, "./libgo5", "main5.c", "testp5") +} + +func TestPIE(t *testing.T) { + t.Parallel() + + switch GOOS { + case "linux", "android": + break + default: + t.Logf("Skipping on %s", GOOS) + return + } + + createHeadersOnce(t) + + f, err := elf.Open(libgoname) + if err != nil { + t.Fatalf("elf.Open failed: %v", err) + } + defer f.Close() + + ds := f.SectionByType(elf.SHT_DYNAMIC) + if ds == nil { + t.Fatalf("no SHT_DYNAMIC section") + } + d, err := ds.Data() + if err != nil { + t.Fatalf("can't read SHT_DYNAMIC contents: %v", err) + } + for len(d) > 0 { + var tag elf.DynTag + switch f.Class { + case elf.ELFCLASS32: + tag = elf.DynTag(f.ByteOrder.Uint32(d[:4])) + d = d[8:] + case elf.ELFCLASS64: + tag = elf.DynTag(f.ByteOrder.Uint64(d[:8])) + d = d[16:] + } + if tag == elf.DT_TEXTREL { + t.Fatalf("%s has DT_TEXTREL flag", libgoname) + } + } +} + +// Test that installing a second time recreates the header file. +func TestCachedInstall(t *testing.T) { + tmpdir, err := os.MkdirTemp("", "cshared") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "go.mod"), "go.mod") + copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "libgo", "libgo.go"), filepath.Join("libgo", "libgo.go")) + copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "p", "p.go"), filepath.Join("p", "p.go")) + + buildcmd := []string{"go", "install", "-x", "-buildmode=c-shared", "-installsuffix", "testcshared", "./libgo"} + + cmd := exec.Command(buildcmd[0], buildcmd[1:]...) + cmd.Dir = filepath.Join(tmpdir, "src", "testcshared") + env := append(cmd.Environ(), + "GOPATH="+tmpdir, + "GOBIN="+filepath.Join(tmpdir, "bin"), + "GO111MODULE=off", // 'go install' only works in GOPATH mode + ) + cmd.Env = env + t.Log(buildcmd) + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + if err != nil { + t.Fatal(err) + } + + var libgoh, ph string + + walker := func(path string, info os.FileInfo, err error) error { + if err != nil { + t.Fatal(err) + } + var ps *string + switch filepath.Base(path) { + case "libgo.h": + ps = &libgoh + case "p.h": + ps = &ph + } + if ps != nil { + if *ps != "" { + t.Fatalf("%s found again", *ps) + } + *ps = path + } + return nil + } + + if err := filepath.Walk(tmpdir, walker); err != nil { + t.Fatal(err) + } + + if libgoh == "" { + t.Fatal("libgo.h not installed") + } + + if err := os.Remove(libgoh); err != nil { + t.Fatal(err) + } + + cmd = exec.Command(buildcmd[0], buildcmd[1:]...) + cmd.Dir = filepath.Join(tmpdir, "src", "testcshared") + cmd.Env = env + t.Log(buildcmd) + out, err = cmd.CombinedOutput() + t.Logf("%s", out) + if err != nil { + t.Fatal(err) + } + + if _, err := os.Stat(libgoh); err != nil { + t.Errorf("libgo.h not installed in second run: %v", err) + } +} + +// copyFile copies src to dst. +func copyFile(t *testing.T, dst, src string) { + t.Helper() + data, err := os.ReadFile(src) + if err != nil { + t.Fatal(err) + } + if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(dst, data, 0666); err != nil { + t.Fatal(err) + } +} + +func TestGo2C2Go(t *testing.T) { + switch GOOS { + case "darwin", "ios", "windows": + // Non-ELF shared libraries don't support the multiple + // copies of the runtime package implied by this test. + t.Skipf("linking c-shared into Go programs not supported on %s; issue 29061, 49457", GOOS) + case "android": + t.Skip("test fails on android; issue 29087") + } + + t.Parallel() + + tmpdir, err := os.MkdirTemp("", "cshared-TestGo2C2Go") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + lib := filepath.Join(tmpdir, "libtestgo2c2go.a") + var env []string + if GOOS == "windows" && strings.HasSuffix(lib, ".a") { + env = append(env, "CGO_LDFLAGS=-Wl,--out-implib,"+lib, "CGO_LDFLAGS_ALLOW=.*") + lib = strings.TrimSuffix(lib, ".a") + ".dll" + } + run(t, env, "go", "build", "-buildmode=c-shared", "-o", lib, "./go2c2go/go") + + cgoCflags := os.Getenv("CGO_CFLAGS") + if cgoCflags != "" { + cgoCflags += " " + } + cgoCflags += "-I" + tmpdir + + cgoLdflags := os.Getenv("CGO_LDFLAGS") + if cgoLdflags != "" { + cgoLdflags += " " + } + cgoLdflags += "-L" + tmpdir + " -ltestgo2c2go" + + goenv := []string{"CGO_CFLAGS=" + cgoCflags, "CGO_LDFLAGS=" + cgoLdflags} + + ldLibPath := os.Getenv("LD_LIBRARY_PATH") + if ldLibPath != "" { + ldLibPath += ":" + } + ldLibPath += tmpdir + + runenv := []string{"LD_LIBRARY_PATH=" + ldLibPath} + + bin := filepath.Join(tmpdir, "m1") + exeSuffix + run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m1") + runExe(t, runenv, bin) + + bin = filepath.Join(tmpdir, "m2") + exeSuffix + run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m2") + runExe(t, runenv, bin) +} + +func TestIssue36233(t *testing.T) { + t.Parallel() + + // Test that the export header uses GoComplex64 and GoComplex128 + // for complex types. + + tmpdir, err := os.MkdirTemp("", "cshared-TestIssue36233") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + const exportHeader = "issue36233.h" + + run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue36233/issue36233.go") + data, err := os.ReadFile(exportHeader) + if err != nil { + t.Fatal(err) + } + + funcs := []struct{ name, signature string }{ + {"exportComplex64", "GoComplex64 exportComplex64(GoComplex64 v)"}, + {"exportComplex128", "GoComplex128 exportComplex128(GoComplex128 v)"}, + {"exportComplexfloat", "GoComplex64 exportComplexfloat(GoComplex64 v)"}, + {"exportComplexdouble", "GoComplex128 exportComplexdouble(GoComplex128 v)"}, + } + + scanner := bufio.NewScanner(bytes.NewReader(data)) + var found int + for scanner.Scan() { + b := scanner.Bytes() + for _, fn := range funcs { + if bytes.Contains(b, []byte(fn.name)) { + found++ + if !bytes.Contains(b, []byte(fn.signature)) { + t.Errorf("function signature mismatch; got %q, want %q", b, fn.signature) + } + } + } + } + if err = scanner.Err(); err != nil { + t.Errorf("scanner encountered error: %v", err) + } + if found != len(funcs) { + t.Error("missing functions") + } +} diff --git a/misc/cgo/testcshared/overlaydir_test.go b/misc/cgo/testcshared/overlaydir_test.go new file mode 100644 index 0000000..85d6b44 --- /dev/null +++ b/misc/cgo/testcshared/overlaydir_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cshared_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/testcshared/testdata/go2c2go/go/shlib.go b/misc/cgo/testcshared/testdata/go2c2go/go/shlib.go new file mode 100644 index 0000000..76a5323 --- /dev/null +++ b/misc/cgo/testcshared/testdata/go2c2go/go/shlib.go @@ -0,0 +1,12 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "C" + +//export GoFunc +func GoFunc() int { return 1 } + +func main() {} diff --git a/misc/cgo/testcshared/testdata/go2c2go/m1/c.c b/misc/cgo/testcshared/testdata/go2c2go/m1/c.c new file mode 100644 index 0000000..0e8fac4 --- /dev/null +++ b/misc/cgo/testcshared/testdata/go2c2go/m1/c.c @@ -0,0 +1,9 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "libtestgo2c2go.h" + +int CFunc(void) { + return (GoFunc() << 8) + 2; +} diff --git a/misc/cgo/testcshared/testdata/go2c2go/m1/main.go b/misc/cgo/testcshared/testdata/go2c2go/m1/main.go new file mode 100644 index 0000000..17ba1eb --- /dev/null +++ b/misc/cgo/testcshared/testdata/go2c2go/m1/main.go @@ -0,0 +1,22 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// extern int CFunc(void); +import "C" + +import ( + "fmt" + "os" +) + +func main() { + got := C.CFunc() + const want = (1 << 8) | 2 + if got != want { + fmt.Printf("got %#x, want %#x\n", got, want) + os.Exit(1) + } +} diff --git a/misc/cgo/testcshared/testdata/go2c2go/m2/main.go b/misc/cgo/testcshared/testdata/go2c2go/m2/main.go new file mode 100644 index 0000000..91bf308 --- /dev/null +++ b/misc/cgo/testcshared/testdata/go2c2go/m2/main.go @@ -0,0 +1,22 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// #include "libtestgo2c2go.h" +import "C" + +import ( + "fmt" + "os" +) + +func main() { + got := C.GoFunc() + const want = 1 + if got != want { + fmt.Printf("got %#x, want %#x\n", got, want) + os.Exit(1) + } +} diff --git a/misc/cgo/testcshared/testdata/issue36233/issue36233.go b/misc/cgo/testcshared/testdata/issue36233/issue36233.go new file mode 100644 index 0000000..d0d1e5d --- /dev/null +++ b/misc/cgo/testcshared/testdata/issue36233/issue36233.go @@ -0,0 +1,29 @@ +// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package main
+
+// #include <complex.h>
+import "C"
+
+//export exportComplex64
+func exportComplex64(v complex64) complex64 {
+ return v
+}
+
+//export exportComplex128
+func exportComplex128(v complex128) complex128 {
+ return v
+}
+
+//export exportComplexfloat
+func exportComplexfloat(v C.complexfloat) C.complexfloat {
+ return v
+}
+
+//export exportComplexdouble
+func exportComplexdouble(v C.complexdouble) C.complexdouble {
+ return v
+}
+
+func main() {}
diff --git a/misc/cgo/testcshared/testdata/libgo/libgo.go b/misc/cgo/testcshared/testdata/libgo/libgo.go new file mode 100644 index 0000000..0634417 --- /dev/null +++ b/misc/cgo/testcshared/testdata/libgo/libgo.go @@ -0,0 +1,46 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "syscall" + _ "testcshared/p" + "time" +) + +import "C" + +var initCh = make(chan int, 1) +var ranMain bool + +func init() { + // emulate an exceedingly slow package initialization function + time.Sleep(100 * time.Millisecond) + initCh <- 42 +} + +func main() { + ranMain = true +} + +//export DidInitRun +func DidInitRun() bool { + select { + case x := <-initCh: + if x != 42 { + // Just in case initCh was not correctly made. + println("want init value of 42, got: ", x) + syscall.Exit(2) + } + return true + default: + return false + } +} + +//export DidMainRun +func DidMainRun() bool { + return ranMain +} diff --git a/misc/cgo/testcshared/testdata/libgo2/dup2.go b/misc/cgo/testcshared/testdata/libgo2/dup2.go new file mode 100644 index 0000000..d343aa5 --- /dev/null +++ b/misc/cgo/testcshared/testdata/libgo2/dup2.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux,!arm64,!riscv64 netbsd openbsd + +package main + +import "syscall" + +func dup2(oldfd, newfd int) error { + return syscall.Dup2(oldfd, newfd) +} diff --git a/misc/cgo/testcshared/testdata/libgo2/dup3.go b/misc/cgo/testcshared/testdata/libgo2/dup3.go new file mode 100644 index 0000000..459f0dc --- /dev/null +++ b/misc/cgo/testcshared/testdata/libgo2/dup3.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,arm64 linux,riscv64 + +package main + +import "syscall" + +func dup2(oldfd, newfd int) error { + return syscall.Dup3(oldfd, newfd, 0) +} diff --git a/misc/cgo/testcshared/testdata/libgo2/libgo2.go b/misc/cgo/testcshared/testdata/libgo2/libgo2.go new file mode 100644 index 0000000..e57c93b --- /dev/null +++ b/misc/cgo/testcshared/testdata/libgo2/libgo2.go @@ -0,0 +1,52 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package main + +// Test a shared library created by -buildmode=c-shared that does not +// export anything. + +import ( + "fmt" + "os" + "syscall" +) + +// To test this we want to communicate between the main program and +// the shared library without using any exported symbols. The init +// function creates a pipe and Dups the read end to a known number +// that the C code can also use. + +const ( + fd = 30 +) + +func init() { + var p [2]int + if e := syscall.Pipe(p[0:]); e != nil { + fmt.Fprintf(os.Stderr, "pipe: %v\n", e) + os.Exit(2) + } + + if e := dup2(p[0], fd); e != nil { + fmt.Fprintf(os.Stderr, "dup2: %v\n", e) + os.Exit(2) + } + + const str = "PASS" + if n, e := syscall.Write(p[1], []byte(str)); e != nil || n != len(str) { + fmt.Fprintf(os.Stderr, "write: %d %v\n", n, e) + os.Exit(2) + } + + if e := syscall.Close(p[1]); e != nil { + fmt.Fprintf(os.Stderr, "close: %v\n", e) + os.Exit(2) + } +} + +func main() { +} diff --git a/misc/cgo/testcshared/testdata/libgo4/libgo4.go b/misc/cgo/testcshared/testdata/libgo4/libgo4.go new file mode 100644 index 0000000..ab40b75 --- /dev/null +++ b/misc/cgo/testcshared/testdata/libgo4/libgo4.go @@ -0,0 +1,45 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "C" + +import ( + "fmt" + "os" + "runtime" +) + +// RunGoroutines starts some goroutines that don't do anything. +// The idea is to get some threads going, so that a signal will be delivered +// to a thread started by Go. +//export RunGoroutines +func RunGoroutines() { + for i := 0; i < 4; i++ { + go func() { + runtime.LockOSThread() + select {} + }() + } +} + +var P *byte + +// TestSEGV makes sure that an invalid address turns into a run-time Go panic. +//export TestSEGV +func TestSEGV() { + defer func() { + if recover() == nil { + fmt.Fprintln(os.Stderr, "no panic from segv") + os.Exit(1) + } + }() + *P = 0 + fmt.Fprintln(os.Stderr, "continued after segv") + os.Exit(1) +} + +func main() { +} diff --git a/misc/cgo/testcshared/testdata/libgo5/libgo5.go b/misc/cgo/testcshared/testdata/libgo5/libgo5.go new file mode 100644 index 0000000..94e5d21 --- /dev/null +++ b/misc/cgo/testcshared/testdata/libgo5/libgo5.go @@ -0,0 +1,44 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "C" + +import ( + "os" + "os/signal" + "syscall" + "time" +) + +// The channel used to read SIGIO signals. +var sigioChan chan os.Signal + +// CatchSIGIO starts catching SIGIO signals. +//export CatchSIGIO +func CatchSIGIO() { + sigioChan = make(chan os.Signal, 1) + signal.Notify(sigioChan, syscall.SIGIO) +} + +// ResetSIGIO stops catching SIGIO signals. +//export ResetSIGIO +func ResetSIGIO() { + signal.Reset(syscall.SIGIO) +} + +// SawSIGIO returns whether we saw a SIGIO within a brief pause. +//export SawSIGIO +func SawSIGIO() C.int { + select { + case <-sigioChan: + return 1 + case <-time.After(100 * time.Millisecond): + return 0 + } +} + +func main() { +} diff --git a/misc/cgo/testcshared/testdata/main0.c b/misc/cgo/testcshared/testdata/main0.c new file mode 100644 index 0000000..39ef7e3 --- /dev/null +++ b/misc/cgo/testcshared/testdata/main0.c @@ -0,0 +1,42 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <stdint.h> +#include <stdio.h> + +#include "p.h" +#include "libgo.h" + +// Tests libgo.so to export the following functions. +// int8_t DidInitRun(); +// int8_t DidMainRun(); +// int32_t FromPkg(); +// uint32_t Divu(uint32_t, uint32_t); +int main(void) { + int8_t ran_init = DidInitRun(); + if (!ran_init) { + fprintf(stderr, "ERROR: DidInitRun returned unexpected results: %d\n", + ran_init); + return 1; + } + int8_t ran_main = DidMainRun(); + if (ran_main) { + fprintf(stderr, "ERROR: DidMainRun returned unexpected results: %d\n", + ran_main); + return 1; + } + int32_t from_pkg = FromPkg(); + if (from_pkg != 1024) { + fprintf(stderr, "ERROR: FromPkg=%d, want %d\n", from_pkg, 1024); + return 1; + } + uint32_t divu = Divu(2264, 31); + if (divu != 73) { + fprintf(stderr, "ERROR: Divu(2264, 31)=%d, want %d\n", divu, 73); + return 1; + } + // test.bash looks for "PASS" to ensure this program has reached the end. + printf("PASS\n"); + return 0; +} diff --git a/misc/cgo/testcshared/testdata/main1.c b/misc/cgo/testcshared/testdata/main1.c new file mode 100644 index 0000000..420dd1e --- /dev/null +++ b/misc/cgo/testcshared/testdata/main1.c @@ -0,0 +1,69 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <stdint.h> +#include <stdio.h> +#include <dlfcn.h> + +int check_int8(void* handle, const char* fname, int8_t want) { + int8_t (*fn)(); + fn = (int8_t (*)())dlsym(handle, fname); + if (!fn) { + fprintf(stderr, "ERROR: missing %s: %s\n", fname, dlerror()); + return 1; + } + signed char ret = fn(); + if (ret != want) { + fprintf(stderr, "ERROR: %s=%d, want %d\n", fname, ret, want); + return 1; + } + return 0; +} + +int check_int32(void* handle, const char* fname, int32_t want) { + int32_t (*fn)(); + fn = (int32_t (*)())dlsym(handle, fname); + if (!fn) { + fprintf(stderr, "ERROR: missing %s: %s\n", fname, dlerror()); + return 1; + } + int32_t ret = fn(); + if (ret != want) { + fprintf(stderr, "ERROR: %s=%d, want %d\n", fname, ret, want); + return 1; + } + return 0; +} + +// Tests libgo.so to export the following functions. +// int8_t DidInitRun() // returns true +// int8_t DidMainRun() // returns true +// int32_t FromPkg() // returns 1024 +int main(int argc, char** argv) { + void* handle = dlopen(argv[1], RTLD_LAZY | RTLD_GLOBAL); + if (!handle) { + fprintf(stderr, "ERROR: failed to open the shared library: %s\n", + dlerror()); + return 2; + } + + int ret = 0; + ret = check_int8(handle, "DidInitRun", 1); + if (ret != 0) { + return ret; + } + + ret = check_int8(handle, "DidMainRun", 0); + if (ret != 0) { + return ret; + } + + ret = check_int32(handle, "FromPkg", 1024); + if (ret != 0) { + return ret; + } + // test.bash looks for "PASS" to ensure this program has reached the end. + printf("PASS\n"); + return 0; +} diff --git a/misc/cgo/testcshared/testdata/main2.c b/misc/cgo/testcshared/testdata/main2.c new file mode 100644 index 0000000..f89bcca --- /dev/null +++ b/misc/cgo/testcshared/testdata/main2.c @@ -0,0 +1,56 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#define fd (30) + +// Tests libgo2.so, which does not export any functions. +// Read a string from the file descriptor and print it. +int main(void) { + int i; + ssize_t n; + char buf[20]; + struct timespec ts; + + // The descriptor will be initialized in a thread, so we have to + // give a chance to get opened. + for (i = 0; i < 200; i++) { + n = read(fd, buf, sizeof buf); + if (n >= 0) + break; + if (errno != EBADF && errno != EINVAL) { + fprintf(stderr, "BUG: read: %s\n", strerror(errno)); + return 2; + } + + // An EBADF error means that the shared library has not opened the + // descriptor yet. + ts.tv_sec = 0; + ts.tv_nsec = 10000000; + nanosleep(&ts, NULL); + } + + if (n < 0) { + fprintf(stderr, "BUG: failed to read any data from pipe\n"); + return 2; + } + + if (n == 0) { + fprintf(stderr, "BUG: unexpected EOF\n"); + return 2; + } + + if (n == sizeof buf) { + n--; + } + buf[n] = '\0'; + printf("%s\n", buf); + return 0; +} diff --git a/misc/cgo/testcshared/testdata/main3.c b/misc/cgo/testcshared/testdata/main3.c new file mode 100644 index 0000000..49cc055 --- /dev/null +++ b/misc/cgo/testcshared/testdata/main3.c @@ -0,0 +1,29 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <stdint.h> +#include <stdio.h> +#include <dlfcn.h> + +// Tests "main.main" is exported on android/arm, +// which golang.org/x/mobile/app depends on. +int main(int argc, char** argv) { + void* handle = dlopen(argv[1], RTLD_LAZY | RTLD_GLOBAL); + if (!handle) { + fprintf(stderr, "ERROR: failed to open the shared library: %s\n", + dlerror()); + return 2; + } + + uintptr_t main_fn = (uintptr_t)dlsym(handle, "main.main"); + if (!main_fn) { + fprintf(stderr, "ERROR: missing main.main: %s\n", dlerror()); + return 2; + } + + // TODO(hyangah): check that main.main can run. + + printf("PASS\n"); + return 0; +} diff --git a/misc/cgo/testcshared/testdata/main4.c b/misc/cgo/testcshared/testdata/main4.c new file mode 100644 index 0000000..355cdef --- /dev/null +++ b/misc/cgo/testcshared/testdata/main4.c @@ -0,0 +1,215 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that a signal handler that uses up stack space does not crash +// if the signal is delivered to a thread running a goroutine. +// This is a lot like misc/cgo/testcarchive/main2.c. + +#include <setjmp.h> +#include <signal.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#include <sched.h> +#include <time.h> +#include <dlfcn.h> + +static void die(const char* msg) { + perror(msg); + exit(EXIT_FAILURE); +} + +static volatile sig_atomic_t sigioSeen; + +// Use up some stack space. +static void recur(int i, char *p) { + char a[1024]; + + *p = '\0'; + if (i > 0) { + recur(i - 1, a); + } +} + +// Signal handler that uses up more stack space than a goroutine will have. +static void ioHandler(int signo, siginfo_t* info, void* ctxt) { + char a[1024]; + + recur(4, a); + sigioSeen = 1; +} + +static jmp_buf jmp; +static char* nullPointer; + +// Signal handler for SIGSEGV on a C thread. +static void segvHandler(int signo, siginfo_t* info, void* ctxt) { + sigset_t mask; + int i; + + if (sigemptyset(&mask) < 0) { + die("sigemptyset"); + } + if (sigaddset(&mask, SIGSEGV) < 0) { + die("sigaddset"); + } + i = sigprocmask(SIG_UNBLOCK, &mask, NULL); + if (i != 0) { + fprintf(stderr, "sigprocmask: %s\n", strerror(i)); + exit(EXIT_FAILURE); + } + + // Don't try this at home. + longjmp(jmp, signo); + + // We should never get here. + abort(); +} + +int main(int argc, char** argv) { + int verbose; + struct sigaction sa; + void* handle; + void (*fn)(void); + sigset_t mask; + int i; + struct timespec ts; + + verbose = argc > 2; + setvbuf(stdout, NULL, _IONBF, 0); + + // Call setsid so that we can use kill(0, SIGIO) below. + // Don't check the return value so that this works both from + // a job control shell and from a shell script. + setsid(); + + if (verbose) { + printf("calling sigaction\n"); + } + + memset(&sa, 0, sizeof sa); + sa.sa_sigaction = ioHandler; + if (sigemptyset(&sa.sa_mask) < 0) { + die("sigemptyset"); + } + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGIO, &sa, NULL) < 0) { + die("sigaction"); + } + + sa.sa_sigaction = segvHandler; + if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) { + die("sigaction"); + } + + if (verbose) { + printf("calling dlopen\n"); + } + + handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL); + if (handle == NULL) { + fprintf(stderr, "%s\n", dlerror()); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling dlsym\n"); + } + + // Start some goroutines. + fn = (void(*)(void))dlsym(handle, "RunGoroutines"); + if (fn == NULL) { + fprintf(stderr, "%s\n", dlerror()); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling RunGoroutines\n"); + } + + fn(); + + // Block SIGIO in this thread to make it more likely that it + // will be delivered to a goroutine. + + if (verbose) { + printf("calling pthread_sigmask\n"); + } + + if (sigemptyset(&mask) < 0) { + die("sigemptyset"); + } + if (sigaddset(&mask, SIGIO) < 0) { + die("sigaddset"); + } + i = pthread_sigmask(SIG_BLOCK, &mask, NULL); + if (i != 0) { + fprintf(stderr, "pthread_sigmask: %s\n", strerror(i)); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling kill\n"); + } + + if (kill(0, SIGIO) < 0) { + die("kill"); + } + + if (verbose) { + printf("waiting for sigioSeen\n"); + } + + // Wait until the signal has been delivered. + i = 0; + while (!sigioSeen) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for signal\n"); + exit(EXIT_FAILURE); + } + } + + if (verbose) { + printf("calling setjmp\n"); + } + + // Test that a SIGSEGV on this thread is delivered to us. + if (setjmp(jmp) == 0) { + if (verbose) { + printf("triggering SIGSEGV\n"); + } + + *nullPointer = '\0'; + + fprintf(stderr, "continued after address error\n"); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling dlsym\n"); + } + + // Make sure that a SIGSEGV in Go causes a run-time panic. + fn = (void (*)(void))dlsym(handle, "TestSEGV"); + if (fn == NULL) { + fprintf(stderr, "%s\n", dlerror()); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling TestSEGV\n"); + } + + fn(); + + printf("PASS\n"); + return 0; +} diff --git a/misc/cgo/testcshared/testdata/main5.c b/misc/cgo/testcshared/testdata/main5.c new file mode 100644 index 0000000..1bc9910 --- /dev/null +++ b/misc/cgo/testcshared/testdata/main5.c @@ -0,0 +1,199 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that a signal handler works in non-Go code when using +// os/signal.Notify. +// This is a lot like misc/cgo/testcarchive/main3.c. + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sched.h> +#include <dlfcn.h> + +static void die(const char* msg) { + perror(msg); + exit(EXIT_FAILURE); +} + +static volatile sig_atomic_t sigioSeen; + +static void ioHandler(int signo, siginfo_t* info, void* ctxt) { + sigioSeen = 1; +} + +int main(int argc, char** argv) { + int verbose; + struct sigaction sa; + void* handle; + void (*fn1)(void); + int (*sawSIGIO)(void); + int i; + struct timespec ts; + + verbose = argc > 2; + setvbuf(stdout, NULL, _IONBF, 0); + + if (verbose) { + printf("calling sigaction\n"); + } + + memset(&sa, 0, sizeof sa); + sa.sa_sigaction = ioHandler; + if (sigemptyset(&sa.sa_mask) < 0) { + die("sigemptyset"); + } + sa.sa_flags = SA_SIGINFO; + if (sigaction(SIGIO, &sa, NULL) < 0) { + die("sigaction"); + } + + if (verbose) { + printf("calling dlopen\n"); + } + + handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL); + if (handle == NULL) { + fprintf(stderr, "%s\n", dlerror()); + exit(EXIT_FAILURE); + } + + // At this point there should not be a Go signal handler + // installed for SIGIO. + + if (verbose) { + printf("raising SIGIO\n"); + } + + if (raise(SIGIO) < 0) { + die("raise"); + } + + if (verbose) { + printf("waiting for sigioSeen\n"); + } + + // Wait until the signal has been delivered. + i = 0; + while (!sigioSeen) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for signal\n"); + exit(EXIT_FAILURE); + } + } + + sigioSeen = 0; + + // Tell the Go code to catch SIGIO. + + if (verbose) { + printf("calling dlsym\n"); + } + + fn1 = (void(*)(void))dlsym(handle, "CatchSIGIO"); + if (fn1 == NULL) { + fprintf(stderr, "%s\n", dlerror()); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling CatchSIGIO\n"); + } + + fn1(); + + if (verbose) { + printf("raising SIGIO\n"); + } + + if (raise(SIGIO) < 0) { + die("raise"); + } + + if (verbose) { + printf("calling dlsym\n"); + } + + // Check that the Go code saw SIGIO. + sawSIGIO = (int (*)(void))dlsym(handle, "SawSIGIO"); + if (sawSIGIO == NULL) { + fprintf(stderr, "%s\n", dlerror()); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling SawSIGIO\n"); + } + + if (!sawSIGIO()) { + fprintf(stderr, "Go handler did not see SIGIO\n"); + exit(EXIT_FAILURE); + } + + if (sigioSeen != 0) { + fprintf(stderr, "C handler saw SIGIO when only Go handler should have\n"); + exit(EXIT_FAILURE); + } + + // Tell the Go code to stop catching SIGIO. + + if (verbose) { + printf("calling dlsym\n"); + } + + fn1 = (void(*)(void))dlsym(handle, "ResetSIGIO"); + if (fn1 == NULL) { + fprintf(stderr, "%s\n", dlerror()); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("calling ResetSIGIO\n"); + } + + fn1(); + + if (verbose) { + printf("raising SIGIO\n"); + } + + if (raise(SIGIO) < 0) { + die("raise"); + } + + if (verbose) { + printf("calling SawSIGIO\n"); + } + + if (sawSIGIO()) { + fprintf(stderr, "Go handler saw SIGIO after Reset\n"); + exit(EXIT_FAILURE); + } + + if (verbose) { + printf("waiting for sigioSeen\n"); + } + + // Wait until the signal has been delivered. + i = 0; + while (!sigioSeen) { + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); + i++; + if (i > 5000) { + fprintf(stderr, "looping too long waiting for signal\n"); + exit(EXIT_FAILURE); + } + } + + printf("PASS\n"); + return 0; +} diff --git a/misc/cgo/testcshared/testdata/p/p.go b/misc/cgo/testcshared/testdata/p/p.go new file mode 100644 index 0000000..0f02cf3 --- /dev/null +++ b/misc/cgo/testcshared/testdata/p/p.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import "C" + +//export FromPkg +func FromPkg() int32 { return 1024 } + +//export Divu +func Divu(a, b uint32) uint32 { return a / b } diff --git a/misc/cgo/testgodefs/testdata/anonunion.go b/misc/cgo/testgodefs/testdata/anonunion.go new file mode 100644 index 0000000..18840f2 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/anonunion.go @@ -0,0 +1,26 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +// This file tests that when cgo -godefs sees a struct with a field +// that is an anonymous union, the first field in the union is +// promoted to become a field of the struct. See issue 6677 for +// background. + +/* +typedef struct { + union { + long l; + int c; + }; +} t; +*/ +import "C" + +// Input for cgo -godefs. + +type T C.t diff --git a/misc/cgo/testgodefs/testdata/bitfields.go b/misc/cgo/testgodefs/testdata/bitfields.go new file mode 100644 index 0000000..6a9724d --- /dev/null +++ b/misc/cgo/testgodefs/testdata/bitfields.go @@ -0,0 +1,31 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +// This file tests that we don't generate an incorrect field location +// for a bitfield that appears aligned. + +/* +struct bitfields { + unsigned int B1 : 5; + unsigned int B2 : 1; + unsigned int B3 : 1; + unsigned int B4 : 1; + unsigned int Short1 : 16; // misaligned on 8 bit boundary + unsigned int B5 : 1; + unsigned int B6 : 1; + unsigned int B7 : 1; + unsigned int B8 : 1; + unsigned int B9 : 1; + unsigned int B10 : 3; + unsigned int Short2 : 16; // alignment is OK + unsigned int Short3 : 16; // alignment is OK +}; +*/ +import "C" + +type bitfields C.struct_bitfields diff --git a/misc/cgo/testgodefs/testdata/fieldtypedef.go b/misc/cgo/testgodefs/testdata/fieldtypedef.go new file mode 100644 index 0000000..45c0bf8 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/fieldtypedef.go @@ -0,0 +1,18 @@ +// Copyright 2018 The Go Authors. All rights reserve d. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +/* +struct S1 { int f1; }; +struct S2 { struct S1 s1; }; +typedef struct S1 S1Type; +typedef struct S2 S2Type; +*/ +import "C" + +type S1 C.S1Type +type S2 C.S2Type diff --git a/misc/cgo/testgodefs/testdata/issue37479.go b/misc/cgo/testgodefs/testdata/issue37479.go new file mode 100644 index 0000000..a210eb5 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/issue37479.go @@ -0,0 +1,33 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +/* +typedef struct A A; + +typedef struct { + struct A *next; + struct A **prev; +} N; + +struct A +{ + N n; +}; + +typedef struct B +{ + A* a; +} B; +*/ +import "C" + +type N C.N + +type A C.A + +type B C.B diff --git a/misc/cgo/testgodefs/testdata/issue37621.go b/misc/cgo/testgodefs/testdata/issue37621.go new file mode 100644 index 0000000..d5ace3f --- /dev/null +++ b/misc/cgo/testgodefs/testdata/issue37621.go @@ -0,0 +1,23 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +/* +struct tt { + long long a; + long long b; +}; + +struct s { + struct tt ts[3]; +}; +*/ +import "C" + +type TT C.struct_tt + +type S C.struct_s diff --git a/misc/cgo/testgodefs/testdata/issue38649.go b/misc/cgo/testgodefs/testdata/issue38649.go new file mode 100644 index 0000000..6af74d6 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/issue38649.go @@ -0,0 +1,15 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +/* +struct Issue38649 { int x; }; +#define issue38649 struct Issue38649 +*/ +import "C" + +type issue38649 C.issue38649 diff --git a/misc/cgo/testgodefs/testdata/issue39534.go b/misc/cgo/testgodefs/testdata/issue39534.go new file mode 100644 index 0000000..9899ba1 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/issue39534.go @@ -0,0 +1,12 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +// enum { ENUMVAL = 0x1 }; +import "C" + +const ENUMVAL = C.ENUMVAL diff --git a/misc/cgo/testgodefs/testdata/issue48396.go b/misc/cgo/testgodefs/testdata/issue48396.go new file mode 100644 index 0000000..d4c1924 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/issue48396.go @@ -0,0 +1,18 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +/* +// from <linux/kcm.h> +struct issue48396 { + int fd; + int bpf_fd; +}; +*/ +import "C" + +type Issue48396 C.struct_issue48396 diff --git a/misc/cgo/testgodefs/testdata/issue8478.go b/misc/cgo/testgodefs/testdata/issue8478.go new file mode 100644 index 0000000..2321446 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/issue8478.go @@ -0,0 +1,20 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// +build ignore + +package main + +// Issue 8478. Test that void* is consistently mapped to *byte. + +/* +typedef struct { + void *p; + void **q; + void ***r; +} s; +*/ +import "C" + +type Issue8478 C.s diff --git a/misc/cgo/testgodefs/testdata/main.go b/misc/cgo/testgodefs/testdata/main.go new file mode 100644 index 0000000..5c670f3 --- /dev/null +++ b/misc/cgo/testgodefs/testdata/main.go @@ -0,0 +1,57 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "reflect" +) + +// Test that the struct field in anonunion.go was promoted. +var v1 T +var v2 = v1.L + +// Test that P, Q, and R all point to byte. +var v3 = Issue8478{P: (*byte)(nil), Q: (**byte)(nil), R: (***byte)(nil)} + +// Test that N, A and B are fully defined +var v4 = N{} +var v5 = A{} +var v6 = B{} + +// Test that S is fully defined +var v7 = S{} + +// Test that #define'd type is fully defined +var _ = issue38649{X: 0} + +// Test that prefixes do not cause duplicate field names. +var _ = Issue48396{Fd: 1, Bpf_fd: 2} + +func main() { + pass := true + + // The Go translation of bitfields should not have any of the + // bitfield types. The order in which bitfields are laid out + // in memory is implementation defined, so we can't easily + // know how a bitfield should correspond to a Go type, even if + // it appears to be aligned correctly. + bitfieldType := reflect.TypeOf(bitfields{}) + check := func(name string) { + _, ok := bitfieldType.FieldByName(name) + if ok { + fmt.Fprintf(os.Stderr, "found unexpected bitfields field %s\n", name) + pass = false + } + } + check("Short1") + check("Short2") + check("Short3") + + if !pass { + os.Exit(1) + } +} diff --git a/misc/cgo/testgodefs/testgodefs_test.go b/misc/cgo/testgodefs/testgodefs_test.go new file mode 100644 index 0000000..d03769e --- /dev/null +++ b/misc/cgo/testgodefs/testgodefs_test.go @@ -0,0 +1,112 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package testgodefs + +import ( + "bytes" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +// We are testing cgo -godefs, which translates Go files that use +// import "C" into Go files with Go definitions of types defined in the +// import "C" block. Add more tests here. +var filePrefixes = []string{ + "anonunion", + "bitfields", + "issue8478", + "fieldtypedef", + "issue37479", + "issue37621", + "issue38649", + "issue39534", + "issue48396", +} + +func TestGoDefs(t *testing.T) { + testdata, err := filepath.Abs("testdata") + if err != nil { + t.Fatal(err) + } + + gopath, err := os.MkdirTemp("", "testgodefs-gopath") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(gopath) + + dir := filepath.Join(gopath, "src", "testgodefs") + if err := os.MkdirAll(dir, 0755); err != nil { + t.Fatal(err) + } + + for _, fp := range filePrefixes { + cmd := exec.Command("go", "tool", "cgo", + "-godefs", + "-srcdir", testdata, + "-objdir", dir, + fp+".go") + cmd.Stderr = new(bytes.Buffer) + + out, err := cmd.Output() + if err != nil { + t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) + } + + fn := fp + "_defs.go" + if err := os.WriteFile(filepath.Join(dir, fn), out, 0644); err != nil { + t.Fatal(err) + } + + // Verify that command line arguments are not rewritten in the generated comment, + // see go.dev/issue/52063 + hasGeneratedByComment := false + for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") { + cgoExe := "cgo" + if runtime.GOOS == "windows" { + cgoExe = "cgo.exe" + } + if !strings.HasPrefix(line, "// "+cgoExe+" -godefs") { + continue + } + if want := "// " + cgoExe + " " + strings.Join(cmd.Args[3:], " "); line != want { + t.Errorf("%s: got generated comment %q, want %q", fn, line, want) + } + hasGeneratedByComment = true + break + } + + if !hasGeneratedByComment { + t.Errorf("%s: comment with generating cgo -godefs command not found", fn) + } + } + + main, err := os.ReadFile(filepath.Join("testdata", "main.go")) + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "main.go"), main, 0644); err != nil { + t.Fatal(err) + } + + if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module testgodefs\ngo 1.14\n"), 0644); err != nil { + t.Fatal(err) + } + + // Use 'go run' to build and run the resulting binary in a single step, + // instead of invoking 'go build' and the resulting binary separately, so that + // this test can pass on mobile builders, which do not copy artifacts back + // from remote invocations. + cmd := exec.Command("go", "run", ".") + cmd.Env = append(os.Environ(), "GOPATH="+gopath) + cmd.Dir = dir + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("%s [%s]: %v\n%s", strings.Join(cmd.Args, " "), dir, err, out) + } +} diff --git a/misc/cgo/testplugin/altpath/testdata/common/common.go b/misc/cgo/testplugin/altpath/testdata/common/common.go new file mode 100644 index 0000000..505ba02 --- /dev/null +++ b/misc/cgo/testplugin/altpath/testdata/common/common.go @@ -0,0 +1,11 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package common + +var X int + +func init() { + X = 4 +} diff --git a/misc/cgo/testplugin/altpath/testdata/plugin-mismatch/main.go b/misc/cgo/testplugin/altpath/testdata/plugin-mismatch/main.go new file mode 100644 index 0000000..bfb4ba4 --- /dev/null +++ b/misc/cgo/testplugin/altpath/testdata/plugin-mismatch/main.go @@ -0,0 +1,17 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// // No C code required. +import "C" + +// The common package imported here does not match the common package +// imported by plugin1. A program that attempts to load plugin1 and +// plugin-mismatch should produce an error. +import "testplugin/common" + +func ReadCommonX() int { + return common.X +} diff --git a/misc/cgo/testplugin/overlaydir_test.go b/misc/cgo/testplugin/overlaydir_test.go new file mode 100644 index 0000000..e2c32d8 --- /dev/null +++ b/misc/cgo/testplugin/overlaydir_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package plugin_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/testplugin/plugin_test.go b/misc/cgo/testplugin/plugin_test.go new file mode 100644 index 0000000..2856810 --- /dev/null +++ b/misc/cgo/testplugin/plugin_test.go @@ -0,0 +1,342 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package plugin_test + +import ( + "bytes" + "context" + "flag" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" +) + +var gcflags string = os.Getenv("GO_GCFLAGS") +var goroot string + +func TestMain(m *testing.M) { + flag.Parse() + if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { + fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n") + os.Exit(0) + } + log.SetFlags(log.Lshortfile) + os.Exit(testMain(m)) +} + +// tmpDir is used to cleanup logged commands -- s/tmpDir/$TMPDIR/ +var tmpDir string + +// prettyPrintf prints lines with tmpDir sanitized. +func prettyPrintf(format string, args ...interface{}) { + s := fmt.Sprintf(format, args...) + if tmpDir != "" { + s = strings.ReplaceAll(s, tmpDir, "$TMPDIR") + } + fmt.Print(s) +} + +func testMain(m *testing.M) int { + cwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + goroot = filepath.Join(cwd, "../../..") + + // Copy testdata into GOPATH/src/testplugin, along with a go.mod file + // declaring the same path. + + GOPATH, err := os.MkdirTemp("", "plugin_test") + if err != nil { + log.Panic(err) + } + defer os.RemoveAll(GOPATH) + tmpDir = GOPATH + + modRoot := filepath.Join(GOPATH, "src", "testplugin") + altRoot := filepath.Join(GOPATH, "alt", "src", "testplugin") + for srcRoot, dstRoot := range map[string]string{ + "testdata": modRoot, + filepath.Join("altpath", "testdata"): altRoot, + } { + if err := overlayDir(dstRoot, srcRoot); err != nil { + log.Panic(err) + } + prettyPrintf("mkdir -p %s\n", dstRoot) + prettyPrintf("rsync -a %s/ %s\n", srcRoot, dstRoot) + + if err := os.WriteFile(filepath.Join(dstRoot, "go.mod"), []byte("module testplugin\n"), 0666); err != nil { + log.Panic(err) + } + prettyPrintf("echo 'module testplugin' > %s/go.mod\n", dstRoot) + } + + os.Setenv("GOPATH", filepath.Join(GOPATH, "alt")) + if err := os.Chdir(altRoot); err != nil { + log.Panic(err) + } else { + prettyPrintf("cd %s\n", altRoot) + } + os.Setenv("PWD", altRoot) + goCmd(nil, "build", "-buildmode=plugin", "-o", filepath.Join(modRoot, "plugin-mismatch.so"), "./plugin-mismatch") + + os.Setenv("GOPATH", GOPATH) + if err := os.Chdir(modRoot); err != nil { + log.Panic(err) + } else { + prettyPrintf("cd %s\n", modRoot) + } + os.Setenv("PWD", modRoot) + + os.Setenv("LD_LIBRARY_PATH", modRoot) + + goCmd(nil, "build", "-buildmode=plugin", "./plugin1") + goCmd(nil, "build", "-buildmode=plugin", "./plugin2") + so, err := os.ReadFile("plugin2.so") + if err != nil { + log.Panic(err) + } + if err := os.WriteFile("plugin2-dup.so", so, 0444); err != nil { + log.Panic(err) + } + prettyPrintf("cp plugin2.so plugin2-dup.so\n") + + goCmd(nil, "build", "-buildmode=plugin", "-o=sub/plugin1.so", "./sub/plugin1") + goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed1.so", "./unnamed1/main.go") + goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed2.so", "./unnamed2/main.go") + goCmd(nil, "build", "-o", "host.exe", "./host") + + return m.Run() +} + +func goCmd(t *testing.T, op string, args ...string) { + if t != nil { + t.Helper() + } + run(t, filepath.Join(goroot, "bin", "go"), append([]string{op, "-gcflags", gcflags}, args...)...) +} + +// escape converts a string to something suitable for a shell command line. +func escape(s string) string { + s = strings.Replace(s, "\\", "\\\\", -1) + s = strings.Replace(s, "'", "\\'", -1) + // Conservative guess at characters that will force quoting + if s == "" || strings.ContainsAny(s, "\\ ;#*&$~?!|[]()<>{}`") { + s = "'" + s + "'" + } + return s +} + +// asCommandLine renders cmd as something that could be copy-and-pasted into a command line +func asCommandLine(cwd string, cmd *exec.Cmd) string { + s := "(" + if cmd.Dir != "" && cmd.Dir != cwd { + s += "cd" + escape(cmd.Dir) + ";" + } + for _, e := range cmd.Env { + if !strings.HasPrefix(e, "PATH=") && + !strings.HasPrefix(e, "HOME=") && + !strings.HasPrefix(e, "USER=") && + !strings.HasPrefix(e, "SHELL=") { + s += " " + s += escape(e) + } + } + // These EVs are relevant to this test. + for _, e := range os.Environ() { + if strings.HasPrefix(e, "PWD=") || + strings.HasPrefix(e, "GOPATH=") || + strings.HasPrefix(e, "LD_LIBRARY_PATH=") { + s += " " + s += escape(e) + } + } + for _, a := range cmd.Args { + s += " " + s += escape(a) + } + s += " )" + return s +} + +func run(t *testing.T, bin string, args ...string) string { + cmd := exec.Command(bin, args...) + cmdLine := asCommandLine(".", cmd) + prettyPrintf("%s\n", cmdLine) + cmd.Stderr = new(strings.Builder) + out, err := cmd.Output() + if err != nil { + if t == nil { + log.Panicf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) + } else { + t.Helper() + t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) + } + } + + return string(bytes.TrimSpace(out)) +} + +func TestDWARFSections(t *testing.T) { + // test that DWARF sections are emitted for plugins and programs importing "plugin" + goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse") + goCmd(t, "run", "./checkdwarf/main.go", "./host.exe", "main.main") +} + +func TestRunHost(t *testing.T) { + run(t, "./host.exe") +} + +func TestUniqueTypesAndItabs(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "./iface_a") + goCmd(t, "build", "-buildmode=plugin", "./iface_b") + goCmd(t, "build", "-o", "iface.exe", "./iface") + run(t, "./iface.exe") +} + +func TestIssue18676(t *testing.T) { + // make sure we don't add the same itab twice. + // The buggy code hangs forever, so use a timeout to check for that. + goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18676/plugin.go") + goCmd(t, "build", "-o", "issue18676.exe", "./issue18676/main.go") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + cmd := exec.CommandContext(ctx, "./issue18676.exe") + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out) + } +} + +func TestIssue19534(t *testing.T) { + // Test that we can load a plugin built in a path with non-alpha characters. + goCmd(t, "build", "-buildmode=plugin", "-gcflags=-p=issue.19534", "-ldflags=-pluginpath=issue.19534", "-o", "plugin.so", "./issue19534/plugin.go") + goCmd(t, "build", "-o", "issue19534.exe", "./issue19534/main.go") + run(t, "./issue19534.exe") +} + +func TestIssue18584(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18584/plugin.go") + goCmd(t, "build", "-o", "issue18584.exe", "./issue18584/main.go") + run(t, "./issue18584.exe") +} + +func TestIssue19418(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-ldflags=-X main.Val=linkstr", "-o", "plugin.so", "./issue19418/plugin.go") + goCmd(t, "build", "-o", "issue19418.exe", "./issue19418/main.go") + run(t, "./issue19418.exe") +} + +func TestIssue19529(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue19529/plugin.go") +} + +func TestIssue22175(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin1.so", "./issue22175/plugin1.go") + goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin2.so", "./issue22175/plugin2.go") + goCmd(t, "build", "-o", "issue22175.exe", "./issue22175/main.go") + run(t, "./issue22175.exe") +} + +func TestIssue22295(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "issue.22295.so", "./issue22295.pkg") + goCmd(t, "build", "-o", "issue22295.exe", "./issue22295.pkg/main.go") + run(t, "./issue22295.exe") +} + +func TestIssue24351(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "issue24351.so", "./issue24351/plugin.go") + goCmd(t, "build", "-o", "issue24351.exe", "./issue24351/main.go") + run(t, "./issue24351.exe") +} + +func TestIssue25756(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin") + goCmd(t, "build", "-o", "issue25756.exe", "./issue25756/main.go") + // Fails intermittently, but 20 runs should cause the failure + for n := 20; n > 0; n-- { + t.Run(fmt.Sprint(n), func(t *testing.T) { + t.Parallel() + run(t, "./issue25756.exe") + }) + } +} + +// Test with main using -buildmode=pie with plugin for issue #43228 +func TestIssue25756pie(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin") + goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go") + run(t, "./issue25756pie.exe") +} + +func TestMethod(t *testing.T) { + // Exported symbol's method must be live. + goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go") + goCmd(t, "build", "-o", "method.exe", "./method/main.go") + run(t, "./method.exe") +} + +func TestMethod2(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "method2.so", "./method2/plugin.go") + goCmd(t, "build", "-o", "method2.exe", "./method2/main.go") + run(t, "./method2.exe") +} + +func TestMethod3(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "method3.so", "./method3/plugin.go") + goCmd(t, "build", "-o", "method3.exe", "./method3/main.go") + run(t, "./method3.exe") +} + +func TestIssue44956(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p1.so", "./issue44956/plugin1.go") + goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p2.so", "./issue44956/plugin2.go") + goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go") + run(t, "./issue44956.exe") +} + +func TestIssue52937(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "issue52937.so", "./issue52937/main.go") +} + +func TestIssue53989(t *testing.T) { + goCmd(t, "build", "-buildmode=plugin", "-o", "issue53989.so", "./issue53989/plugin.go") + goCmd(t, "build", "-o", "issue53989.exe", "./issue53989/main.go") + run(t, "./issue53989.exe") +} + +func TestForkExec(t *testing.T) { + // Issue 38824: importing the plugin package causes it hang in forkExec on darwin. + + t.Parallel() + goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go") + + var cmd *exec.Cmd + done := make(chan int, 1) + + go func() { + for i := 0; i < 100; i++ { + cmd = exec.Command("./forkexec.exe", "1") + err := cmd.Run() + if err != nil { + t.Errorf("running command failed: %v", err) + break + } + } + done <- 1 + }() + select { + case <-done: + case <-time.After(5 * time.Minute): + cmd.Process.Kill() + t.Fatalf("subprocess hang") + } +} diff --git a/misc/cgo/testplugin/testdata/checkdwarf/main.go b/misc/cgo/testplugin/testdata/checkdwarf/main.go new file mode 100644 index 0000000..7886c83 --- /dev/null +++ b/misc/cgo/testplugin/testdata/checkdwarf/main.go @@ -0,0 +1,106 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Usage: +// +// checkdwarf <exe> <suffix> +// +// Opens <exe>, which must be an executable or a library and checks that +// there is an entry in .debug_info whose name ends in <suffix> + +package main + +import ( + "debug/dwarf" + "debug/elf" + "debug/macho" + "debug/pe" + "fmt" + "os" + "strings" +) + +func usage() { + fmt.Fprintf(os.Stderr, "checkdwarf executable-or-library DIE-suffix\n") +} + +type dwarfer interface { + DWARF() (*dwarf.Data, error) +} + +func openElf(path string) dwarfer { + exe, err := elf.Open(path) + if err != nil { + return nil + } + return exe +} + +func openMacho(path string) dwarfer { + exe, err := macho.Open(path) + if err != nil { + return nil + } + return exe +} + +func openPE(path string) dwarfer { + exe, err := pe.Open(path) + if err != nil { + return nil + } + return exe +} + +func main() { + if len(os.Args) != 3 { + usage() + } + + exePath := os.Args[1] + dieSuffix := os.Args[2] + + var exe dwarfer + + for _, openfn := range []func(string) dwarfer{openMacho, openPE, openElf} { + exe = openfn(exePath) + if exe != nil { + break + } + } + + if exe == nil { + fmt.Fprintf(os.Stderr, "could not open %s\n", exePath) + os.Exit(1) + } + + data, err := exe.DWARF() + if err != nil { + fmt.Fprintf(os.Stderr, "%s: error opening DWARF: %v\n", exePath, err) + os.Exit(1) + } + + rdr := data.Reader() + for { + e, err := rdr.Next() + if err != nil { + fmt.Fprintf(os.Stderr, "%s: error reading DWARF: %v\n", exePath, err) + os.Exit(1) + } + if e == nil { + break + } + name, hasname := e.Val(dwarf.AttrName).(string) + if !hasname { + continue + } + if strings.HasSuffix(name, dieSuffix) { + // found + os.Exit(0) + } + } + + fmt.Fprintf(os.Stderr, "%s: no entry with a name ending in %q was found\n", exePath, dieSuffix) + os.Exit(1) +} diff --git a/misc/cgo/testplugin/testdata/common/common.go b/misc/cgo/testplugin/testdata/common/common.go new file mode 100644 index 0000000..b064e6b --- /dev/null +++ b/misc/cgo/testplugin/testdata/common/common.go @@ -0,0 +1,11 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package common + +var X int + +func init() { + X = 3 +} diff --git a/misc/cgo/testplugin/testdata/forkexec/main.go b/misc/cgo/testplugin/testdata/forkexec/main.go new file mode 100644 index 0000000..3169ff5 --- /dev/null +++ b/misc/cgo/testplugin/testdata/forkexec/main.go @@ -0,0 +1,30 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "os" + "os/exec" + _ "plugin" + "sync" +) + +func main() { + if os.Args[1] != "1" { + return + } + + var wg sync.WaitGroup + for i := 0; i < 8; i++ { + wg.Add(1) + go func() { + defer wg.Done() + // does not matter what we exec, just exec itself + cmd := exec.Command("./forkexec.exe", "0") + cmd.Run() + }() + } + wg.Wait() +} diff --git a/misc/cgo/testplugin/testdata/host/host.go b/misc/cgo/testplugin/testdata/host/host.go new file mode 100644 index 0000000..a379932 --- /dev/null +++ b/misc/cgo/testplugin/testdata/host/host.go @@ -0,0 +1,176 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "log" + "path/filepath" + "plugin" + "strings" + + "testplugin/common" +) + +func init() { + common.X *= 5 +} + +// testUnnamed tests that two plugins built with .go files passed on +// the command line do not have overlapping symbols. That is, +// unnamed1.so/FuncInt and unnamed2.so/FuncInt should be distinct functions. +func testUnnamed() { + p, err := plugin.Open("unnamed1.so") + if err != nil { + log.Fatalf(`plugin.Open("unnamed1.so"): %v`, err) + } + fn, err := p.Lookup("FuncInt") + if err != nil { + log.Fatalf(`unnamed1.so: Lookup("FuncInt") failed: %v`, err) + } + if got, want := fn.(func() int)(), 1; got != want { + log.Fatalf("unnamed1.so: FuncInt()=%d, want %d", got, want) + } + + p, err = plugin.Open("unnamed2.so") + if err != nil { + log.Fatalf(`plugin.Open("unnamed2.so"): %v`, err) + } + fn, err = p.Lookup("FuncInt") + if err != nil { + log.Fatalf(`unnamed2.so: Lookup("FuncInt") failed: %v`, err) + } + if got, want := fn.(func() int)(), 2; got != want { + log.Fatalf("unnamed2.so: FuncInt()=%d, want %d", got, want) + } +} + +func main() { + if got, want := common.X, 3*5; got != want { + log.Fatalf("before plugin load common.X=%d, want %d", got, want) + } + + p, err := plugin.Open("plugin1.so") + if err != nil { + log.Fatalf("plugin.Open failed: %v", err) + } + + const wantX = 3 * 5 * 7 + if got := common.X; got != wantX { + log.Fatalf("after plugin load common.X=%d, want %d", got, wantX) + } + + seven, err := p.Lookup("Seven") + if err != nil { + log.Fatalf(`Lookup("Seven") failed: %v`, err) + } + if got, want := *seven.(*int), 7; got != want { + log.Fatalf("plugin1.Seven=%d, want %d", got, want) + } + + readFunc, err := p.Lookup("ReadCommonX") + if err != nil { + log.Fatalf(`plugin1.Lookup("ReadCommonX") failed: %v`, err) + } + if got := readFunc.(func() int)(); got != wantX { + log.Fatalf("plugin1.ReadCommonX()=%d, want %d", got, wantX) + } + + // sub/plugin1.so is a different plugin with the same name as + // the already loaded plugin. It also depends on common. Test + // that we can load the different plugin, it is actually + // different, and that it sees the same common package. + subpPath, err := filepath.Abs("sub/plugin1.so") + if err != nil { + log.Fatalf("filepath.Abs(%q) failed: %v", subpPath, err) + } + subp, err := plugin.Open(subpPath) + if err != nil { + log.Fatalf("plugin.Open(%q) failed: %v", subpPath, err) + } + + funcVar, err := subp.Lookup("FuncVar") + if err != nil { + log.Fatalf(`sub/plugin1.Lookup("FuncVar") failed: %v`, err) + } + called := false + *funcVar.(*func()) = func() { + called = true + } + + readFunc, err = subp.Lookup("ReadCommonX") + if err != nil { + log.Fatalf(`sub/plugin1.Lookup("ReadCommonX") failed: %v`, err) + } + if got := readFunc.(func() int)(); got != wantX { + log.Fatalf("sub/plugin1.ReadCommonX()=%d, want %d", got, wantX) + } + if !called { + log.Fatal("calling ReadCommonX did not call FuncVar") + } + + subf, err := subp.Lookup("F") + if err != nil { + log.Fatalf(`sub/plugin1.Lookup("F") failed: %v`, err) + } + if gotf := subf.(func() int)(); gotf != 17 { + log.Fatalf(`sub/plugin1.F()=%d, want 17`, gotf) + } + f, err := p.Lookup("F") + if err != nil { + log.Fatalf(`plugin1.Lookup("F") failed: %v`, err) + } + if gotf := f.(func() int)(); gotf != 3 { + log.Fatalf(`plugin1.F()=%d, want 17`, gotf) + } + + p2, err := plugin.Open("plugin2.so") + if err != nil { + log.Fatalf("plugin.Open failed: %v", err) + } + // Check that plugin2's init function was called, and + // that it modifies the same global variable as the host. + if got, want := common.X, 2; got != want { + log.Fatalf("after loading plugin2, common.X=%d, want %d", got, want) + } + + _, err = plugin.Open("plugin2-dup.so") + if err == nil { + log.Fatal(`plugin.Open("plugin2-dup.so"): duplicate open should have failed`) + } + if s := err.Error(); !strings.Contains(s, "already loaded") { + log.Fatal(`plugin.Open("plugin2.so"): error does not mention "already loaded"`) + } + + _, err = plugin.Open("plugin-mismatch.so") + if err == nil { + log.Fatal(`plugin.Open("plugin-mismatch.so"): should have failed`) + } + if s := err.Error(); !strings.Contains(s, "different version") { + log.Fatalf(`plugin.Open("plugin-mismatch.so"): error does not mention "different version": %v`, s) + } + + _, err = plugin.Open("plugin2-dup.so") + if err == nil { + log.Fatal(`plugin.Open("plugin2-dup.so"): duplicate open after bad plugin should have failed`) + } + _, err = plugin.Open("plugin2.so") + if err != nil { + log.Fatalf(`plugin.Open("plugin2.so"): second open with same name failed: %v`, err) + } + + // Test that unexported types with the same names in + // different plugins do not interfere with each other. + // + // See Issue #21386. + UnexportedNameReuse, _ := p.Lookup("UnexportedNameReuse") + UnexportedNameReuse.(func())() + UnexportedNameReuse, _ = p2.Lookup("UnexportedNameReuse") + UnexportedNameReuse.(func())() + + testUnnamed() + + fmt.Println("PASS") +} diff --git a/misc/cgo/testplugin/testdata/iface/main.go b/misc/cgo/testplugin/testdata/iface/main.go new file mode 100644 index 0000000..c04f288 --- /dev/null +++ b/misc/cgo/testplugin/testdata/iface/main.go @@ -0,0 +1,47 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "log" + "plugin" + + "testplugin/iface_i" +) + +func main() { + a, err := plugin.Open("iface_a.so") + if err != nil { + log.Fatalf(`plugin.Open("iface_a.so"): %v`, err) + } + b, err := plugin.Open("iface_b.so") + if err != nil { + log.Fatalf(`plugin.Open("iface_b.so"): %v`, err) + } + + af, err := a.Lookup("F") + if err != nil { + log.Fatalf(`a.Lookup("F") failed: %v`, err) + } + bf, err := b.Lookup("F") + if err != nil { + log.Fatalf(`b.Lookup("F") failed: %v`, err) + } + if af.(func() interface{})() != bf.(func() interface{})() { + panic("empty interfaces not equal") + } + + ag, err := a.Lookup("G") + if err != nil { + log.Fatalf(`a.Lookup("G") failed: %v`, err) + } + bg, err := b.Lookup("G") + if err != nil { + log.Fatalf(`b.Lookup("G") failed: %v`, err) + } + if ag.(func() iface_i.I)() != bg.(func() iface_i.I)() { + panic("nonempty interfaces not equal") + } +} diff --git a/misc/cgo/testplugin/testdata/iface_a/a.go b/misc/cgo/testplugin/testdata/iface_a/a.go new file mode 100644 index 0000000..357f7e8 --- /dev/null +++ b/misc/cgo/testplugin/testdata/iface_a/a.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "testplugin/iface_i" + +//go:noinline +func F() interface{} { + return (*iface_i.T)(nil) +} + +//go:noinline +func G() iface_i.I { + return (*iface_i.T)(nil) +} diff --git a/misc/cgo/testplugin/testdata/iface_b/b.go b/misc/cgo/testplugin/testdata/iface_b/b.go new file mode 100644 index 0000000..357f7e8 --- /dev/null +++ b/misc/cgo/testplugin/testdata/iface_b/b.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "testplugin/iface_i" + +//go:noinline +func F() interface{} { + return (*iface_i.T)(nil) +} + +//go:noinline +func G() iface_i.I { + return (*iface_i.T)(nil) +} diff --git a/misc/cgo/testplugin/testdata/iface_i/i.go b/misc/cgo/testplugin/testdata/iface_i/i.go new file mode 100644 index 0000000..31c8038 --- /dev/null +++ b/misc/cgo/testplugin/testdata/iface_i/i.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package iface_i + +type I interface { + M() +} + +type T struct { +} + +func (t *T) M() { +} + +// *T implements I diff --git a/misc/cgo/testplugin/testdata/issue18584/main.go b/misc/cgo/testplugin/testdata/issue18584/main.go new file mode 100644 index 0000000..c280fd4 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue18584/main.go @@ -0,0 +1,23 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "plugin" + +func main() { + p, err := plugin.Open("plugin.so") + if err != nil { + panic(err) + } + + sym, err := p.Lookup("G") + if err != nil { + panic(err) + } + g := sym.(func() bool) + if !g() { + panic("expected types to match, Issue #18584") + } +} diff --git a/misc/cgo/testplugin/testdata/issue18584/plugin.go b/misc/cgo/testplugin/testdata/issue18584/plugin.go new file mode 100644 index 0000000..be0868d --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue18584/plugin.go @@ -0,0 +1,19 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "reflect" + +type C struct { +} + +func F(c *C) *C { + return nil +} + +func G() bool { + var c *C + return reflect.TypeOf(F).Out(0) == reflect.TypeOf(c) +} diff --git a/misc/cgo/testplugin/testdata/issue18676/dynamodbstreamsevt/definition.go b/misc/cgo/testplugin/testdata/issue18676/dynamodbstreamsevt/definition.go new file mode 100644 index 0000000..70fd054 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue18676/dynamodbstreamsevt/definition.go @@ -0,0 +1,13 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dynamodbstreamsevt + +import "encoding/json" + +var foo json.RawMessage + +type Event struct{} + +func (e *Event) Dummy() {} diff --git a/misc/cgo/testplugin/testdata/issue18676/main.go b/misc/cgo/testplugin/testdata/issue18676/main.go new file mode 100644 index 0000000..b1dadbe --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue18676/main.go @@ -0,0 +1,31 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The bug happened like this: +// 1) The main binary adds an itab for *json.UnsupportedValueError / error +// (concrete type / interface type). This itab goes in hash bucket 0x111. +// 2) The plugin adds that same itab again. That makes a cycle in the itab +// chain rooted at hash bucket 0x111. +// 3) The main binary then asks for the itab for *dynamodbstreamsevt.Event / +// json.Unmarshaler. This itab happens to also live in bucket 0x111. +// The lookup code goes into an infinite loop searching for this itab. +// The code is carefully crafted so that the two itabs are both from the +// same bucket, and so that the second itab doesn't exist in +// the itab hashmap yet (so the entire linked list must be searched). +package main + +import ( + "encoding/json" + "plugin" + "testplugin/issue18676/dynamodbstreamsevt" +) + +func main() { + plugin.Open("plugin.so") + + var x interface{} = (*dynamodbstreamsevt.Event)(nil) + if _, ok := x.(json.Unmarshaler); !ok { + println("something") + } +} diff --git a/misc/cgo/testplugin/testdata/issue18676/plugin.go b/misc/cgo/testplugin/testdata/issue18676/plugin.go new file mode 100644 index 0000000..e7fc74f --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue18676/plugin.go @@ -0,0 +1,11 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "C" + +import "testplugin/issue18676/dynamodbstreamsevt" + +func F(evt *dynamodbstreamsevt.Event) {} diff --git a/misc/cgo/testplugin/testdata/issue19418/main.go b/misc/cgo/testplugin/testdata/issue19418/main.go new file mode 100644 index 0000000..2ec9f9a --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue19418/main.go @@ -0,0 +1,29 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "plugin" +) + +func main() { + p, err := plugin.Open("plugin.so") + if err != nil { + panic(err) + } + + val, err := p.Lookup("Val") + if err != nil { + panic(err) + } + got := *val.(*string) + const want = "linkstr" + if got != want { + fmt.Fprintf(os.Stderr, "issue19418 value is %q, want %q\n", got, want) + os.Exit(2) + } +} diff --git a/misc/cgo/testplugin/testdata/issue19418/plugin.go b/misc/cgo/testplugin/testdata/issue19418/plugin.go new file mode 100644 index 0000000..fe93b16 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue19418/plugin.go @@ -0,0 +1,7 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +var Val = "val-unset" diff --git a/misc/cgo/testplugin/testdata/issue19529/plugin.go b/misc/cgo/testplugin/testdata/issue19529/plugin.go new file mode 100644 index 0000000..ad2df6c --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue19529/plugin.go @@ -0,0 +1,15 @@ +package main + +import ( + "reflect" +) + +type Foo struct { + Bar string `json:"Bar@baz,omitempty"` +} + +func F() { + println(reflect.TypeOf(Foo{}).Field(0).Tag) +} + +func main() {} diff --git a/misc/cgo/testplugin/testdata/issue19534/main.go b/misc/cgo/testplugin/testdata/issue19534/main.go new file mode 100644 index 0000000..de263b6 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue19534/main.go @@ -0,0 +1,23 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "plugin" + +func main() { + p, err := plugin.Open("plugin.so") + if err != nil { + panic(err) + } + + sym, err := p.Lookup("Foo") + if err != nil { + panic(err) + } + f := sym.(func() int) + if f() != 42 { + panic("expected f() == 42") + } +} diff --git a/misc/cgo/testplugin/testdata/issue19534/plugin.go b/misc/cgo/testplugin/testdata/issue19534/plugin.go new file mode 100644 index 0000000..582d333 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue19534/plugin.go @@ -0,0 +1,9 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func Foo() int { + return 42 +} diff --git a/misc/cgo/testplugin/testdata/issue22175/main.go b/misc/cgo/testplugin/testdata/issue22175/main.go new file mode 100644 index 0000000..9be9bab --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue22175/main.go @@ -0,0 +1,28 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "os" + "plugin" +) + +func main() { + p2, err := plugin.Open("issue22175_plugin1.so") + if err != nil { + panic(err) + } + f, err := p2.Lookup("F") + if err != nil { + panic(err) + } + got := f.(func() int)() + const want = 971 + if got != want { + fmt.Fprintf(os.Stderr, "issue22175: F()=%d, want %d", got, want) + os.Exit(1) + } +} diff --git a/misc/cgo/testplugin/testdata/issue22175/plugin1.go b/misc/cgo/testplugin/testdata/issue22175/plugin1.go new file mode 100644 index 0000000..5ae6cb6 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue22175/plugin1.go @@ -0,0 +1,21 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "plugin" + +func F() int { + p2, err := plugin.Open("issue22175_plugin2.so") + if err != nil { + panic(err) + } + g, err := p2.Lookup("G") + if err != nil { + panic(err) + } + return g.(func() int)() +} + +func main() {} diff --git a/misc/cgo/testplugin/testdata/issue22175/plugin2.go b/misc/cgo/testplugin/testdata/issue22175/plugin2.go new file mode 100644 index 0000000..f387a19 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue22175/plugin2.go @@ -0,0 +1,9 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func G() int { return 971 } + +func main() {} diff --git a/misc/cgo/testplugin/testdata/issue22295.pkg/main.go b/misc/cgo/testplugin/testdata/issue22295.pkg/main.go new file mode 100644 index 0000000..6cb186e --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue22295.pkg/main.go @@ -0,0 +1,28 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import ( + "log" + "plugin" +) + +func main() { + p, err := plugin.Open("issue.22295.so") + if err != nil { + log.Fatal(err) + } + f, err := p.Lookup("F") + if err != nil { + log.Fatal(err) + } + const want = 2503 + got := f.(func() int)() + if got != want { + log.Fatalf("got %d, want %d", got, want) + } +} diff --git a/misc/cgo/testplugin/testdata/issue22295.pkg/plugin.go b/misc/cgo/testplugin/testdata/issue22295.pkg/plugin.go new file mode 100644 index 0000000..46b08a4 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue22295.pkg/plugin.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +var f *int + +func init() { + f = new(int) + *f = 2503 +} + +func F() int { return *f } + +func main() {} diff --git a/misc/cgo/testplugin/testdata/issue24351/main.go b/misc/cgo/testplugin/testdata/issue24351/main.go new file mode 100644 index 0000000..4107adf --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue24351/main.go @@ -0,0 +1,21 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "plugin" + +func main() { + p, err := plugin.Open("issue24351.so") + if err != nil { + panic(err) + } + f, err := p.Lookup("B") + if err != nil { + panic(err) + } + c := make(chan bool) + f.(func(chan bool))(c) + <-c +} diff --git a/misc/cgo/testplugin/testdata/issue24351/plugin.go b/misc/cgo/testplugin/testdata/issue24351/plugin.go new file mode 100644 index 0000000..db17e0a --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue24351/plugin.go @@ -0,0 +1,14 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "fmt" + +func B(c chan bool) { + go func() { + fmt.Println(1.5) + c <- true + }() +} diff --git a/misc/cgo/testplugin/testdata/issue25756/main.go b/misc/cgo/testplugin/testdata/issue25756/main.go new file mode 100644 index 0000000..817daf4 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue25756/main.go @@ -0,0 +1,52 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Run the game of life in C using Go for parallelization. + +package main + +import ( + "flag" + "fmt" + "plugin" +) + +const MAXDIM = 100 + +var dim = flag.Int("dim", 16, "board dimensions") +var gen = flag.Int("gen", 10, "generations") + +func main() { + flag.Parse() + + var a [MAXDIM * MAXDIM]int32 + for i := 2; i < *dim; i += 8 { + for j := 2; j < *dim-3; j += 8 { + for y := 0; y < 3; y++ { + a[i**dim+j+y] = 1 + } + } + } + + p, err := plugin.Open("life.so") + if err != nil { + panic(err) + } + f, err := p.Lookup("Run") + if err != nil { + panic(err) + } + f.(func(int, int, int, []int32))(*gen, *dim, *dim, a[:]) + + for i := 0; i < *dim; i++ { + for j := 0; j < *dim; j++ { + if a[i**dim+j] == 0 { + fmt.Print(" ") + } else { + fmt.Print("X") + } + } + fmt.Print("\n") + } +} diff --git a/misc/cgo/testplugin/testdata/issue25756/plugin/c-life.c b/misc/cgo/testplugin/testdata/issue25756/plugin/c-life.c new file mode 100644 index 0000000..f853163 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue25756/plugin/c-life.c @@ -0,0 +1,56 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <assert.h> +#include "life.h" +#include "_cgo_export.h" + +const int MYCONST = 0; + +// Do the actual manipulation of the life board in C. This could be +// done easily in Go, we are just using C for demonstration +// purposes. +void +Step(int x, int y, int *a, int *n) +{ + struct GoStart_return r; + + // Use Go to start 4 goroutines each of which handles 1/4 of the + // board. + r = GoStart(0, x, y, 0, x / 2, 0, y / 2, a, n); + assert(r.r0 == 0 && r.r1 == 100); // test multiple returns + r = GoStart(1, x, y, x / 2, x, 0, y / 2, a, n); + assert(r.r0 == 1 && r.r1 == 101); // test multiple returns + GoStart(2, x, y, 0, x / 2, y / 2, y, a, n); + GoStart(3, x, y, x / 2, x, y / 2, y, a, n); + GoWait(0); + GoWait(1); + GoWait(2); + GoWait(3); +} + +// The actual computation. This is called in parallel. +void +DoStep(int xdim, int ydim, int xstart, int xend, int ystart, int yend, int *a, int *n) +{ + int x, y, c, i, j; + + for(x = xstart; x < xend; x++) { + for(y = ystart; y < yend; y++) { + c = 0; + for(i = -1; i <= 1; i++) { + for(j = -1; j <= 1; j++) { + if(x+i >= 0 && x+i < xdim && + y+j >= 0 && y+j < ydim && + (i != 0 || j != 0)) + c += a[(x+i)*xdim + (y+j)] != 0; + } + } + if(c == 3 || (c == 2 && a[x*xdim + y] != 0)) + n[x*xdim + y] = 1; + else + n[x*xdim + y] = 0; + } + } +} diff --git a/misc/cgo/testplugin/testdata/issue25756/plugin/life.go b/misc/cgo/testplugin/testdata/issue25756/plugin/life.go new file mode 100644 index 0000000..675a192 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue25756/plugin/life.go @@ -0,0 +1,39 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// #include "life.h" +import "C" + +import "unsafe" + +func Run(gen, x, y int, a []int32) { + n := make([]int32, x*y) + for i := 0; i < gen; i++ { + C.Step(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&a[0])), (*C.int)(unsafe.Pointer(&n[0]))) + copy(a, n) + } +} + +// Keep the channels visible from Go. +var chans [4]chan bool + +//export GoStart +// Double return value is just for testing. +func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) { + c := make(chan bool, int(C.MYCONST)) + go func() { + C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n) + c <- true + }() + chans[i] = c + return int(i), int(i + 100) +} + +//export GoWait +func GoWait(i C.int) { + <-chans[i] + chans[i] = nil +} diff --git a/misc/cgo/testplugin/testdata/issue25756/plugin/life.h b/misc/cgo/testplugin/testdata/issue25756/plugin/life.h new file mode 100644 index 0000000..11d2b97 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue25756/plugin/life.h @@ -0,0 +1,7 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +extern void Step(int, int, int *, int *); +extern void DoStep(int, int, int, int, int, int, int *, int *); +extern const int MYCONST; diff --git a/misc/cgo/testplugin/testdata/issue44956/base/base.go b/misc/cgo/testplugin/testdata/issue44956/base/base.go new file mode 100644 index 0000000..609aa0d --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue44956/base/base.go @@ -0,0 +1,7 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package base + +var X = &map[int]int{123: 456} diff --git a/misc/cgo/testplugin/testdata/issue44956/main.go b/misc/cgo/testplugin/testdata/issue44956/main.go new file mode 100644 index 0000000..287a605 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue44956/main.go @@ -0,0 +1,47 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 44956: writable static temp is not exported correctly. +// In the test below, package base is +// +// X = &map{...} +// +// which compiles to +// +// X = &stmp // static +// stmp = makemap(...) // in init function +// +// plugin1 and plugin2 both import base. plugin1 doesn't use +// base.X, so that symbol is deadcoded in plugin1. +// +// plugin1 is loaded first. base.init runs at that point, which +// initialize base.stmp. +// +// plugin2 is then loaded. base.init already ran, so it doesn't run +// again. When base.stmp is not exported, plugin2's base.X points to +// its own private base.stmp, which is not initialized, fail. + +package main + +import "plugin" + +func main() { + _, err := plugin.Open("issue44956p1.so") + if err != nil { + panic("FAIL") + } + + p2, err := plugin.Open("issue44956p2.so") + if err != nil { + panic("FAIL") + } + f, err := p2.Lookup("F") + if err != nil { + panic("FAIL") + } + x := f.(func() *map[int]int)() + if x == nil || (*x)[123] != 456 { + panic("FAIL") + } +} diff --git a/misc/cgo/testplugin/testdata/issue44956/plugin1.go b/misc/cgo/testplugin/testdata/issue44956/plugin1.go new file mode 100644 index 0000000..499fa31 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue44956/plugin1.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import _ "testplugin/issue44956/base" + +func main() {} diff --git a/misc/cgo/testplugin/testdata/issue44956/plugin2.go b/misc/cgo/testplugin/testdata/issue44956/plugin2.go new file mode 100644 index 0000000..a73542c --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue44956/plugin2.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "testplugin/issue44956/base" + +func F() *map[int]int { return base.X } + +func main() {} diff --git a/misc/cgo/testplugin/testdata/issue52937/main.go b/misc/cgo/testplugin/testdata/issue52937/main.go new file mode 100644 index 0000000..66f09ef --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue52937/main.go @@ -0,0 +1,9 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() {} +func F[T any]() {} +func G[T any](T) {} diff --git a/misc/cgo/testplugin/testdata/issue53989/main.go b/misc/cgo/testplugin/testdata/issue53989/main.go new file mode 100644 index 0000000..6907dfd --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue53989/main.go @@ -0,0 +1,32 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 53989: the use of jump table caused a function +// from the plugin jumps in the middle of the function +// to the function with the same name in the main +// executable. As these two functions may be compiled +// differently as plugin needs to be PIC, this causes +// crash. + +package main + +import ( + "plugin" + + "testplugin/issue53989/p" +) + +func main() { + p.Square(7) // call the function in main executable + + p, err := plugin.Open("issue53989.so") + if err != nil { + panic(err) + } + f, err := p.Lookup("Square") + if err != nil { + panic(err) + } + f.(func(int))(7) // call the plugin one +} diff --git a/misc/cgo/testplugin/testdata/issue53989/p/p.go b/misc/cgo/testplugin/testdata/issue53989/p/p.go new file mode 100644 index 0000000..02567c1 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue53989/p/p.go @@ -0,0 +1,52 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +import ( + "fmt" + "runtime" +) + +var y int + +//go:noinline +func Square(x int) { + var pc0, pc1 [1]uintptr + runtime.Callers(1, pc0[:]) // get PC at entry + + // a switch using jump table + switch x { + case 1: + y = 1 + case 2: + y = 4 + case 3: + y = 9 + case 4: + y = 16 + case 5: + y = 25 + case 6: + y = 36 + case 7: + y = 49 + case 8: + y = 64 + default: + panic("too large") + } + + // check PC is in the same function + runtime.Callers(1, pc1[:]) + if pc1[0] < pc0[0] || pc1[0] > pc0[0]+1000000 { + fmt.Printf("jump across DSO boundary. pc0=%x, pc1=%x\n", pc0[0], pc1[0]) + panic("FAIL") + } + + if y != x*x { + fmt.Printf("x=%d y=%d!=%d\n", x, y, x*x) + panic("FAIL") + } +} diff --git a/misc/cgo/testplugin/testdata/issue53989/plugin.go b/misc/cgo/testplugin/testdata/issue53989/plugin.go new file mode 100644 index 0000000..a753ee4 --- /dev/null +++ b/misc/cgo/testplugin/testdata/issue53989/plugin.go @@ -0,0 +1,13 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "testplugin/issue53989/p" + +func Square(x int) { // export Square for plugin + p.Square(x) +} + +func main() {} diff --git a/misc/cgo/testplugin/testdata/method/main.go b/misc/cgo/testplugin/testdata/method/main.go new file mode 100644 index 0000000..5e9189b --- /dev/null +++ b/misc/cgo/testplugin/testdata/method/main.go @@ -0,0 +1,26 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 42579: methods of symbols exported from plugin must be live. + +package main + +import ( + "plugin" + "reflect" +) + +func main() { + p, err := plugin.Open("plugin.so") + if err != nil { + panic(err) + } + + x, err := p.Lookup("X") + if err != nil { + panic(err) + } + + reflect.ValueOf(x).Elem().MethodByName("M").Call(nil) +} diff --git a/misc/cgo/testplugin/testdata/method/plugin.go b/misc/cgo/testplugin/testdata/method/plugin.go new file mode 100644 index 0000000..240edd3 --- /dev/null +++ b/misc/cgo/testplugin/testdata/method/plugin.go @@ -0,0 +1,13 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +func main() {} + +type T int + +func (T) M() { println("M") } + +var X T diff --git a/misc/cgo/testplugin/testdata/method2/main.go b/misc/cgo/testplugin/testdata/method2/main.go new file mode 100644 index 0000000..89afbda --- /dev/null +++ b/misc/cgo/testplugin/testdata/method2/main.go @@ -0,0 +1,32 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// A type can be passed to a plugin and converted to interface +// there. So its methods need to be live. + +package main + +import ( + "plugin" + + "testplugin/method2/p" +) + +var t p.T + +type I interface{ M() } + +func main() { + pl, err := plugin.Open("method2.so") + if err != nil { + panic(err) + } + + f, err := pl.Lookup("F") + if err != nil { + panic(err) + } + + f.(func(p.T) interface{})(t).(I).M() +} diff --git a/misc/cgo/testplugin/testdata/method2/p/p.go b/misc/cgo/testplugin/testdata/method2/p/p.go new file mode 100644 index 0000000..acb526a --- /dev/null +++ b/misc/cgo/testplugin/testdata/method2/p/p.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T int + +func (T) M() { println("M") } diff --git a/misc/cgo/testplugin/testdata/method2/plugin.go b/misc/cgo/testplugin/testdata/method2/plugin.go new file mode 100644 index 0000000..6198e76 --- /dev/null +++ b/misc/cgo/testplugin/testdata/method2/plugin.go @@ -0,0 +1,11 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "testplugin/method2/p" + +func main() {} + +func F(t p.T) interface{} { return t } diff --git a/misc/cgo/testplugin/testdata/method3/main.go b/misc/cgo/testplugin/testdata/method3/main.go new file mode 100644 index 0000000..a3a5171 --- /dev/null +++ b/misc/cgo/testplugin/testdata/method3/main.go @@ -0,0 +1,32 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// An unexported method can be reachable from the plugin via interface +// when a package is shared. So it need to be live. + +package main + +import ( + "plugin" + + "testplugin/method3/p" +) + +var i p.I + +func main() { + pl, err := plugin.Open("method3.so") + if err != nil { + panic(err) + } + + f, err := pl.Lookup("F") + if err != nil { + panic(err) + } + + f.(func())() + + i = p.T(123) +} diff --git a/misc/cgo/testplugin/testdata/method3/p/p.go b/misc/cgo/testplugin/testdata/method3/p/p.go new file mode 100644 index 0000000..3846bc0 --- /dev/null +++ b/misc/cgo/testplugin/testdata/method3/p/p.go @@ -0,0 +1,17 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T int + +func (T) m() { println("m") } + +type I interface { m() } + +func F() { + i.m() +} + +var i I = T(123) diff --git a/misc/cgo/testplugin/testdata/method3/plugin.go b/misc/cgo/testplugin/testdata/method3/plugin.go new file mode 100644 index 0000000..bd25b31 --- /dev/null +++ b/misc/cgo/testplugin/testdata/method3/plugin.go @@ -0,0 +1,11 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "testplugin/method3/p" + +func main() {} + +func F() { p.F() } diff --git a/misc/cgo/testplugin/testdata/plugin1/plugin1.go b/misc/cgo/testplugin/testdata/plugin1/plugin1.go new file mode 100644 index 0000000..d29d674 --- /dev/null +++ b/misc/cgo/testplugin/testdata/plugin1/plugin1.go @@ -0,0 +1,57 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// // No C code required. +import "C" + +import ( + "reflect" + + "testplugin/common" +) + +func F() int { + _ = make([]byte, 1<<21) // trigger stack unwind, Issue #18190. + return 3 +} + +func ReadCommonX() int { + return common.X +} + +var Seven int + +func call(fn func()) { + fn() +} + +func g() { + common.X *= Seven +} + +func init() { + Seven = 7 + call(g) +} + +type sameNameReusedInPlugins struct { + X string +} + +type sameNameHolder struct { + F *sameNameReusedInPlugins +} + +func UnexportedNameReuse() { + h := sameNameHolder{} + v := reflect.ValueOf(&h).Elem().Field(0) + newval := reflect.New(v.Type().Elem()) + v.Set(newval) +} + +func main() { + panic("plugin1.main called") +} diff --git a/misc/cgo/testplugin/testdata/plugin2/plugin2.go b/misc/cgo/testplugin/testdata/plugin2/plugin2.go new file mode 100644 index 0000000..31ed642 --- /dev/null +++ b/misc/cgo/testplugin/testdata/plugin2/plugin2.go @@ -0,0 +1,44 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +//#include <errno.h> +//#include <string.h> +import "C" + +// #include +// void cfunc() {} // uses cgo_topofstack + +import ( + "reflect" + "strings" + + "testplugin/common" +) + +func init() { + _ = strings.NewReplacer() // trigger stack unwind, Issue #18190. + C.strerror(C.EIO) // uses cgo_topofstack + common.X = 2 +} + +type sameNameReusedInPlugins struct { + X string +} + +type sameNameHolder struct { + F *sameNameReusedInPlugins +} + +func UnexportedNameReuse() { + h := sameNameHolder{} + v := reflect.ValueOf(&h).Elem().Field(0) + newval := reflect.New(v.Type().Elem()) + v.Set(newval) +} + +func main() { + panic("plugin1.main called") +} diff --git a/misc/cgo/testplugin/testdata/sub/plugin1/plugin1.go b/misc/cgo/testplugin/testdata/sub/plugin1/plugin1.go new file mode 100644 index 0000000..5f891b0 --- /dev/null +++ b/misc/cgo/testplugin/testdata/sub/plugin1/plugin1.go @@ -0,0 +1,23 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// // No C code required. +import "C" + +import "testplugin/common" + +func F() int { return 17 } + +var FuncVar = func() {} + +func ReadCommonX() int { + FuncVar() + return common.X +} + +func main() { + panic("plugin1.main called") +} diff --git a/misc/cgo/testplugin/testdata/unnamed1/main.go b/misc/cgo/testplugin/testdata/unnamed1/main.go new file mode 100644 index 0000000..dd1777b --- /dev/null +++ b/misc/cgo/testplugin/testdata/unnamed1/main.go @@ -0,0 +1,25 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +// // No C code required. +import "C" + +func FuncInt() int { return 1 } + +// Add a recursive type to check that type equality across plugins doesn't +// crash. See https://golang.org/issues/19258 +func FuncRecursive() X { return X{} } + +type Y struct { + X *X +} +type X struct { + Y Y +} + +func main() {} diff --git a/misc/cgo/testplugin/testdata/unnamed2/main.go b/misc/cgo/testplugin/testdata/unnamed2/main.go new file mode 100644 index 0000000..757436f --- /dev/null +++ b/misc/cgo/testplugin/testdata/unnamed2/main.go @@ -0,0 +1,23 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +// // No C code required. +import "C" + +func FuncInt() int { return 2 } + +func FuncRecursive() X { return X{} } + +type Y struct { + X *X +} +type X struct { + Y Y +} + +func main() {} diff --git a/misc/cgo/testsanitizers/asan_test.go b/misc/cgo/testsanitizers/asan_test.go new file mode 100644 index 0000000..932cfb1 --- /dev/null +++ b/misc/cgo/testsanitizers/asan_test.go @@ -0,0 +1,141 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sanitizers_test + +import ( + "fmt" + "strings" + "testing" +) + +func TestASAN(t *testing.T) { + goos, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + goarch, err := goEnv("GOARCH") + if err != nil { + t.Fatal(err) + } + // The asan tests require support for the -asan option. + if !aSanSupported(goos, goarch) { + t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch) + } + // The current implementation is only compatible with the ASan library from version + // v7 to v9 (See the description in src/runtime/asan/asan.go). Therefore, using the + // -asan option must use a compatible version of ASan library, which requires that + // the gcc version is not less than 7 and the clang version is not less than 9, + // otherwise a segmentation fault will occur. + if !compilerRequiredAsanVersion(goos, goarch) { + t.Skipf("skipping on %s/%s: too old version of compiler", goos, goarch) + } + + t.Parallel() + requireOvercommit(t) + config := configure("address") + config.skipIfCSanitizerBroken(t) + + mustRun(t, config.goCmd("build", "std")) + + cases := []struct { + src string + memoryAccessError string + errorLocation string + experiments []string + }{ + {src: "asan1_fail.go", memoryAccessError: "heap-use-after-free", errorLocation: "asan1_fail.go:25"}, + {src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow", errorLocation: "asan2_fail.go:31"}, + {src: "asan3_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan3_fail.go:13"}, + {src: "asan4_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan4_fail.go:13"}, + {src: "asan5_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan5_fail.go:18"}, + {src: "asan_useAfterReturn.go"}, + {src: "asan_unsafe_fail1.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail1.go:25"}, + {src: "asan_unsafe_fail2.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail2.go:25"}, + {src: "asan_unsafe_fail3.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail3.go:18"}, + {src: "asan_global1_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global1_fail.go:12"}, + {src: "asan_global2_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global2_fail.go:19"}, + {src: "asan_global3_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global3_fail.go:13"}, + {src: "asan_global4_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global4_fail.go:21"}, + {src: "asan_global5.go"}, + {src: "arena_fail.go", memoryAccessError: "use-after-poison", errorLocation: "arena_fail.go:26", experiments: []string{"arenas"}}, + } + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.src, ".go") + t.Run(name, func(t *testing.T) { + t.Parallel() + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + outPath := dir.Join(name) + mustRun(t, config.goCmdWithExperiments("build", []string{"-o", outPath, srcPath(tc.src)}, tc.experiments)) + + cmd := hangProneCmd(outPath) + if tc.memoryAccessError != "" { + outb, err := cmd.CombinedOutput() + out := string(outb) + if err != nil && strings.Contains(out, tc.memoryAccessError) { + // This string is output if the + // sanitizer library needs a + // symbolizer program and can't find it. + const noSymbolizer = "external symbolizer" + // Check if -asan option can correctly print where the error occurred. + if tc.errorLocation != "" && + !strings.Contains(out, tc.errorLocation) && + !strings.Contains(out, noSymbolizer) && + compilerSupportsLocation() { + + t.Errorf("%#q exited without expected location of the error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.errorLocation, out) + } + return + } + t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out) + } + mustRun(t, cmd) + }) + } +} + +func TestASANLinkerX(t *testing.T) { + // Test ASAN with linker's -X flag (see issue 56175). + goos, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + goarch, err := goEnv("GOARCH") + if err != nil { + t.Fatal(err) + } + // The asan tests require support for the -asan option. + if !aSanSupported(goos, goarch) { + t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch) + } + if !compilerRequiredAsanVersion(goos, goarch) { + t.Skipf("skipping on %s/%s: too old version of compiler", goos, goarch) + } + + t.Parallel() + requireOvercommit(t) + config := configure("address") + config.skipIfCSanitizerBroken(t) + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + var ldflags string + for i := 1; i <= 10; i++ { + ldflags += fmt.Sprintf("-X=main.S%d=%d -X=misc/cgo/testsanitizers/testdata/asan_linkerx/p.S%d=%d ", i, i, i, i) + } + + // build the binary + outPath := dir.Join("main.exe") + cmd := config.goCmd("build", "-ldflags="+ldflags, "-o", outPath) + cmd.Dir = srcPath("asan_linkerx") + mustRun(t, cmd) + + // run the binary + mustRun(t, hangProneCmd(outPath)) +} diff --git a/misc/cgo/testsanitizers/cc_test.go b/misc/cgo/testsanitizers/cc_test.go new file mode 100644 index 0000000..8eda137 --- /dev/null +++ b/misc/cgo/testsanitizers/cc_test.go @@ -0,0 +1,583 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// sanitizers_test checks the use of Go with sanitizers like msan, asan, etc. +// See https://github.com/google/sanitizers. +package sanitizers_test + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" + "sync" + "syscall" + "testing" + "time" + "unicode" +) + +var overcommit struct { + sync.Once + value int + err error +} + +// requireOvercommit skips t if the kernel does not allow overcommit. +func requireOvercommit(t *testing.T) { + t.Helper() + + overcommit.Once.Do(func() { + var out []byte + out, overcommit.err = os.ReadFile("/proc/sys/vm/overcommit_memory") + if overcommit.err != nil { + return + } + overcommit.value, overcommit.err = strconv.Atoi(string(bytes.TrimSpace(out))) + }) + + if overcommit.err != nil { + t.Skipf("couldn't determine vm.overcommit_memory (%v); assuming no overcommit", overcommit.err) + } + if overcommit.value == 2 { + t.Skip("vm.overcommit_memory=2") + } +} + +var env struct { + sync.Once + m map[string]string + err error +} + +// goEnv returns the output of $(go env) as a map. +func goEnv(key string) (string, error) { + env.Once.Do(func() { + var out []byte + out, env.err = exec.Command("go", "env", "-json").Output() + if env.err != nil { + return + } + + env.m = make(map[string]string) + env.err = json.Unmarshal(out, &env.m) + }) + if env.err != nil { + return "", env.err + } + + v, ok := env.m[key] + if !ok { + return "", fmt.Errorf("`go env`: no entry for %v", key) + } + return v, nil +} + +// replaceEnv sets the key environment variable to value in cmd. +func replaceEnv(cmd *exec.Cmd, key, value string) { + if cmd.Env == nil { + cmd.Env = cmd.Environ() + } + cmd.Env = append(cmd.Env, key+"="+value) +} + +// appendExperimentEnv appends comma-separated experiments to GOEXPERIMENT. +func appendExperimentEnv(cmd *exec.Cmd, experiments []string) { + if cmd.Env == nil { + cmd.Env = cmd.Environ() + } + exps := strings.Join(experiments, ",") + for _, evar := range cmd.Env { + c := strings.SplitN(evar, "=", 2) + if c[0] == "GOEXPERIMENT" { + exps = c[1] + "," + exps + } + } + cmd.Env = append(cmd.Env, "GOEXPERIMENT="+exps) +} + +// mustRun executes t and fails cmd with a well-formatted message if it fails. +func mustRun(t *testing.T, cmd *exec.Cmd) { + t.Helper() + out := new(strings.Builder) + cmd.Stdout = out + cmd.Stderr = out + + err := cmd.Start() + if err != nil { + t.Fatalf("%v: %v", cmd, err) + } + + if deadline, ok := t.Deadline(); ok { + timeout := time.Until(deadline) + timeout -= timeout / 10 // Leave 10% headroom for logging and cleanup. + timer := time.AfterFunc(timeout, func() { + cmd.Process.Signal(syscall.SIGQUIT) + }) + defer timer.Stop() + } + + if err := cmd.Wait(); err != nil { + t.Fatalf("%v exited with %v\n%s", cmd, err, out) + } +} + +// cc returns a cmd that executes `$(go env CC) $(go env GOGCCFLAGS) $args`. +func cc(args ...string) (*exec.Cmd, error) { + CC, err := goEnv("CC") + if err != nil { + return nil, err + } + + GOGCCFLAGS, err := goEnv("GOGCCFLAGS") + if err != nil { + return nil, err + } + + // Split GOGCCFLAGS, respecting quoting. + // + // TODO(bcmills): This code also appears in + // misc/cgo/testcarchive/carchive_test.go, and perhaps ought to go in + // src/cmd/dist/test.go as well. Figure out where to put it so that it can be + // shared. + var flags []string + quote := '\000' + start := 0 + lastSpace := true + backslash := false + for i, c := range GOGCCFLAGS { + if quote == '\000' && unicode.IsSpace(c) { + if !lastSpace { + flags = append(flags, GOGCCFLAGS[start:i]) + lastSpace = true + } + } else { + if lastSpace { + start = i + lastSpace = false + } + if quote == '\000' && !backslash && (c == '"' || c == '\'') { + quote = c + backslash = false + } else if !backslash && quote == c { + quote = '\000' + } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { + backslash = true + } else { + backslash = false + } + } + } + if !lastSpace { + flags = append(flags, GOGCCFLAGS[start:]) + } + + cmd := exec.Command(CC, flags...) + cmd.Args = append(cmd.Args, args...) + return cmd, nil +} + +type version struct { + name string + major, minor int +} + +var compiler struct { + sync.Once + version + err error +} + +// compilerVersion detects the version of $(go env CC). +// +// It returns a non-nil error if the compiler matches a known version schema but +// the version could not be parsed, or if $(go env CC) could not be determined. +func compilerVersion() (version, error) { + compiler.Once.Do(func() { + compiler.err = func() error { + compiler.name = "unknown" + + cmd, err := cc("--version") + if err != nil { + return err + } + out, err := cmd.Output() + if err != nil { + // Compiler does not support "--version" flag: not Clang or GCC. + return nil + } + + var match [][]byte + if bytes.HasPrefix(out, []byte("gcc")) { + compiler.name = "gcc" + cmd, err := cc("-dumpfullversion", "-dumpversion") + if err != nil { + return err + } + out, err := cmd.Output() + if err != nil { + // gcc, but does not support gcc's "-v" flag?! + return err + } + gccRE := regexp.MustCompile(`(\d+)\.(\d+)`) + match = gccRE.FindSubmatch(out) + } else { + clangRE := regexp.MustCompile(`clang version (\d+)\.(\d+)`) + if match = clangRE.FindSubmatch(out); len(match) > 0 { + compiler.name = "clang" + } + } + + if len(match) < 3 { + return nil // "unknown" + } + if compiler.major, err = strconv.Atoi(string(match[1])); err != nil { + return err + } + if compiler.minor, err = strconv.Atoi(string(match[2])); err != nil { + return err + } + return nil + }() + }) + return compiler.version, compiler.err +} + +// compilerSupportsLocation reports whether the compiler should be +// able to provide file/line information in backtraces. +func compilerSupportsLocation() bool { + compiler, err := compilerVersion() + if err != nil { + return false + } + switch compiler.name { + case "gcc": + return compiler.major >= 10 + case "clang": + return true + default: + return false + } +} + +// compilerRequiredTsanVersion reports whether the compiler is the version required by Tsan. +// Only restrictions for ppc64le are known; otherwise return true. +func compilerRequiredTsanVersion(goos, goarch string) bool { + compiler, err := compilerVersion() + if err != nil { + return false + } + if compiler.name == "gcc" && goarch == "ppc64le" { + return compiler.major >= 9 + } + return true +} + +// compilerRequiredAsanVersion reports whether the compiler is the version required by Asan. +func compilerRequiredAsanVersion(goos, goarch string) bool { + compiler, err := compilerVersion() + if err != nil { + return false + } + switch compiler.name { + case "gcc": + if goarch == "ppc64le" { + return compiler.major >= 9 + } + return compiler.major >= 7 + case "clang": + return compiler.major >= 9 + default: + return false + } +} + +type compilerCheck struct { + once sync.Once + err error + skip bool // If true, skip with err instead of failing with it. +} + +type config struct { + sanitizer string + + cFlags, ldFlags, goFlags []string + + sanitizerCheck, runtimeCheck compilerCheck +} + +var configs struct { + sync.Mutex + m map[string]*config +} + +// configure returns the configuration for the given sanitizer. +func configure(sanitizer string) *config { + configs.Lock() + defer configs.Unlock() + if c, ok := configs.m[sanitizer]; ok { + return c + } + + c := &config{ + sanitizer: sanitizer, + cFlags: []string{"-fsanitize=" + sanitizer}, + ldFlags: []string{"-fsanitize=" + sanitizer}, + } + + if testing.Verbose() { + c.goFlags = append(c.goFlags, "-x") + } + + switch sanitizer { + case "memory": + c.goFlags = append(c.goFlags, "-msan") + + case "thread": + c.goFlags = append(c.goFlags, "--installsuffix=tsan") + compiler, _ := compilerVersion() + if compiler.name == "gcc" { + c.cFlags = append(c.cFlags, "-fPIC") + c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan") + } + + case "address": + c.goFlags = append(c.goFlags, "-asan") + // Set the debug mode to print the C stack trace. + c.cFlags = append(c.cFlags, "-g") + + case "fuzzer": + c.goFlags = append(c.goFlags, "-tags=libfuzzer", "-gcflags=-d=libfuzzer") + + default: + panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer)) + } + + if configs.m == nil { + configs.m = make(map[string]*config) + } + configs.m[sanitizer] = c + return c +} + +// goCmd returns a Cmd that executes "go $subcommand $args" with appropriate +// additional flags and environment. +func (c *config) goCmd(subcommand string, args ...string) *exec.Cmd { + return c.goCmdWithExperiments(subcommand, args, nil) +} + +// goCmdWithExperiments returns a Cmd that executes +// "GOEXPERIMENT=$experiments go $subcommand $args" with appropriate +// additional flags and CGO-related environment variables. +func (c *config) goCmdWithExperiments(subcommand string, args []string, experiments []string) *exec.Cmd { + cmd := exec.Command("go", subcommand) + cmd.Args = append(cmd.Args, c.goFlags...) + cmd.Args = append(cmd.Args, args...) + replaceEnv(cmd, "CGO_CFLAGS", strings.Join(c.cFlags, " ")) + replaceEnv(cmd, "CGO_LDFLAGS", strings.Join(c.ldFlags, " ")) + appendExperimentEnv(cmd, experiments) + return cmd +} + +// skipIfCSanitizerBroken skips t if the C compiler does not produce working +// binaries as configured. +func (c *config) skipIfCSanitizerBroken(t *testing.T) { + check := &c.sanitizerCheck + check.once.Do(func() { + check.skip, check.err = c.checkCSanitizer() + }) + if check.err != nil { + t.Helper() + if check.skip { + t.Skip(check.err) + } + t.Fatal(check.err) + } +} + +var cMain = []byte(` +int main() { + return 0; +} +`) + +var cLibFuzzerInput = []byte(` +#include <stddef.h> +int LLVMFuzzerTestOneInput(char *data, size_t size) { + return 0; +} +`) + +func (c *config) checkCSanitizer() (skip bool, err error) { + dir, err := os.MkdirTemp("", c.sanitizer) + if err != nil { + return false, fmt.Errorf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + src := filepath.Join(dir, "return0.c") + cInput := cMain + if c.sanitizer == "fuzzer" { + // libFuzzer generates the main function itself, and uses a different input. + cInput = cLibFuzzerInput + } + if err := os.WriteFile(src, cInput, 0600); err != nil { + return false, fmt.Errorf("failed to write C source file: %v", err) + } + + dst := filepath.Join(dir, "return0") + cmd, err := cc(c.cFlags...) + if err != nil { + return false, err + } + cmd.Args = append(cmd.Args, c.ldFlags...) + cmd.Args = append(cmd.Args, "-o", dst, src) + out, err := cmd.CombinedOutput() + if err != nil { + if bytes.Contains(out, []byte("-fsanitize")) && + (bytes.Contains(out, []byte("unrecognized")) || + bytes.Contains(out, []byte("unsupported"))) { + return true, errors.New(string(out)) + } + return true, fmt.Errorf("%#q failed: %v\n%s", strings.Join(cmd.Args, " "), err, out) + } + + if c.sanitizer == "fuzzer" { + // For fuzzer, don't try running the test binary. It never finishes. + return false, nil + } + + if out, err := exec.Command(dst).CombinedOutput(); err != nil { + if os.IsNotExist(err) { + return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err) + } + snippet, _, _ := bytes.Cut(out, []byte("\n")) + return true, fmt.Errorf("%#q generated broken executable: %v\n%s", strings.Join(cmd.Args, " "), err, snippet) + } + + return false, nil +} + +// skipIfRuntimeIncompatible skips t if the Go runtime is suspected not to work +// with cgo as configured. +func (c *config) skipIfRuntimeIncompatible(t *testing.T) { + check := &c.runtimeCheck + check.once.Do(func() { + check.skip, check.err = c.checkRuntime() + }) + if check.err != nil { + t.Helper() + if check.skip { + t.Skip(check.err) + } + t.Fatal(check.err) + } +} + +func (c *config) checkRuntime() (skip bool, err error) { + if c.sanitizer != "thread" { + return false, nil + } + + // libcgo.h sets CGO_TSAN if it detects TSAN support in the C compiler. + // Dump the preprocessor defines to check that works. + // (Sometimes it doesn't: see https://golang.org/issue/15983.) + cmd, err := cc(c.cFlags...) + if err != nil { + return false, err + } + cmd.Args = append(cmd.Args, "-dM", "-E", "../../../src/runtime/cgo/libcgo.h") + cmdStr := strings.Join(cmd.Args, " ") + out, err := cmd.CombinedOutput() + if err != nil { + return false, fmt.Errorf("%#q exited with %v\n%s", cmdStr, err, out) + } + if !bytes.Contains(out, []byte("#define CGO_TSAN")) { + return true, fmt.Errorf("%#q did not define CGO_TSAN", cmdStr) + } + return false, nil +} + +// srcPath returns the path to the given file relative to this test's source tree. +func srcPath(path string) string { + return filepath.Join("testdata", path) +} + +// A tempDir manages a temporary directory within a test. +type tempDir struct { + base string +} + +func (d *tempDir) RemoveAll(t *testing.T) { + t.Helper() + if d.base == "" { + return + } + if err := os.RemoveAll(d.base); err != nil { + t.Fatalf("Failed to remove temp dir: %v", err) + } +} + +func (d *tempDir) Base() string { + return d.base +} + +func (d *tempDir) Join(name string) string { + return filepath.Join(d.base, name) +} + +func newTempDir(t *testing.T) *tempDir { + t.Helper() + dir, err := os.MkdirTemp("", filepath.Dir(t.Name())) + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + return &tempDir{base: dir} +} + +// hangProneCmd returns an exec.Cmd for a command that is likely to hang. +// +// If one of these tests hangs, the caller is likely to kill the test process +// using SIGINT, which will be sent to all of the processes in the test's group. +// Unfortunately, TSAN in particular is prone to dropping signals, so the SIGINT +// may terminate the test binary but leave the subprocess running. hangProneCmd +// configures subprocess to receive SIGKILL instead to ensure that it won't +// leak. +func hangProneCmd(name string, arg ...string) *exec.Cmd { + cmd := exec.Command(name, arg...) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Pdeathsig: syscall.SIGKILL, + } + return cmd +} + +// mSanSupported is a copy of the function cmd/internal/sys.MSanSupported, +// because the internal package can't be used here. +func mSanSupported(goos, goarch string) bool { + switch goos { + case "linux": + return goarch == "amd64" || goarch == "arm64" + case "freebsd": + return goarch == "amd64" + default: + return false + } +} + +// aSanSupported is a copy of the function cmd/internal/sys.ASanSupported, +// because the internal package can't be used here. +func aSanSupported(goos, goarch string) bool { + switch goos { + case "linux": + return goarch == "amd64" || goarch == "arm64" || goarch == "riscv64" || goarch == "ppc64le" + default: + return false + } +} diff --git a/misc/cgo/testsanitizers/cshared_test.go b/misc/cgo/testsanitizers/cshared_test.go new file mode 100644 index 0000000..21b13ce --- /dev/null +++ b/misc/cgo/testsanitizers/cshared_test.go @@ -0,0 +1,90 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sanitizers_test + +import ( + "fmt" + "os" + "strings" + "testing" +) + +func TestShared(t *testing.T) { + t.Parallel() + requireOvercommit(t) + + GOOS, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + + GOARCH, err := goEnv("GOARCH") + if err != nil { + t.Fatal(err) + } + + libExt := "so" + if GOOS == "darwin" { + libExt = "dylib" + } + + cases := []struct { + src string + sanitizer string + }{ + { + src: "msan_shared.go", + sanitizer: "memory", + }, + { + src: "tsan_shared.go", + sanitizer: "thread", + }, + } + + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.src, ".go") + //The memory sanitizer tests require support for the -msan option. + if tc.sanitizer == "memory" && !mSanSupported(GOOS, GOARCH) { + t.Logf("skipping %s test on %s/%s; -msan option is not supported.", name, GOOS, GOARCH) + continue + } + if tc.sanitizer == "thread" && !compilerRequiredTsanVersion(GOOS, GOARCH) { + t.Logf("skipping %s test on %s/%s; compiler version too old for -tsan.", name, GOOS, GOARCH) + continue + } + + t.Run(name, func(t *testing.T) { + t.Parallel() + config := configure(tc.sanitizer) + config.skipIfCSanitizerBroken(t) + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + lib := dir.Join(fmt.Sprintf("lib%s.%s", name, libExt)) + mustRun(t, config.goCmd("build", "-buildmode=c-shared", "-o", lib, srcPath(tc.src))) + + cSrc := dir.Join("main.c") + if err := os.WriteFile(cSrc, cMain, 0600); err != nil { + t.Fatalf("failed to write C source file: %v", err) + } + + dstBin := dir.Join(name) + cmd, err := cc(config.cFlags...) + if err != nil { + t.Fatal(err) + } + cmd.Args = append(cmd.Args, config.ldFlags...) + cmd.Args = append(cmd.Args, "-o", dstBin, cSrc, lib) + mustRun(t, cmd) + + cmd = hangProneCmd(dstBin) + replaceEnv(cmd, "LD_LIBRARY_PATH", ".") + mustRun(t, cmd) + }) + } +} diff --git a/misc/cgo/testsanitizers/libfuzzer_test.go b/misc/cgo/testsanitizers/libfuzzer_test.go new file mode 100644 index 0000000..345751b --- /dev/null +++ b/misc/cgo/testsanitizers/libfuzzer_test.go @@ -0,0 +1,91 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sanitizers_test + +import ( + "strings" + "testing" +) + +func TestLibFuzzer(t *testing.T) { + goos, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + goarch, err := goEnv("GOARCH") + if err != nil { + t.Fatal(err) + } + if !libFuzzerSupported(goos, goarch) { + t.Skipf("skipping on %s/%s; libfuzzer option is not supported.", goos, goarch) + } + config := configure("fuzzer") + config.skipIfCSanitizerBroken(t) + + cases := []struct { + goSrc string + cSrc string + expectedError string + }{ + {goSrc: "libfuzzer1.go", expectedError: "panic: found it"}, + {goSrc: "libfuzzer2.go", cSrc: "libfuzzer2.c", expectedError: "panic: found it"}, + } + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.goSrc, ".go") + t.Run(name, func(t *testing.T) { + t.Parallel() + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + // build Go code in libfuzzer mode to a c-archive + outPath := dir.Join(name) + archivePath := dir.Join(name + ".a") + mustRun(t, config.goCmd("build", "-buildmode=c-archive", "-o", archivePath, srcPath(tc.goSrc))) + + // build C code (if any) and link with Go code + cmd, err := cc(config.cFlags...) + if err != nil { + t.Fatalf("error running cc: %v", err) + } + cmd.Args = append(cmd.Args, config.ldFlags...) + cmd.Args = append(cmd.Args, "-o", outPath, "-I", dir.Base()) + if tc.cSrc != "" { + cmd.Args = append(cmd.Args, srcPath(tc.cSrc)) + } + cmd.Args = append(cmd.Args, archivePath) + mustRun(t, cmd) + + cmd = hangProneCmd(outPath) + cmd.Dir = dir.Base() + outb, err := cmd.CombinedOutput() + out := string(outb) + if err == nil { + t.Fatalf("fuzzing succeeded unexpectedly; output:\n%s", out) + } + if !strings.Contains(out, tc.expectedError) { + t.Errorf("exited without expected error %q; got\n%s", tc.expectedError, out) + } + }) + } +} + +// libFuzzerSupported is a copy of the function internal/platform.FuzzInstrumented, +// because the internal package can't be used here. +func libFuzzerSupported(goos, goarch string) bool { + switch goarch { + case "amd64", "arm64": + // TODO(#14565): support more architectures. + switch goos { + case "darwin", "freebsd", "linux", "windows": + return true + default: + return false + } + default: + return false + } +} diff --git a/misc/cgo/testsanitizers/msan_test.go b/misc/cgo/testsanitizers/msan_test.go new file mode 100644 index 0000000..96397e0 --- /dev/null +++ b/misc/cgo/testsanitizers/msan_test.go @@ -0,0 +1,81 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sanitizers_test + +import ( + "strings" + "testing" +) + +func TestMSAN(t *testing.T) { + goos, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + goarch, err := goEnv("GOARCH") + if err != nil { + t.Fatal(err) + } + // The msan tests require support for the -msan option. + if !mSanSupported(goos, goarch) { + t.Skipf("skipping on %s/%s; -msan option is not supported.", goos, goarch) + } + + t.Parallel() + // Overcommit is enabled by default on FreeBSD (vm.overcommit=0, see tuning(7)). + // Do not skip tests with stricter overcommit settings unless testing shows that FreeBSD has similar issues. + if goos == "linux" { + requireOvercommit(t) + } + config := configure("memory") + config.skipIfCSanitizerBroken(t) + + mustRun(t, config.goCmd("build", "std")) + + cases := []struct { + src string + wantErr bool + experiments []string + }{ + {src: "msan.go"}, + {src: "msan2.go"}, + {src: "msan2_cmsan.go"}, + {src: "msan3.go"}, + {src: "msan4.go"}, + {src: "msan5.go"}, + {src: "msan6.go"}, + {src: "msan7.go"}, + {src: "msan8.go"}, + {src: "msan_fail.go", wantErr: true}, + // This may not always fail specifically due to MSAN. It may sometimes + // fail because of a fault. However, we don't care what kind of error we + // get here, just that we get an error. This is an MSAN test because without + // MSAN it would not fail deterministically. + {src: "arena_fail.go", wantErr: true, experiments: []string{"arenas"}}, + } + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.src, ".go") + t.Run(name, func(t *testing.T) { + t.Parallel() + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + outPath := dir.Join(name) + mustRun(t, config.goCmdWithExperiments("build", []string{"-o", outPath, srcPath(tc.src)}, tc.experiments)) + + cmd := hangProneCmd(outPath) + if tc.wantErr { + out, err := cmd.CombinedOutput() + if err != nil { + return + } + t.Fatalf("%#q exited without error; want MSAN failure\n%s", strings.Join(cmd.Args, " "), out) + } + mustRun(t, cmd) + }) + } +} diff --git a/misc/cgo/testsanitizers/testdata/arena_fail.go b/misc/cgo/testsanitizers/testdata/arena_fail.go new file mode 100644 index 0000000..5b6c52e --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/arena_fail.go @@ -0,0 +1,27 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build goexperiment.arenas + +package main + +import "arena" + +func main() { + a := arena.NewArena() + x := arena.New[[200]byte](a) + x[0] = 9 + a.Free() + // Use after free. + // + // ASAN should detect this deterministically as Free + // should poison the arena memory. + // + // MSAN should detect that this access is to freed + // memory. This may crash with an "accessed freed arena + // memory" error before MSAN gets a chance, but if MSAN + // was not enabled there would be a chance that this + // could fail to crash on its own. + println(x[0]) +} diff --git a/misc/cgo/testsanitizers/testdata/asan1_fail.go b/misc/cgo/testsanitizers/testdata/asan1_fail.go new file mode 100644 index 0000000..80289e5 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan1_fail.go @@ -0,0 +1,28 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <stdlib.h> +#include <stdio.h> + +int *p; +int* test() { + p = (int *)malloc(2 * sizeof(int)); + free(p); + return p; +} +*/ +import "C" +import "fmt" + +func main() { + // C passes Go an invalid pointer. + a := C.test() + // Use after free + *a = 2 // BOOM + // We shouldn't get here; asan should stop us first. + fmt.Println(*a) +} diff --git a/misc/cgo/testsanitizers/testdata/asan2_fail.go b/misc/cgo/testsanitizers/testdata/asan2_fail.go new file mode 100644 index 0000000..3ab0608 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan2_fail.go @@ -0,0 +1,34 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <stdlib.h> +#include <stdio.h> + +int *p; +int* f() { + int i; + p = (int *)malloc(5*sizeof(int)); + for (i = 0; i < 5; i++) { + p[i] = i+10; + } + return p; +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +func main() { + a := C.f() + q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5)) + // Access to C pointer out of bounds. + *q5 = 100 // BOOM + // We shouldn't get here; asan should stop us first. + fmt.Printf("q5: %d, %x\n", *q5, q5) +} diff --git a/misc/cgo/testsanitizers/testdata/asan3_fail.go b/misc/cgo/testsanitizers/testdata/asan3_fail.go new file mode 100644 index 0000000..9f6d26d --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan3_fail.go @@ -0,0 +1,23 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <stdlib.h> +#include <stdio.h> + +void test(int *a) { + // Access Go pointer out of bounds. + int c = a[5]; // BOOM + // We shouldn't get here; asan should stop us first. + printf("a[5]=%d\n", c); +} +*/ +import "C" + +func main() { + cIntSlice := []C.int{200, 201, 203, 203, 204} + C.test(&cIntSlice[0]) +} diff --git a/misc/cgo/testsanitizers/testdata/asan4_fail.go b/misc/cgo/testsanitizers/testdata/asan4_fail.go new file mode 100644 index 0000000..1209845 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan4_fail.go @@ -0,0 +1,22 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <stdlib.h> +#include <stdio.h> + +void test(int* a) { + // Access Go pointer out of bounds. + a[3] = 300; // BOOM + // We shouldn't get here; asan should stop us first. + printf("a[3]=%d\n", a[3]); +}*/ +import "C" + +func main() { + var cIntArray [2]C.int + C.test(&cIntArray[0]) // cIntArray is moved to heap. +} diff --git a/misc/cgo/testsanitizers/testdata/asan5_fail.go b/misc/cgo/testsanitizers/testdata/asan5_fail.go new file mode 100644 index 0000000..d6853ea --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan5_fail.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "runtime" + "unsafe" +) + +func main() { + p := new([1024 * 1000]int) + p[0] = 10 + r := bar(&p[1024*1000-1]) + fmt.Printf("r value is %d", r) +} + +func bar(a *int) int { + p := unsafe.Add(unsafe.Pointer(a), 2*unsafe.Sizeof(int(1))) + runtime.ASanWrite(p, 8) // BOOM + *((*int)(p)) = 10 + return *((*int)(p)) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global1_fail.go b/misc/cgo/testsanitizers/testdata/asan_global1_fail.go new file mode 100644 index 0000000..6cfc0b7 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global1_fail.go @@ -0,0 +1,25 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <stdlib.h> +#include <stdio.h> + +int test(int *a) { + a[2] = 300; // BOOM + return a[2]; +} +*/ +import "C" + +import "fmt" + +var cIntArray [2]C.int + +func main() { + r := C.test(&cIntArray[0]) + fmt.Println("r value = ", r) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global2_fail.go b/misc/cgo/testsanitizers/testdata/asan_global2_fail.go new file mode 100644 index 0000000..1932633 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global2_fail.go @@ -0,0 +1,31 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <stdlib.h> +#include <stdio.h> + +struct ss { + int *p; + int len; + int cap; +}; + +int test(struct ss *a) { + struct ss *t = a + 1; + t->len = 100; // BOOM + return t->len; +} +*/ +import "C" +import "fmt" + +var tt C.struct_ss + +func main() { + r := C.test(&tt) + fmt.Println("r value = ", r) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global3_fail.go b/misc/cgo/testsanitizers/testdata/asan_global3_fail.go new file mode 100644 index 0000000..9ab026c --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global3_fail.go @@ -0,0 +1,28 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <stdlib.h> +#include <stdio.h> + +int test(int *a) { + int* p = a+1; + *p = 10; // BOOM + return *p; +} +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +var cIntV C.int + +func main() { + r := C.test((*C.int)(unsafe.Pointer(&cIntV))) + fmt.Printf("r value is %d", r) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global4_fail.go b/misc/cgo/testsanitizers/testdata/asan_global4_fail.go new file mode 100644 index 0000000..d593598 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global4_fail.go @@ -0,0 +1,25 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +var intGlo int + +func main() { + r := bar(&intGlo) + fmt.Printf("r value is %d", r) +} + +func bar(a *int) int { + p := (*int)(unsafe.Add(unsafe.Pointer(a), 1*unsafe.Sizeof(int(1)))) + if *p == 10 { // BOOM + fmt.Println("its value is 10") + } + return *p +} diff --git a/misc/cgo/testsanitizers/testdata/asan_global5.go b/misc/cgo/testsanitizers/testdata/asan_global5.go new file mode 100644 index 0000000..0ed103d --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_global5.go @@ -0,0 +1,22 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" +) + +type Any struct { + s string + b int64 +} + +var Sg = []interface{}{ + Any{"a", 10}, +} + +func main() { + fmt.Println(Sg[0]) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_linkerx/main.go b/misc/cgo/testsanitizers/testdata/asan_linkerx/main.go new file mode 100644 index 0000000..bbd6127 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_linkerx/main.go @@ -0,0 +1,28 @@ +package main + +import "misc/cgo/testsanitizers/testdata/asan_linkerx/p" + +func pstring(s *string) { + println(*s) +} + +func main() { + all := []*string{ + &S1, &S2, &S3, &S4, &S5, &S6, &S7, &S8, &S9, &S10, + &p.S1, &p.S2, &p.S3, &p.S4, &p.S5, &p.S6, &p.S7, &p.S8, &p.S9, &p.S10, + } + for _, ps := range all { + pstring(ps) + } +} + +var S1 string +var S2 string +var S3 string +var S4 string +var S5 string +var S6 string +var S7 string +var S8 string +var S9 string +var S10 string diff --git a/misc/cgo/testsanitizers/testdata/asan_linkerx/p/p.go b/misc/cgo/testsanitizers/testdata/asan_linkerx/p/p.go new file mode 100644 index 0000000..c31f001 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_linkerx/p/p.go @@ -0,0 +1,12 @@ +package p + +var S1 string +var S2 string +var S3 string +var S4 string +var S5 string +var S6 string +var S7 string +var S8 string +var S9 string +var S10 string diff --git a/misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go new file mode 100644 index 0000000..ec54a66 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail1.go @@ -0,0 +1,27 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +func main() { + a := 1 + b := 2 + c := add(a, b) + d := a + b + fmt.Println(c, d) +} + +//go:noinline +func add(a1, b1 int) int { + // The arguments. + // When -asan is enabled, unsafe.Pointer(&a1) conversion is escaping. + var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a1), 1*unsafe.Sizeof(int(1)))) + *p = 10 // BOOM + return a1 + b1 +} diff --git a/misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go new file mode 100644 index 0000000..70f2127 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail2.go @@ -0,0 +1,28 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +func main() { + a := 1 + b := 2 + c := add(a, b) + d := a + b + fmt.Println(c, d) +} + +//go:noinline +func add(a1, b1 int) (ret int) { + // The return value + // When -asan is enabled, the unsafe.Pointer(&ret) conversion is escaping. + var p *int = (*int)(unsafe.Add(unsafe.Pointer(&ret), 1*unsafe.Sizeof(int(1)))) + *p = 123 // BOOM + ret = a1 + b1 + return +} diff --git a/misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go new file mode 100644 index 0000000..47a8a07 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_unsafe_fail3.go @@ -0,0 +1,21 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "unsafe" +) + +func main() { + a := 1 + b := 2 + // The local variables. + // When -asan is enabled, the unsafe.Pointer(&a) conversion is escaping. + var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a), 1*unsafe.Sizeof(int(1)))) + *p = 20 // BOOM + d := a + b + fmt.Println(d) +} diff --git a/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go b/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go new file mode 100644 index 0000000..3d3d5a6 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/asan_useAfterReturn.go @@ -0,0 +1,26 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// The -fsanitize=address option of C compier can detect stack-use-after-return bugs. +// In the following program, the local variable 'local' was moved to heap by the Go +// compiler because foo() is returning the reference to 'local', and return stack of +// foo() will be invalid. Thus for main() to use the reference to 'local', the 'local' +// must be available even after foo() has finished. Therefore, Go has no such issue. + +import "fmt" + +var ptr *int + +func main() { + foo() + fmt.Printf("ptr=%x, %v", *ptr, ptr) +} + +func foo() { + var local int + local = 1 + ptr = &local // local is moved to heap. +} diff --git a/misc/cgo/testsanitizers/testdata/libfuzzer1.go b/misc/cgo/testsanitizers/testdata/libfuzzer1.go new file mode 100644 index 0000000..d178fb1 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/libfuzzer1.go @@ -0,0 +1,16 @@ +package main + +import "C" + +import "unsafe" + +//export LLVMFuzzerTestOneInput +func LLVMFuzzerTestOneInput(p unsafe.Pointer, sz C.int) C.int { + b := C.GoBytes(p, sz) + if len(b) >= 6 && b[0] == 'F' && b[1] == 'u' && b[2] == 'z' && b[3] == 'z' && b[4] == 'M' && b[5] == 'e' { + panic("found it") + } + return 0 +} + +func main() {} diff --git a/misc/cgo/testsanitizers/testdata/libfuzzer2.c b/misc/cgo/testsanitizers/testdata/libfuzzer2.c new file mode 100644 index 0000000..567ff5a --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/libfuzzer2.c @@ -0,0 +1,11 @@ +#include <stddef.h> + +#include "libfuzzer2.h" + +int LLVMFuzzerTestOneInput(char *data, size_t size) { + if (size > 0 && data[0] == 'H') + if (size > 1 && data[1] == 'I') + if (size > 2 && data[2] == '!') + FuzzMe(data, size); + return 0; +} diff --git a/misc/cgo/testsanitizers/testdata/libfuzzer2.go b/misc/cgo/testsanitizers/testdata/libfuzzer2.go new file mode 100644 index 0000000..c7a4325 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/libfuzzer2.go @@ -0,0 +1,16 @@ +package main + +import "C" + +import "unsafe" + +//export FuzzMe +func FuzzMe(p unsafe.Pointer, sz C.int) { + b := C.GoBytes(p, sz) + b = b[3:] + if len(b) >= 4 && b[0] == 'f' && b[1] == 'u' && b[2] == 'z' && b[3] == 'z' { + panic("found it") + } +} + +func main() {} diff --git a/misc/cgo/testsanitizers/testdata/msan.go b/misc/cgo/testsanitizers/testdata/msan.go new file mode 100644 index 0000000..7915fa8 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan.go @@ -0,0 +1,35 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <stdint.h> + +void f(int32_t *p, int n) { + int i; + + for (i = 0; i < n; i++) { + p[i] = (int32_t)i; + } +} +*/ +import "C" + +import ( + "fmt" + "os" + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + for i, v := range a { + if i != int(v) { + fmt.Println("bad %d: %v\n", i, a) + os.Exit(1) + } + } +} diff --git a/misc/cgo/testsanitizers/testdata/msan2.go b/misc/cgo/testsanitizers/testdata/msan2.go new file mode 100644 index 0000000..6690cb0 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan2.go @@ -0,0 +1,35 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +void f(int32_t *p, int n) { + int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n); + memcpy(p, q, n * sizeof(*p)); + free(q); +} + +void g(int32_t *p, int n) { + if (p[4] != 1) { + abort(); + } +} +*/ +import "C" + +import ( + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + a[4] = 1 + C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) +} diff --git a/misc/cgo/testsanitizers/testdata/msan2_cmsan.go b/misc/cgo/testsanitizers/testdata/msan2_cmsan.go new file mode 100644 index 0000000..8fdaea9 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan2_cmsan.go @@ -0,0 +1,38 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#cgo LDFLAGS: -fsanitize=memory +#cgo CPPFLAGS: -fsanitize=memory + +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +void f(int32_t *p, int n) { + int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n); + memcpy(p, q, n * sizeof(*p)); + free(q); +} + +void g(int32_t *p, int n) { + if (p[4] != 1) { + abort(); + } +} +*/ +import "C" + +import ( + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + a[4] = 1 + C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) +} diff --git a/misc/cgo/testsanitizers/testdata/msan3.go b/misc/cgo/testsanitizers/testdata/msan3.go new file mode 100644 index 0000000..61a9c29 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan3.go @@ -0,0 +1,33 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +extern int *GoFn(int *); + +// Yes, you can have definitions if you use //export, as long as they are weak. +int f(void) __attribute__ ((weak)); + +int f() { + int i; + int *p = GoFn(&i); + if (*p != 12345) + return 0; + return 1; +} +*/ +import "C" + +//export GoFn +func GoFn(p *C.int) *C.int { + *p = C.int(12345) + return p +} + +func main() { + if r := C.f(); r != 1 { + panic(r) + } +} diff --git a/misc/cgo/testsanitizers/testdata/msan4.go b/misc/cgo/testsanitizers/testdata/msan4.go new file mode 100644 index 0000000..6c91ff5 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan4.go @@ -0,0 +1,50 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// The memory profiler can call copy from a slice on the system stack, +// which msan used to think meant a reference to uninitialized memory. + +/* +#include <time.h> +#include <unistd.h> + +extern void Nop(char*); + +// Use weak as a hack to permit defining a function even though we use export. +void poison() __attribute__ ((weak)); + +// Poison the stack. +void poison() { + char a[1024]; + Nop(&a[0]); +} + +*/ +import "C" + +import ( + "runtime" +) + +func main() { + runtime.MemProfileRate = 1 + start(100) +} + +func start(i int) { + if i == 0 { + return + } + C.poison() + // Tie up a thread. + // We won't actually wait for this sleep to complete. + go func() { C.sleep(1) }() + start(i - 1) +} + +//export Nop +func Nop(*C.char) { +} diff --git a/misc/cgo/testsanitizers/testdata/msan5.go b/misc/cgo/testsanitizers/testdata/msan5.go new file mode 100644 index 0000000..f1479eb --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan5.go @@ -0,0 +1,57 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// Using reflect to set a value was not seen by msan. + +/* +#include <stdlib.h> + +extern void Go1(int*); +extern void Go2(char*); + +// Use weak as a hack to permit defining a function even though we use export. +void C1() __attribute__ ((weak)); +void C2() __attribute__ ((weak)); + +void C1() { + int i; + Go1(&i); + if (i != 42) { + abort(); + } +} + +void C2() { + char a[2]; + a[1] = 42; + Go2(a); + if (a[0] != 42) { + abort(); + } +} +*/ +import "C" + +import ( + "reflect" + "unsafe" +) + +//export Go1 +func Go1(p *C.int) { + reflect.ValueOf(p).Elem().Set(reflect.ValueOf(C.int(42))) +} + +//export Go2 +func Go2(p *C.char) { + a := (*[2]byte)(unsafe.Pointer(p)) + reflect.Copy(reflect.ValueOf(a[:1]), reflect.ValueOf(a[1:])) +} + +func main() { + C.C1() + C.C2() +} diff --git a/misc/cgo/testsanitizers/testdata/msan6.go b/misc/cgo/testsanitizers/testdata/msan6.go new file mode 100644 index 0000000..003989c --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan6.go @@ -0,0 +1,72 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// A C function returning a value on the Go stack could leave the Go +// stack marked as uninitialized, potentially causing a later error +// when the stack is used for something else. Issue 26209. + +/* +#cgo LDFLAGS: -fsanitize=memory +#cgo CPPFLAGS: -fsanitize=memory + +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +typedef struct { + uintptr_t a[20]; +} S; + +S f() { + S *p; + + p = (S *)(malloc(sizeof(S))); + p->a[0] = 0; + return *p; +} +*/ +import "C" + +// allocateStack extends the stack so that stack copying doesn't +// confuse the msan data structures. +//go:noinline +func allocateStack(i int) int { + if i == 0 { + return i + } + return allocateStack(i - 1) +} + +// F1 marks a chunk of stack as uninitialized. +// C.f returns an uninitialized struct on the stack, so msan will mark +// the stack as uninitialized. +//go:noinline +func F1() uintptr { + s := C.f() + return uintptr(s.a[0]) +} + +// F2 allocates a struct on the stack and converts it to an empty interface, +// which will call msanread and see that the data appears uninitialized. +//go:noinline +func F2() interface{} { + return C.S{} +} + +func poisonStack(i int) int { + if i == 0 { + return int(F1()) + } + F1() + r := poisonStack(i - 1) + F2() + return r +} + +func main() { + allocateStack(16384) + poisonStack(128) +} diff --git a/misc/cgo/testsanitizers/testdata/msan7.go b/misc/cgo/testsanitizers/testdata/msan7.go new file mode 100644 index 0000000..2f29fd2 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan7.go @@ -0,0 +1,38 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// Test passing C struct to exported Go function. + +/* +#include <stdint.h> +#include <stdlib.h> + +// T is a C struct with alignment padding after b. +// The padding bytes are not considered initialized by MSAN. +// It is big enough to be passed on stack in C ABI (and least +// on AMD64). +typedef struct { char b; uintptr_t x, y; } T; + +extern void F(T); + +// Use weak as a hack to permit defining a function even though we use export. +void CF(int x) __attribute__ ((weak)); +void CF(int x) { + T *t = malloc(sizeof(T)); + t->b = (char)x; + t->x = x; + t->y = x; + F(*t); +} +*/ +import "C" + +//export F +func F(t C.T) { println(t.b, t.x, t.y) } + +func main() { + C.CF(C.int(0)) +} diff --git a/misc/cgo/testsanitizers/testdata/msan8.go b/misc/cgo/testsanitizers/testdata/msan8.go new file mode 100644 index 0000000..1cb5c56 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan8.go @@ -0,0 +1,109 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <pthread.h> +#include <signal.h> +#include <stdint.h> + +#include <sanitizer/msan_interface.h> + +// cgoTracebackArg is the type of the argument passed to msanGoTraceback. +struct cgoTracebackArg { + uintptr_t context; + uintptr_t sigContext; + uintptr_t* buf; + uintptr_t max; +}; + +// msanGoTraceback is registered as the cgo traceback function. +// This will be called when a signal occurs. +void msanGoTraceback(void* parg) { + struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg); + arg->buf[0] = 0; +} + +// msanGoWait will be called with all registers undefined as far as +// msan is concerned. It just waits for a signal. +// Because the registers are msan-undefined, the signal handler will +// be invoked with all registers msan-undefined. +__attribute__((noinline)) +void msanGoWait(unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6) { + sigset_t mask; + + sigemptyset(&mask); + sigsuspend(&mask); +} + +// msanGoSignalThread is the thread ID of the msanGoLoop thread. +static pthread_t msanGoSignalThread; + +// msanGoSignalThreadSet is used to record that msanGoSignalThread +// has been initialized. This is accessed atomically. +static int32_t msanGoSignalThreadSet; + +// uninit is explicitly poisoned, so that we can make all registers +// undefined by calling msanGoWait. +static unsigned long uninit; + +// msanGoLoop loops calling msanGoWait, with the arguments passed +// such that msan thinks that they are undefined. msan permits +// undefined values to be used as long as they are not used to +// for conditionals or for memory access. +void msanGoLoop() { + int i; + + msanGoSignalThread = pthread_self(); + __atomic_store_n(&msanGoSignalThreadSet, 1, __ATOMIC_SEQ_CST); + + // Force uninit to be undefined for msan. + __msan_poison(&uninit, sizeof uninit); + for (i = 0; i < 100; i++) { + msanGoWait(uninit, uninit, uninit, uninit, uninit, uninit); + } +} + +// msanGoReady returns whether msanGoSignalThread is set. +int msanGoReady() { + return __atomic_load_n(&msanGoSignalThreadSet, __ATOMIC_SEQ_CST) != 0; +} + +// msanGoSendSignal sends a signal to the msanGoLoop thread. +void msanGoSendSignal() { + pthread_kill(msanGoSignalThread, SIGWINCH); +} +*/ +import "C" + +import ( + "runtime" + "time" +) + +func main() { + runtime.SetCgoTraceback(0, C.msanGoTraceback, nil, nil) + + c := make(chan bool) + go func() { + defer func() { c <- true }() + C.msanGoLoop() + }() + + for C.msanGoReady() == 0 { + time.Sleep(time.Microsecond) + } + +loop: + for { + select { + case <-c: + break loop + default: + C.msanGoSendSignal() + time.Sleep(time.Microsecond) + } + } +} diff --git a/misc/cgo/testsanitizers/testdata/msan_fail.go b/misc/cgo/testsanitizers/testdata/msan_fail.go new file mode 100644 index 0000000..4c8dab3 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan_fail.go @@ -0,0 +1,36 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +/* +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +void f(int32_t *p, int n) { + int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n); + memcpy(p, q, n * sizeof(*p)); + free(q); +} + +void g(int32_t *p, int n) { + if (p[4] != 1) { + // We shouldn't get here; msan should stop us first. + exit(0); + } +} +*/ +import "C" + +import ( + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + a[3] = 1 + C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) +} diff --git a/misc/cgo/testsanitizers/testdata/msan_shared.go b/misc/cgo/testsanitizers/testdata/msan_shared.go new file mode 100644 index 0000000..966947c --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/msan_shared.go @@ -0,0 +1,12 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This program segfaulted during libpreinit when built with -msan: +// http://golang.org/issue/18707 + +package main + +import "C" + +func main() {} diff --git a/misc/cgo/testsanitizers/testdata/tsan.go b/misc/cgo/testsanitizers/testdata/tsan.go new file mode 100644 index 0000000..6c377a7 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan.go @@ -0,0 +1,44 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program produced false race reports when run under the C/C++ +// ThreadSanitizer, as it did not understand the synchronization in +// the Go code. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +int val; + +int getVal() { + return val; +} + +void setVal(int i) { + val = i; +} +*/ +import "C" + +import ( + "runtime" +) + +func main() { + runtime.LockOSThread() + C.setVal(1) + c := make(chan bool) + go func() { + runtime.LockOSThread() + C.setVal(2) + c <- true + }() + <-c + if v := C.getVal(); v != 2 { + panic(v) + } +} diff --git a/misc/cgo/testsanitizers/testdata/tsan10.go b/misc/cgo/testsanitizers/testdata/tsan10.go new file mode 100644 index 0000000..a40f245 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan10.go @@ -0,0 +1,31 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program hung when run under the C/C++ ThreadSanitizer. +// TSAN defers asynchronous signals until the signaled thread calls into libc. +// Since the Go runtime makes direct futex syscalls, Go runtime threads could +// run for an arbitrarily long time without triggering the libc interceptors. +// See https://golang.org/issue/18717. + +import ( + "os" + "os/signal" + "syscall" +) + +/* +#cgo CFLAGS: -g -fsanitize=thread +#cgo LDFLAGS: -g -fsanitize=thread +*/ +import "C" + +func main() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGUSR1) + defer signal.Stop(c) + syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) + <-c +} diff --git a/misc/cgo/testsanitizers/testdata/tsan11.go b/misc/cgo/testsanitizers/testdata/tsan11.go new file mode 100644 index 0000000..189e10f --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan11.go @@ -0,0 +1,55 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program hung when run under the C/C++ ThreadSanitizer. TSAN defers +// asynchronous signals until the signaled thread calls into libc. The runtime's +// sysmon goroutine idles itself using direct usleep syscalls, so it could +// run for an arbitrarily long time without triggering the libc interceptors. +// See https://golang.org/issue/18717. + +import ( + "os" + "os/signal" + "syscall" +) + +/* +#cgo CFLAGS: -g -fsanitize=thread +#cgo LDFLAGS: -g -fsanitize=thread + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static void raise_usr2(int signo) { + raise(SIGUSR2); +} + +static void register_handler(int signo) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_ONSTACK; + sa.sa_handler = raise_usr2; + + if (sigaction(SIGUSR1, &sa, NULL) != 0) { + perror("failed to register SIGUSR1 handler"); + exit(EXIT_FAILURE); + } +} +*/ +import "C" + +func main() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGUSR2) + + C.register_handler(C.int(syscall.SIGUSR1)) + syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) + + <-ch +} diff --git a/misc/cgo/testsanitizers/testdata/tsan12.go b/misc/cgo/testsanitizers/testdata/tsan12.go new file mode 100644 index 0000000..0ef545d --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan12.go @@ -0,0 +1,35 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program hung when run under the C/C++ ThreadSanitizer. TSAN installs a +// libc interceptor that writes signal handlers to a global variable within the +// TSAN runtime instead of making a sigaction system call. A bug in +// syscall.runtime_AfterForkInChild corrupted TSAN's signal forwarding table +// during calls to (*os/exec.Cmd).Run, causing the parent process to fail to +// invoke signal handlers. + +import ( + "fmt" + "os" + "os/exec" + "os/signal" + "syscall" +) + +import "C" + +func main() { + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGUSR1) + + if err := exec.Command("true").Run(); err != nil { + fmt.Fprintf(os.Stderr, "Unexpected error from `true`: %v", err) + os.Exit(1) + } + + syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) + <-ch +} diff --git a/misc/cgo/testsanitizers/testdata/tsan2.go b/misc/cgo/testsanitizers/testdata/tsan2.go new file mode 100644 index 0000000..5018a19 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan2.go @@ -0,0 +1,55 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program produced false race reports when run under the C/C++ +// ThreadSanitizer, as it did not understand the synchronization in +// the Go code. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +extern void GoRun(void); + +// Yes, you can have definitions if you use //export, as long as they are weak. + +int val __attribute__ ((weak)); + +int run(void) __attribute__ ((weak)); + +int run() { + val = 1; + GoRun(); + return val; +} + +void setVal(int) __attribute__ ((weak)); + +void setVal(int i) { + val = i; +} +*/ +import "C" + +import "runtime" + +//export GoRun +func GoRun() { + runtime.LockOSThread() + c := make(chan bool) + go func() { + runtime.LockOSThread() + C.setVal(2) + c <- true + }() + <-c +} + +func main() { + if v := C.run(); v != 2 { + panic(v) + } +} diff --git a/misc/cgo/testsanitizers/testdata/tsan3.go b/misc/cgo/testsanitizers/testdata/tsan3.go new file mode 100644 index 0000000..87f6c80 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan3.go @@ -0,0 +1,40 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// The stubs for the C functions read and write the same slot on the +// g0 stack when copying arguments in and out. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +int Func1() { + return 0; +} + +void Func2(int x) { + (void)x; +} +*/ +import "C" + +func main() { + const N = 10000 + done := make(chan bool, N) + for i := 0; i < N; i++ { + go func() { + C.Func1() + done <- true + }() + go func() { + C.Func2(0) + done <- true + }() + } + for i := 0; i < 2*N; i++ { + <-done + } +} diff --git a/misc/cgo/testsanitizers/testdata/tsan4.go b/misc/cgo/testsanitizers/testdata/tsan4.go new file mode 100644 index 0000000..f0c76d8 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan4.go @@ -0,0 +1,34 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// Check that calls to C.malloc/C.free do not trigger TSAN false +// positive reports. + +// #cgo CFLAGS: -fsanitize=thread +// #cgo LDFLAGS: -fsanitize=thread +// #include <stdlib.h> +import "C" + +import ( + "runtime" + "sync" +) + +func main() { + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 100; i++ { + p := C.malloc(C.size_t(i * 10)) + runtime.Gosched() + C.free(p) + } + }() + } + wg.Wait() +} diff --git a/misc/cgo/testsanitizers/testdata/tsan5.go b/misc/cgo/testsanitizers/testdata/tsan5.go new file mode 100644 index 0000000..1214a77 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan5.go @@ -0,0 +1,51 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// Check that calls to C.malloc/C.free do not collide with the calls +// made by the os/user package. + +// #cgo CFLAGS: -fsanitize=thread +// #cgo LDFLAGS: -fsanitize=thread +// #include <stdlib.h> +import "C" + +import ( + "fmt" + "os" + "os/user" + "runtime" + "sync" +) + +func main() { + u, err := user.Current() + if err != nil { + fmt.Fprintln(os.Stderr, err) + // Let the test pass. + os.Exit(0) + } + + var wg sync.WaitGroup + for i := 0; i < 20; i++ { + wg.Add(2) + go func() { + defer wg.Done() + for i := 0; i < 1000; i++ { + user.Lookup(u.Username) + runtime.Gosched() + } + }() + go func() { + defer wg.Done() + for i := 0; i < 1000; i++ { + p := C.malloc(C.size_t(len(u.Username) + 1)) + runtime.Gosched() + C.free(p) + } + }() + } + wg.Wait() +} diff --git a/misc/cgo/testsanitizers/testdata/tsan6.go b/misc/cgo/testsanitizers/testdata/tsan6.go new file mode 100644 index 0000000..c96f08d --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan6.go @@ -0,0 +1,49 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// Check that writes to Go allocated memory, with Go synchronization, +// do not look like a race. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +void f(char *p) { + *p = 1; +} +*/ +import "C" + +import ( + "runtime" + "sync" +) + +func main() { + var wg sync.WaitGroup + var mu sync.Mutex + c := make(chan []C.char, 100) + for i := 0; i < 10; i++ { + wg.Add(2) + go func() { + defer wg.Done() + for i := 0; i < 100; i++ { + c <- make([]C.char, 4096) + runtime.Gosched() + } + }() + go func() { + defer wg.Done() + for i := 0; i < 100; i++ { + p := &(<-c)[0] + mu.Lock() + C.f(p) + mu.Unlock() + } + }() + } + wg.Wait() +} diff --git a/misc/cgo/testsanitizers/testdata/tsan7.go b/misc/cgo/testsanitizers/testdata/tsan7.go new file mode 100644 index 0000000..2fb9e45 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan7.go @@ -0,0 +1,40 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// Setting an environment variable in a cgo program changes the C +// environment. Test that this does not confuse the race detector. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread +*/ +import "C" + +import ( + "fmt" + "os" + "sync" + "time" +) + +func main() { + var wg sync.WaitGroup + var mu sync.Mutex + f := func() { + defer wg.Done() + for i := 0; i < 100; i++ { + time.Sleep(time.Microsecond) + mu.Lock() + s := fmt.Sprint(i) + os.Setenv("TSAN_TEST"+s, s) + mu.Unlock() + } + } + wg.Add(2) + go f() + go f() + wg.Wait() +} diff --git a/misc/cgo/testsanitizers/testdata/tsan8.go b/misc/cgo/testsanitizers/testdata/tsan8.go new file mode 100644 index 0000000..88d82a6 --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan8.go @@ -0,0 +1,60 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program failed when run under the C/C++ ThreadSanitizer. The TSAN +// sigaction function interceptor returned SIG_DFL instead of the Go runtime's +// handler in registerSegvForwarder. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct sigaction prev_sa; + +void forwardSignal(int signo, siginfo_t *info, void *context) { + // One of sa_sigaction and/or sa_handler + if ((prev_sa.sa_flags&SA_SIGINFO) != 0) { + prev_sa.sa_sigaction(signo, info, context); + return; + } + if (prev_sa.sa_handler != SIG_IGN && prev_sa.sa_handler != SIG_DFL) { + prev_sa.sa_handler(signo); + return; + } + + fprintf(stderr, "No Go handler to forward to!\n"); + abort(); +} + +void registerSegvFowarder() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO | SA_ONSTACK; + sa.sa_sigaction = forwardSignal; + + if (sigaction(SIGSEGV, &sa, &prev_sa) != 0) { + perror("failed to register SEGV forwarder"); + exit(EXIT_FAILURE); + } +} +*/ +import "C" + +func main() { + C.registerSegvFowarder() + + defer func() { + recover() + }() + var nilp *int + *nilp = 42 +} diff --git a/misc/cgo/testsanitizers/testdata/tsan9.go b/misc/cgo/testsanitizers/testdata/tsan9.go new file mode 100644 index 0000000..06304be --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan9.go @@ -0,0 +1,67 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program failed when run under the C/C++ ThreadSanitizer. The +// TSAN library was not keeping track of whether signals should be +// delivered on the alternate signal stack, and the Go signal handler +// was not preserving callee-saved registers from C callers. + +/* +#cgo CFLAGS: -g -fsanitize=thread +#cgo LDFLAGS: -g -fsanitize=thread + +#include <stdlib.h> +#include <sys/time.h> + +void spin() { + size_t n; + struct timeval tvstart, tvnow; + int diff; + void *prev = NULL, *cur; + + gettimeofday(&tvstart, NULL); + for (n = 0; n < 1<<20; n++) { + cur = malloc(n); + free(prev); + prev = cur; + + gettimeofday(&tvnow, NULL); + diff = (tvnow.tv_sec - tvstart.tv_sec) * 1000 * 1000 + (tvnow.tv_usec - tvstart.tv_usec); + + // Profile frequency is 100Hz so we should definitely + // get a signal in 50 milliseconds. + if (diff > 50 * 1000) { + break; + } + } + + free(prev); +} +*/ +import "C" + +import ( + "io" + "runtime/pprof" + "time" +) + +func goSpin() { + start := time.Now() + for n := 0; n < 1<<20; n++ { + _ = make([]byte, n) + if time.Since(start) > 50*time.Millisecond { + break + } + } +} + +func main() { + pprof.StartCPUProfile(io.Discard) + go C.spin() + goSpin() + pprof.StopCPUProfile() +} diff --git a/misc/cgo/testsanitizers/testdata/tsan_shared.go b/misc/cgo/testsanitizers/testdata/tsan_shared.go new file mode 100644 index 0000000..55ff67e --- /dev/null +++ b/misc/cgo/testsanitizers/testdata/tsan_shared.go @@ -0,0 +1,63 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +// This program failed with SIGSEGV when run under the C/C++ ThreadSanitizer. +// The Go runtime had re-registered the C handler with the wrong flags due to a +// typo, resulting in null pointers being passed for the info and context +// parameters to the handler. + +/* +#cgo CFLAGS: -fsanitize=thread +#cgo LDFLAGS: -fsanitize=thread + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ucontext.h> + +void check_params(int signo, siginfo_t *info, void *context) { + ucontext_t* uc = (ucontext_t*)(context); + + if (info->si_signo != signo) { + fprintf(stderr, "info->si_signo does not match signo.\n"); + abort(); + } + + if (uc->uc_stack.ss_size == 0) { + fprintf(stderr, "uc_stack has size 0.\n"); + abort(); + } +} + + +// Set up the signal handler in a high priority constructor, so +// that it is installed before the Go code starts. + +static void register_handler(void) __attribute__ ((constructor (200))); + +static void register_handler() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = check_params; + + if (sigaction(SIGUSR1, &sa, NULL) != 0) { + perror("failed to register SIGUSR1 handler"); + exit(EXIT_FAILURE); + } +} +*/ +import "C" + +import "syscall" + +func init() { + C.raise(C.int(syscall.SIGUSR1)) +} + +func main() {} diff --git a/misc/cgo/testsanitizers/tsan_test.go b/misc/cgo/testsanitizers/tsan_test.go new file mode 100644 index 0000000..00ad313 --- /dev/null +++ b/misc/cgo/testsanitizers/tsan_test.go @@ -0,0 +1,69 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sanitizers_test + +import ( + "strings" + "testing" +) + +func TestTSAN(t *testing.T) { + goos, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + goarch, err := goEnv("GOARCH") + if err != nil { + t.Fatal(err) + } + // The msan tests require support for the -msan option. + if !compilerRequiredTsanVersion(goos, goarch) { + t.Skipf("skipping on %s/%s; compiler version for -tsan option is too old.", goos, goarch) + } + + t.Parallel() + requireOvercommit(t) + config := configure("thread") + config.skipIfCSanitizerBroken(t) + + mustRun(t, config.goCmd("build", "std")) + + cases := []struct { + src string + needsRuntime bool + }{ + {src: "tsan.go"}, + {src: "tsan2.go"}, + {src: "tsan3.go"}, + {src: "tsan4.go"}, + {src: "tsan5.go", needsRuntime: true}, + {src: "tsan6.go", needsRuntime: true}, + {src: "tsan7.go", needsRuntime: true}, + {src: "tsan8.go"}, + {src: "tsan9.go"}, + {src: "tsan10.go", needsRuntime: true}, + {src: "tsan11.go", needsRuntime: true}, + {src: "tsan12.go", needsRuntime: true}, + } + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.src, ".go") + t.Run(name, func(t *testing.T) { + t.Parallel() + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + outPath := dir.Join(name) + mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src))) + + cmd := hangProneCmd(outPath) + if tc.needsRuntime { + config.skipIfRuntimeIncompatible(t) + } + mustRun(t, cmd) + }) + } +} diff --git a/misc/cgo/testshared/overlaydir_test.go b/misc/cgo/testshared/overlaydir_test.go new file mode 100644 index 0000000..eb587a2 --- /dev/null +++ b/misc/cgo/testshared/overlaydir_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package shared_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go new file mode 100644 index 0000000..03da8f9 --- /dev/null +++ b/misc/cgo/testshared/shared_test.go @@ -0,0 +1,1124 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package shared_test + +import ( + "bufio" + "bytes" + "debug/elf" + "encoding/binary" + "flag" + "fmt" + "go/build" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "sort" + "strconv" + "strings" + "testing" + "time" +) + +var gopathInstallDir, gorootInstallDir string +var oldGOROOT string + +// This is the smallest set of packages we can link into a shared +// library (runtime/cgo is built implicitly). +var minpkgs = []string{"runtime", "sync/atomic"} +var soname = "libruntime,sync-atomic.so" + +var testX = flag.Bool("testx", false, "if true, pass -x to 'go' subcommands invoked by the test") +var testWork = flag.Bool("testwork", false, "if true, log and do not delete the temporary working directory") + +// run runs a command and calls t.Errorf if it fails. +func run(t *testing.T, msg string, args ...string) { + runWithEnv(t, msg, nil, args...) +} + +// runWithEnv runs a command under the given environment and calls t.Errorf if it fails. +func runWithEnv(t *testing.T, msg string, env []string, args ...string) { + c := exec.Command(args[0], args[1:]...) + if len(env) != 0 { + c.Env = append(os.Environ(), env...) + } + if output, err := c.CombinedOutput(); err != nil { + t.Errorf("executing %s (%s) failed %s:\n%s", strings.Join(args, " "), msg, err, output) + } +} + +// goCmd invokes the go tool with the installsuffix set up by TestMain. It calls +// t.Fatalf if the command fails. +func goCmd(t *testing.T, args ...string) string { + newargs := []string{args[0]} + if *testX && args[0] != "env" { + newargs = append(newargs, "-x", "-ldflags=-v") + } + newargs = append(newargs, args[1:]...) + c := exec.Command(filepath.Join(oldGOROOT, "bin", "go"), newargs...) + stderr := new(strings.Builder) + c.Stderr = stderr + + if testing.Verbose() && t == nil { + fmt.Fprintf(os.Stderr, "+ go %s\n", strings.Join(args, " ")) + c.Stderr = os.Stderr + } + output, err := c.Output() + + if err != nil { + if t != nil { + t.Helper() + t.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr) + } else { + // Panic instead of using log.Fatalf so that deferred cleanup may run in testMain. + log.Panicf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr) + } + } + if testing.Verbose() && t != nil { + t.Logf("go %s", strings.Join(args, " ")) + if stderr.Len() > 0 { + t.Logf("%s", stderr) + } + } + return string(bytes.TrimSpace(output)) +} + +// TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit). +func testMain(m *testing.M) (int, error) { + cwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + oldGOROOT = filepath.Join(cwd, "../../..") + + workDir, err := os.MkdirTemp("", "shared_test") + if err != nil { + return 0, err + } + if *testWork || testing.Verbose() { + fmt.Printf("+ mkdir -p %s\n", workDir) + } + if !*testWork { + defer os.RemoveAll(workDir) + } + + // -buildmode=shared fundamentally does not work in module mode. + // (It tries to share package dependencies across builds, but in module mode + // each module has its own distinct set of dependency versions.) + // We would like to eliminate it (see https://go.dev/issue/47788), + // but first need to figure out a replacement that covers the small subset + // of use-cases where -buildmode=shared still works today. + // For now, run the tests in GOPATH mode only. + os.Setenv("GO111MODULE", "off") + + // Some tests need to edit the source in GOPATH, so copy this directory to a + // temporary directory and chdir to that. + gopath := filepath.Join(workDir, "gopath") + modRoot, err := cloneTestdataModule(gopath) + if err != nil { + return 0, err + } + if testing.Verbose() { + fmt.Printf("+ export GOPATH=%s\n", gopath) + fmt.Printf("+ cd %s\n", modRoot) + } + os.Setenv("GOPATH", gopath) + // Explicitly override GOBIN as well, in case it was set through a GOENV file. + os.Setenv("GOBIN", filepath.Join(gopath, "bin")) + os.Chdir(modRoot) + os.Setenv("PWD", modRoot) + + // The test also needs to install libraries into GOROOT/pkg, so copy the + // subset of GOROOT that we need. + // + // TODO(golang.org/issue/28553): Rework -buildmode=shared so that it does not + // need to write to GOROOT. + goroot := filepath.Join(workDir, "goroot") + if err := cloneGOROOTDeps(goroot); err != nil { + return 0, err + } + if testing.Verbose() { + fmt.Fprintf(os.Stderr, "+ export GOROOT=%s\n", goroot) + } + os.Setenv("GOROOT", goroot) + + myContext := build.Default + myContext.GOROOT = goroot + myContext.GOPATH = gopath + + // All tests depend on runtime being built into a shared library. Because + // that takes a few seconds, do it here and have all tests use the version + // built here. + goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs...)...) + + shlib := goCmd(nil, "list", "-linkshared", "-f={{.Shlib}}", "runtime") + if shlib != "" { + gorootInstallDir = filepath.Dir(shlib) + } + + myContext.InstallSuffix = "_dynlink" + depP, err := myContext.Import("./depBase", ".", build.ImportComment) + if err != nil { + return 0, fmt.Errorf("import failed: %v", err) + } + if depP.PkgTargetRoot == "" { + gopathInstallDir = filepath.Dir(goCmd(nil, "list", "-buildmode=shared", "-f", "{{.Target}}", "./depBase")) + } else { + gopathInstallDir = filepath.Join(depP.PkgTargetRoot, "testshared") + } + return m.Run(), nil +} + +func TestMain(m *testing.M) { + log.SetFlags(log.Lshortfile) + flag.Parse() + + exitCode, err := testMain(m) + if err != nil { + log.Fatal(err) + } + os.Exit(exitCode) +} + +// cloneTestdataModule clones the packages from src/testshared into gopath. +// It returns the directory within gopath at which the module root is located. +func cloneTestdataModule(gopath string) (string, error) { + modRoot := filepath.Join(gopath, "src", "testshared") + if err := overlayDir(modRoot, "testdata"); err != nil { + return "", err + } + if err := os.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module testshared\n"), 0644); err != nil { + return "", err + } + return modRoot, nil +} + +// cloneGOROOTDeps copies (or symlinks) the portions of GOROOT/src and +// GOROOT/pkg relevant to this test into the given directory. +// It must be run from within the testdata module. +func cloneGOROOTDeps(goroot string) error { + // Before we clone GOROOT, figure out which packages we need to copy over. + listArgs := []string{ + "list", + "-deps", + "-f", "{{if and .Standard (not .ForTest)}}{{.ImportPath}}{{end}}", + } + stdDeps := goCmd(nil, append(listArgs, minpkgs...)...) + testdataDeps := goCmd(nil, append(listArgs, "-test", "./...")...) + + pkgs := append(strings.Split(strings.TrimSpace(stdDeps), "\n"), + strings.Split(strings.TrimSpace(testdataDeps), "\n")...) + sort.Strings(pkgs) + var pkgRoots []string + for _, pkg := range pkgs { + parentFound := false + for _, prev := range pkgRoots { + if strings.HasPrefix(pkg, prev) { + // We will copy in the source for pkg when we copy in prev. + parentFound = true + break + } + } + if !parentFound { + pkgRoots = append(pkgRoots, pkg) + } + } + + gorootDirs := []string{ + "pkg/tool", + "pkg/include", + } + for _, pkg := range pkgRoots { + gorootDirs = append(gorootDirs, filepath.Join("src", pkg)) + } + + for _, dir := range gorootDirs { + if testing.Verbose() { + fmt.Fprintf(os.Stderr, "+ cp -r %s %s\n", filepath.Join(oldGOROOT, dir), filepath.Join(goroot, dir)) + } + if err := overlayDir(filepath.Join(goroot, dir), filepath.Join(oldGOROOT, dir)); err != nil { + return err + } + } + + return nil +} + +// The shared library was built at the expected location. +func TestSOBuilt(t *testing.T) { + _, err := os.Stat(filepath.Join(gorootInstallDir, soname)) + if err != nil { + t.Error(err) + } +} + +func hasDynTag(f *elf.File, tag elf.DynTag) bool { + ds := f.SectionByType(elf.SHT_DYNAMIC) + if ds == nil { + return false + } + d, err := ds.Data() + if err != nil { + return false + } + for len(d) > 0 { + var t elf.DynTag + switch f.Class { + case elf.ELFCLASS32: + t = elf.DynTag(f.ByteOrder.Uint32(d[0:4])) + d = d[8:] + case elf.ELFCLASS64: + t = elf.DynTag(f.ByteOrder.Uint64(d[0:8])) + d = d[16:] + } + if t == tag { + return true + } + } + return false +} + +// The shared library does not have relocations against the text segment. +func TestNoTextrel(t *testing.T) { + sopath := filepath.Join(gorootInstallDir, soname) + f, err := elf.Open(sopath) + if err != nil { + t.Fatal("elf.Open failed: ", err) + } + defer f.Close() + if hasDynTag(f, elf.DT_TEXTREL) { + t.Errorf("%s has DT_TEXTREL set", soname) + } +} + +// The shared library does not contain symbols called ".dup" +// (See golang.org/issue/14841.) +func TestNoDupSymbols(t *testing.T) { + sopath := filepath.Join(gorootInstallDir, soname) + f, err := elf.Open(sopath) + if err != nil { + t.Fatal("elf.Open failed: ", err) + } + defer f.Close() + syms, err := f.Symbols() + if err != nil { + t.Errorf("error reading symbols %v", err) + return + } + for _, s := range syms { + if s.Name == ".dup" { + t.Fatalf("%s contains symbol called .dup", sopath) + } + } +} + +// The install command should have created a "shlibname" file for the +// listed packages (and runtime/cgo, and math on arm) indicating the +// name of the shared library containing it. +func TestShlibnameFiles(t *testing.T) { + pkgs := append([]string{}, minpkgs...) + pkgs = append(pkgs, "runtime/cgo") + if runtime.GOARCH == "arm" { + pkgs = append(pkgs, "math") + } + for _, pkg := range pkgs { + shlibnamefile := filepath.Join(gorootInstallDir, pkg+".shlibname") + contentsb, err := os.ReadFile(shlibnamefile) + if err != nil { + t.Errorf("error reading shlibnamefile for %s: %v", pkg, err) + continue + } + contents := strings.TrimSpace(string(contentsb)) + if contents != soname { + t.Errorf("shlibnamefile for %s has wrong contents: %q", pkg, contents) + } + } +} + +// Is a given offset into the file contained in a loaded segment? +func isOffsetLoaded(f *elf.File, offset uint64) bool { + for _, prog := range f.Progs { + if prog.Type == elf.PT_LOAD { + if prog.Off <= offset && offset < prog.Off+prog.Filesz { + return true + } + } + } + return false +} + +func rnd(v int32, r int32) int32 { + if r <= 0 { + return v + } + v += r - 1 + c := v % r + if c < 0 { + c += r + } + v -= c + return v +} + +func readwithpad(r io.Reader, sz int32) ([]byte, error) { + data := make([]byte, rnd(sz, 4)) + _, err := io.ReadFull(r, data) + if err != nil { + return nil, err + } + data = data[:sz] + return data, nil +} + +type note struct { + name string + tag int32 + desc string + section *elf.Section +} + +// Read all notes from f. As ELF section names are not supposed to be special, one +// looks for a particular note by scanning all SHT_NOTE sections looking for a note +// with a particular "name" and "tag". +func readNotes(f *elf.File) ([]*note, error) { + var notes []*note + for _, sect := range f.Sections { + if sect.Type != elf.SHT_NOTE { + continue + } + r := sect.Open() + for { + var namesize, descsize, tag int32 + err := binary.Read(r, f.ByteOrder, &namesize) + if err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("read namesize failed: %v", err) + } + err = binary.Read(r, f.ByteOrder, &descsize) + if err != nil { + return nil, fmt.Errorf("read descsize failed: %v", err) + } + err = binary.Read(r, f.ByteOrder, &tag) + if err != nil { + return nil, fmt.Errorf("read type failed: %v", err) + } + name, err := readwithpad(r, namesize) + if err != nil { + return nil, fmt.Errorf("read name failed: %v", err) + } + desc, err := readwithpad(r, descsize) + if err != nil { + return nil, fmt.Errorf("read desc failed: %v", err) + } + notes = append(notes, ¬e{name: string(name), tag: tag, desc: string(desc), section: sect}) + } + } + return notes, nil +} + +func dynStrings(t *testing.T, path string, flag elf.DynTag) []string { + t.Helper() + f, err := elf.Open(path) + if err != nil { + t.Fatalf("elf.Open(%q) failed: %v", path, err) + } + defer f.Close() + dynstrings, err := f.DynString(flag) + if err != nil { + t.Fatalf("DynString(%s) failed on %s: %v", flag, path, err) + } + return dynstrings +} + +func AssertIsLinkedToRegexp(t *testing.T, path string, re *regexp.Regexp) { + t.Helper() + for _, dynstring := range dynStrings(t, path, elf.DT_NEEDED) { + if re.MatchString(dynstring) { + return + } + } + t.Errorf("%s is not linked to anything matching %v", path, re) +} + +func AssertIsLinkedTo(t *testing.T, path, lib string) { + t.Helper() + AssertIsLinkedToRegexp(t, path, regexp.MustCompile(regexp.QuoteMeta(lib))) +} + +func AssertHasRPath(t *testing.T, path, dir string) { + t.Helper() + for _, tag := range []elf.DynTag{elf.DT_RPATH, elf.DT_RUNPATH} { + for _, dynstring := range dynStrings(t, path, tag) { + for _, rpath := range strings.Split(dynstring, ":") { + if filepath.Clean(rpath) == filepath.Clean(dir) { + return + } + } + } + } + t.Errorf("%s does not have rpath %s", path, dir) +} + +// Build a trivial program that links against the shared runtime and check it runs. +func TestTrivialExecutable(t *testing.T) { + goCmd(t, "install", "-linkshared", "./trivial") + run(t, "trivial executable", "../../bin/trivial") + AssertIsLinkedTo(t, "../../bin/trivial", soname) + AssertHasRPath(t, "../../bin/trivial", gorootInstallDir) + // It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment + // 4*64k should be enough, but this might need revision eventually. + checkSize(t, "../../bin/trivial", 256000) +} + +// Build a trivial program in PIE mode that links against the shared runtime and check it runs. +func TestTrivialExecutablePIE(t *testing.T) { + goCmd(t, "build", "-buildmode=pie", "-o", "trivial.pie", "-linkshared", "./trivial") + run(t, "trivial executable", "./trivial.pie") + AssertIsLinkedTo(t, "./trivial.pie", soname) + AssertHasRPath(t, "./trivial.pie", gorootInstallDir) + // It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment + // 4*64k should be enough, but this might need revision eventually. + checkSize(t, "./trivial.pie", 256000) +} + +// Check that the file size does not exceed a limit. +func checkSize(t *testing.T, f string, limit int64) { + fi, err := os.Stat(f) + if err != nil { + t.Fatalf("stat failed: %v", err) + } + if sz := fi.Size(); sz > limit { + t.Errorf("file too large: got %d, want <= %d", sz, limit) + } +} + +// Build a division test program and check it runs. +func TestDivisionExecutable(t *testing.T) { + goCmd(t, "install", "-linkshared", "./division") + run(t, "division executable", "../../bin/division") +} + +// Build an executable that uses cgo linked against the shared runtime and check it +// runs. +func TestCgoExecutable(t *testing.T) { + goCmd(t, "install", "-linkshared", "./execgo") + run(t, "cgo executable", "../../bin/execgo") +} + +func checkPIE(t *testing.T, name string) { + f, err := elf.Open(name) + if err != nil { + t.Fatal("elf.Open failed: ", err) + } + defer f.Close() + if f.Type != elf.ET_DYN { + t.Errorf("%s has type %v, want ET_DYN", name, f.Type) + } + if hasDynTag(f, elf.DT_TEXTREL) { + t.Errorf("%s has DT_TEXTREL set", name) + } +} + +func TestTrivialPIE(t *testing.T) { + if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-alpine") { + t.Skip("skipping on alpine until issue #54354 resolved") + } + name := "trivial_pie" + goCmd(t, "build", "-buildmode=pie", "-o="+name, "./trivial") + defer os.Remove(name) + run(t, name, "./"+name) + checkPIE(t, name) +} + +func TestCgoPIE(t *testing.T) { + name := "cgo_pie" + goCmd(t, "build", "-buildmode=pie", "-o="+name, "./execgo") + defer os.Remove(name) + run(t, name, "./"+name) + checkPIE(t, name) +} + +// Build a GOPATH package into a shared library that links against the goroot runtime +// and an executable that links against both. +func TestGopathShlib(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + shlib := goCmd(t, "list", "-f", "{{.Shlib}}", "-buildmode=shared", "-linkshared", "./depBase") + AssertIsLinkedTo(t, shlib, soname) + goCmd(t, "install", "-linkshared", "./exe") + AssertIsLinkedTo(t, "../../bin/exe", soname) + AssertIsLinkedTo(t, "../../bin/exe", filepath.Base(shlib)) + AssertHasRPath(t, "../../bin/exe", gorootInstallDir) + AssertHasRPath(t, "../../bin/exe", filepath.Dir(gopathInstallDir)) + // And check it runs. + run(t, "executable linked to GOPATH library", "../../bin/exe") +} + +// The shared library contains a note listing the packages it contains in a section +// that is not mapped into memory. +func testPkgListNote(t *testing.T, f *elf.File, note *note) { + if note.section.Flags != 0 { + t.Errorf("package list section has flags %v, want 0", note.section.Flags) + } + if isOffsetLoaded(f, note.section.Offset) { + t.Errorf("package list section contained in PT_LOAD segment") + } + if note.desc != "testshared/depBase\n" { + t.Errorf("incorrect package list %q, want %q", note.desc, "testshared/depBase\n") + } +} + +// The shared library contains a note containing the ABI hash that is mapped into +// memory and there is a local symbol called go.link.abihashbytes that points 16 +// bytes into it. +func testABIHashNote(t *testing.T, f *elf.File, note *note) { + if note.section.Flags != elf.SHF_ALLOC { + t.Errorf("abi hash section has flags %v, want SHF_ALLOC", note.section.Flags) + } + if !isOffsetLoaded(f, note.section.Offset) { + t.Errorf("abihash section not contained in PT_LOAD segment") + } + var hashbytes elf.Symbol + symbols, err := f.Symbols() + if err != nil { + t.Errorf("error reading symbols %v", err) + return + } + for _, sym := range symbols { + if sym.Name == "go:link.abihashbytes" { + hashbytes = sym + } + } + if hashbytes.Name == "" { + t.Errorf("no symbol called go:link.abihashbytes") + return + } + if elf.ST_BIND(hashbytes.Info) != elf.STB_LOCAL { + t.Errorf("%s has incorrect binding %v, want STB_LOCAL", hashbytes.Name, elf.ST_BIND(hashbytes.Info)) + } + if f.Sections[hashbytes.Section] != note.section { + t.Errorf("%s has incorrect section %v, want %s", hashbytes.Name, f.Sections[hashbytes.Section].Name, note.section.Name) + } + if hashbytes.Value-note.section.Addr != 16 { + t.Errorf("%s has incorrect offset into section %d, want 16", hashbytes.Name, hashbytes.Value-note.section.Addr) + } +} + +// A Go shared library contains a note indicating which other Go shared libraries it +// was linked against in an unmapped section. +func testDepsNote(t *testing.T, f *elf.File, note *note) { + if note.section.Flags != 0 { + t.Errorf("package list section has flags %v, want 0", note.section.Flags) + } + if isOffsetLoaded(f, note.section.Offset) { + t.Errorf("package list section contained in PT_LOAD segment") + } + // libdepBase.so just links against the lib containing the runtime. + if note.desc != soname { + t.Errorf("incorrect dependency list %q, want %q", note.desc, soname) + } +} + +// The shared library contains notes with defined contents; see above. +func TestNotes(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + shlib := goCmd(t, "list", "-f", "{{.Shlib}}", "-buildmode=shared", "-linkshared", "./depBase") + f, err := elf.Open(shlib) + if err != nil { + t.Fatal(err) + } + defer f.Close() + notes, err := readNotes(f) + if err != nil { + t.Fatal(err) + } + pkgListNoteFound := false + abiHashNoteFound := false + depsNoteFound := false + for _, note := range notes { + if note.name != "Go\x00\x00" { + continue + } + switch note.tag { + case 1: // ELF_NOTE_GOPKGLIST_TAG + if pkgListNoteFound { + t.Error("multiple package list notes") + } + testPkgListNote(t, f, note) + pkgListNoteFound = true + case 2: // ELF_NOTE_GOABIHASH_TAG + if abiHashNoteFound { + t.Error("multiple abi hash notes") + } + testABIHashNote(t, f, note) + abiHashNoteFound = true + case 3: // ELF_NOTE_GODEPS_TAG + if depsNoteFound { + t.Error("multiple dependency list notes") + } + testDepsNote(t, f, note) + depsNoteFound = true + } + } + if !pkgListNoteFound { + t.Error("package list note not found") + } + if !abiHashNoteFound { + t.Error("abi hash note not found") + } + if !depsNoteFound { + t.Error("deps note not found") + } +} + +// Build a GOPATH package (depBase) into a shared library that links against the goroot +// runtime, another package (dep2) that links against the first, and an +// executable that links against dep2. +func TestTwoGopathShlibs(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep2") + goCmd(t, "install", "-linkshared", "./exe2") + run(t, "executable linked to GOPATH library", "../../bin/exe2") +} + +func TestThreeGopathShlibs(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep2") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep3") + goCmd(t, "install", "-linkshared", "./exe3") + run(t, "executable linked to GOPATH library", "../../bin/exe3") +} + +// If gccgo is not available or not new enough, call t.Skip. +func requireGccgo(t *testing.T) { + t.Helper() + + gccgoName := os.Getenv("GCCGO") + if gccgoName == "" { + gccgoName = "gccgo" + } + gccgoPath, err := exec.LookPath(gccgoName) + if err != nil { + t.Skip("gccgo not found") + } + cmd := exec.Command(gccgoPath, "-dumpversion") + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s -dumpversion failed: %v\n%s", gccgoPath, err, output) + } + dot := bytes.Index(output, []byte{'.'}) + if dot > 0 { + output = output[:dot] + } + major, err := strconv.Atoi(string(output)) + if err != nil { + t.Skipf("can't parse gccgo version number %s", output) + } + if major < 5 { + t.Skipf("gccgo too old (%s)", strings.TrimSpace(string(output))) + } + + gomod, err := exec.Command("go", "env", "GOMOD").Output() + if err != nil { + t.Fatalf("go env GOMOD: %v", err) + } + if len(bytes.TrimSpace(gomod)) > 0 { + t.Skipf("gccgo not supported in module mode; see golang.org/issue/30344") + } +} + +// Build a GOPATH package into a shared library with gccgo and an executable that +// links against it. +func TestGoPathShlibGccgo(t *testing.T) { + requireGccgo(t) + + libgoRE := regexp.MustCompile("libgo.so.[0-9]+") + + goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./depBase") + + // Run 'go list' after 'go install': with gccgo, we apparently don't know the + // shlib location until after we've installed it. + shlib := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./depBase") + + AssertIsLinkedToRegexp(t, shlib, libgoRE) + goCmd(t, "install", "-compiler=gccgo", "-linkshared", "./exe") + AssertIsLinkedToRegexp(t, "../../bin/exe", libgoRE) + AssertIsLinkedTo(t, "../../bin/exe", filepath.Base(shlib)) + AssertHasRPath(t, "../../bin/exe", filepath.Dir(shlib)) + // And check it runs. + run(t, "gccgo-built", "../../bin/exe") +} + +// The gccgo version of TestTwoGopathShlibs: build a GOPATH package into a shared +// library with gccgo, another GOPATH package that depends on the first and an +// executable that links the second library. +func TestTwoGopathShlibsGccgo(t *testing.T) { + requireGccgo(t) + + libgoRE := regexp.MustCompile("libgo.so.[0-9]+") + + goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./dep2") + goCmd(t, "install", "-compiler=gccgo", "-linkshared", "./exe2") + + // Run 'go list' after 'go install': with gccgo, we apparently don't know the + // shlib location until after we've installed it. + dep2 := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./dep2") + depBase := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./depBase") + + AssertIsLinkedToRegexp(t, depBase, libgoRE) + AssertIsLinkedToRegexp(t, dep2, libgoRE) + AssertIsLinkedTo(t, dep2, filepath.Base(depBase)) + AssertIsLinkedToRegexp(t, "../../bin/exe2", libgoRE) + AssertIsLinkedTo(t, "../../bin/exe2", filepath.Base(dep2)) + AssertIsLinkedTo(t, "../../bin/exe2", filepath.Base(depBase)) + + // And check it runs. + run(t, "gccgo-built", "../../bin/exe2") +} + +// Testing rebuilding of shared libraries when they are stale is a bit more +// complicated that it seems like it should be. First, we make everything "old": but +// only a few seconds old, or it might be older than gc (or the runtime source) and +// everything will get rebuilt. Then define a timestamp slightly newer than this +// time, which is what we set the mtime to of a file to cause it to be seen as new, +// and finally another slightly even newer one that we can compare files against to +// see if they have been rebuilt. +var oldTime = time.Now().Add(-9 * time.Second) +var nearlyNew = time.Now().Add(-6 * time.Second) +var stampTime = time.Now().Add(-3 * time.Second) + +// resetFileStamps makes "everything" (bin, src, pkg from GOPATH and the +// test-specific parts of GOROOT) appear old. +func resetFileStamps() { + chtime := func(path string, info os.FileInfo, err error) error { + return os.Chtimes(path, oldTime, oldTime) + } + reset := func(path string) { + if err := filepath.Walk(path, chtime); err != nil { + log.Panicf("resetFileStamps failed: %v", err) + } + + } + reset("../../bin") + reset("../../pkg") + reset("../../src") + reset(gorootInstallDir) +} + +// touch changes path and returns a function that changes it back. +// It also sets the time of the file, so that we can see if it is rewritten. +func touch(t *testing.T, path string) (cleanup func()) { + t.Helper() + data, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + old := make([]byte, len(data)) + copy(old, data) + if bytes.HasPrefix(data, []byte("!<arch>\n")) { + // Change last digit of build ID. + // (Content ID in the new content-based build IDs.) + const marker = `build id "` + i := bytes.Index(data, []byte(marker)) + if i < 0 { + t.Fatal("cannot find build id in archive") + } + j := bytes.IndexByte(data[i+len(marker):], '"') + if j < 0 { + t.Fatal("cannot find build id in archive") + } + i += len(marker) + j - 1 + if data[i] == 'a' { + data[i] = 'b' + } else { + data[i] = 'a' + } + } else { + // assume it's a text file + data = append(data, '\n') + } + + // If the file is still a symlink from an overlay, delete it so that we will + // replace it with a regular file instead of overwriting the symlinked one. + fi, err := os.Lstat(path) + if err == nil && !fi.Mode().IsRegular() { + fi, err = os.Stat(path) + if err := os.Remove(path); err != nil { + t.Fatal(err) + } + } + if err != nil { + t.Fatal(err) + } + + // If we're replacing a symlink to a read-only file, make the new file + // user-writable. + perm := fi.Mode().Perm() | 0200 + + if err := os.WriteFile(path, data, perm); err != nil { + t.Fatal(err) + } + if err := os.Chtimes(path, nearlyNew, nearlyNew); err != nil { + t.Fatal(err) + } + return func() { + if err := os.WriteFile(path, old, perm); err != nil { + t.Fatal(err) + } + } +} + +// isNew returns if the path is newer than the time stamp used by touch. +func isNew(t *testing.T, path string) bool { + t.Helper() + fi, err := os.Stat(path) + if err != nil { + t.Fatal(err) + } + return fi.ModTime().After(stampTime) +} + +// Fail unless path has been rebuilt (i.e. is newer than the time stamp used by +// isNew) +func AssertRebuilt(t *testing.T, msg, path string) { + t.Helper() + if !isNew(t, path) { + t.Errorf("%s was not rebuilt (%s)", msg, path) + } +} + +// Fail if path has been rebuilt (i.e. is newer than the time stamp used by isNew) +func AssertNotRebuilt(t *testing.T, msg, path string) { + t.Helper() + if isNew(t, path) { + t.Errorf("%s was rebuilt (%s)", msg, path) + } +} + +func TestRebuilding(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-linkshared", "./exe") + info := strings.Fields(goCmd(t, "list", "-buildmode=shared", "-linkshared", "-f", "{{.Target}} {{.Shlib}}", "./depBase")) + if len(info) != 2 { + t.Fatalf("go list failed to report Target and/or Shlib") + } + target := info[0] + shlib := info[1] + + // If the source is newer than both the .a file and the .so, both are rebuilt. + t.Run("newsource", func(t *testing.T) { + resetFileStamps() + cleanup := touch(t, "./depBase/dep.go") + defer func() { + cleanup() + goCmd(t, "install", "-linkshared", "./exe") + }() + goCmd(t, "install", "-linkshared", "./exe") + AssertRebuilt(t, "new source", target) + AssertRebuilt(t, "new source", shlib) + }) + + // If the .a file is newer than the .so, the .so is rebuilt (but not the .a) + t.Run("newarchive", func(t *testing.T) { + resetFileStamps() + AssertNotRebuilt(t, "new .a file before build", target) + goCmd(t, "list", "-linkshared", "-f={{.ImportPath}} {{.Stale}} {{.StaleReason}} {{.Target}}", "./depBase") + AssertNotRebuilt(t, "new .a file before build", target) + cleanup := touch(t, target) + defer func() { + cleanup() + goCmd(t, "install", "-v", "-linkshared", "./exe") + }() + goCmd(t, "install", "-v", "-linkshared", "./exe") + AssertNotRebuilt(t, "new .a file", target) + AssertRebuilt(t, "new .a file", shlib) + }) +} + +func appendFile(t *testing.T, path, content string) { + t.Helper() + f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0660) + if err != nil { + t.Fatalf("os.OpenFile failed: %v", err) + } + defer func() { + err := f.Close() + if err != nil { + t.Fatalf("f.Close failed: %v", err) + } + }() + _, err = f.WriteString(content) + if err != nil { + t.Fatalf("f.WriteString failed: %v", err) + } +} + +func createFile(t *testing.T, path, content string) { + t.Helper() + f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) + if err != nil { + t.Fatalf("os.OpenFile failed: %v", err) + } + _, err = f.WriteString(content) + if closeErr := f.Close(); err == nil { + err = closeErr + } + if err != nil { + t.Fatalf("WriteString failed: %v", err) + } +} + +func TestABIChecking(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + goCmd(t, "install", "-linkshared", "./exe") + + // If we make an ABI-breaking change to depBase and rebuild libp.so but not exe, + // exe will abort with a complaint on startup. + // This assumes adding an exported function breaks ABI, which is not true in + // some senses but suffices for the narrow definition of ABI compatibility the + // toolchain uses today. + resetFileStamps() + + createFile(t, "./depBase/break.go", "package depBase\nfunc ABIBreak() {}\n") + defer os.Remove("./depBase/break.go") + + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + c := exec.Command("../../bin/exe") + output, err := c.CombinedOutput() + if err == nil { + t.Fatal("executing exe did not fail after ABI break") + } + scanner := bufio.NewScanner(bytes.NewReader(output)) + foundMsg := false + const wantPrefix = "abi mismatch detected between the executable and lib" + for scanner.Scan() { + if strings.HasPrefix(scanner.Text(), wantPrefix) { + foundMsg = true + break + } + } + if err = scanner.Err(); err != nil { + t.Errorf("scanner encountered error: %v", err) + } + if !foundMsg { + t.Fatalf("exe failed, but without line %q; got output:\n%s", wantPrefix, output) + } + + // Rebuilding exe makes it work again. + goCmd(t, "install", "-linkshared", "./exe") + run(t, "rebuilt exe", "../../bin/exe") + + // If we make a change which does not break ABI (such as adding an unexported + // function) and rebuild libdepBase.so, exe still works, even if new function + // is in a file by itself. + resetFileStamps() + createFile(t, "./depBase/dep2.go", "package depBase\nfunc noABIBreak() {}\n") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase") + run(t, "after non-ABI breaking change", "../../bin/exe") +} + +// If a package 'explicit' imports a package 'implicit', building +// 'explicit' into a shared library implicitly includes implicit in +// the shared library. Building an executable that imports both +// explicit and implicit builds the code from implicit into the +// executable rather than fetching it from the shared library. The +// link still succeeds and the executable still runs though. +func TestImplicitInclusion(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./explicit") + goCmd(t, "install", "-linkshared", "./implicitcmd") + run(t, "running executable linked against library that contains same package as it", "../../bin/implicitcmd") +} + +// Tests to make sure that the type fields of empty interfaces and itab +// fields of nonempty interfaces are unique even across modules, +// so that interface equality works correctly. +func TestInterface(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./iface_a") + // Note: iface_i gets installed implicitly as a dependency of iface_a. + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./iface_b") + goCmd(t, "install", "-linkshared", "./iface") + run(t, "running type/itab uniqueness tester", "../../bin/iface") +} + +// Access a global variable from a library. +func TestGlobal(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./globallib") + goCmd(t, "install", "-linkshared", "./global") + run(t, "global executable", "../../bin/global") + AssertIsLinkedTo(t, "../../bin/global", soname) + AssertHasRPath(t, "../../bin/global", gorootInstallDir) +} + +// Run a test using -linkshared of an installed shared package. +// Issue 26400. +func TestTestInstalledShared(t *testing.T) { + goCmd(t, "test", "-linkshared", "-test.short", "sync/atomic") +} + +// Test generated pointer method with -linkshared. +// Issue 25065. +func TestGeneratedMethod(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue25065") +} + +// Test use of shared library struct with generated hash function. +// Issue 30768. +func TestGeneratedHash(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib") + goCmd(t, "test", "-linkshared", "./issue30768") +} + +// Test that packages can be added not in dependency order (here a depends on b, and a adds +// before b). This could happen with e.g. go build -buildmode=shared std. See issue 39777. +func TestPackageOrder(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue39777/a", "./issue39777/b") +} + +// Test that GC data are generated correctly by the linker when it needs a type defined in +// a shared library. See issue 39927. +func TestGCData(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./gcdata/p") + goCmd(t, "build", "-linkshared", "./gcdata/main") + runWithEnv(t, "running gcdata/main", []string{"GODEBUG=clobberfree=1"}, "./main") +} + +// Test that we don't decode type symbols from shared libraries (which has no data, +// causing panic). See issue 44031. +func TestIssue44031(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/a") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/b") + goCmd(t, "run", "-linkshared", "./issue44031/main") +} + +// Test that we use a variable from shared libraries (which implement an +// interface in shared libraries.). A weak reference is used in the itab +// in main process. It can cause unreacheble panic. See issue 47873. +func TestIssue47873(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue47837/a") + goCmd(t, "run", "-linkshared", "./issue47837/main") +} + +// Test that we can build std in shared mode. +func TestStd(t *testing.T) { + if testing.Short() { + t.Skip("skip in short mode") + } + t.Parallel() + tmpDir := t.TempDir() + // Use a temporary pkgdir to not interfere with other tests, and not write to GOROOT. + // Cannot use goCmd as it runs with cloned GOROOT which is incomplete. + runWithEnv(t, "building std", []string{"GOROOT=" + oldGOROOT}, + filepath.Join(oldGOROOT, "bin", "go"), "install", "-buildmode=shared", "-pkgdir="+tmpDir, "std") + + // Issue #58966. + runWithEnv(t, "testing issue #58966", []string{"GOROOT=" + oldGOROOT}, + filepath.Join(oldGOROOT, "bin", "go"), "run", "-linkshared", "-pkgdir="+tmpDir, "./issue58966/main.go") +} diff --git a/misc/cgo/testshared/testdata/dep2/dep2.go b/misc/cgo/testshared/testdata/dep2/dep2.go new file mode 100644 index 0000000..94f38cf --- /dev/null +++ b/misc/cgo/testshared/testdata/dep2/dep2.go @@ -0,0 +1,15 @@ +package dep2 + +import "testshared/depBase" + +var W int = 1 + +var hasProg depBase.HasProg + +type Dep2 struct { + depBase.Dep +} + +func G() int { + return depBase.F() + 1 +} diff --git a/misc/cgo/testshared/testdata/dep3/dep3.go b/misc/cgo/testshared/testdata/dep3/dep3.go new file mode 100644 index 0000000..6b02ad2 --- /dev/null +++ b/misc/cgo/testshared/testdata/dep3/dep3.go @@ -0,0 +1,22 @@ +package dep3 + +// The point of this test file is that it references a type from +// depBase that is also referenced in dep2, but dep2 is loaded by the +// linker before depBase (because it is earlier in the import list). +// There was a bug in the linker where it would not correctly read out +// the type data in this case and later crash. + +import ( + "testshared/dep2" + "testshared/depBase" +) + +type Dep3 struct { + dep depBase.Dep + dep2 dep2.Dep2 +} + +func D3() int { + var x Dep3 + return x.dep.X + x.dep2.X +} diff --git a/misc/cgo/testshared/testdata/depBase/asm.s b/misc/cgo/testshared/testdata/depBase/asm.s new file mode 100644 index 0000000..0f1111f --- /dev/null +++ b/misc/cgo/testshared/testdata/depBase/asm.s @@ -0,0 +1,10 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gc + +#include "textflag.h" + +TEXT ·ImplementedInAsm(SB),NOSPLIT,$0-0 + RET diff --git a/misc/cgo/testshared/testdata/depBase/dep.go b/misc/cgo/testshared/testdata/depBase/dep.go new file mode 100644 index 0000000..e7cc7c8 --- /dev/null +++ b/misc/cgo/testshared/testdata/depBase/dep.go @@ -0,0 +1,37 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package depBase + +import ( + "os" + "reflect" +) + +var SlicePtr interface{} = &[]int{} + +var V int = 1 + +var HasMask []string = []string{"hi"} + +type HasProg struct { + array [1024]*byte +} + +type Dep struct { + X int +} + +func (d *Dep) Method() int { + // This code below causes various go.itab.* symbols to be generated in + // the shared library. Similar code in ../exe/exe.go results in + // exercising https://golang.org/issues/17594 + reflect.TypeOf(os.Stdout).Elem() + return 10 +} + +func F() int { + defer func() {}() + return V +} diff --git a/misc/cgo/testshared/testdata/depBase/gccgo.go b/misc/cgo/testshared/testdata/depBase/gccgo.go new file mode 100644 index 0000000..2b02a1e --- /dev/null +++ b/misc/cgo/testshared/testdata/depBase/gccgo.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gccgo + +package depBase + +func ImplementedInAsm() {} diff --git a/misc/cgo/testshared/testdata/depBase/stubs.go b/misc/cgo/testshared/testdata/depBase/stubs.go new file mode 100644 index 0000000..c779538 --- /dev/null +++ b/misc/cgo/testshared/testdata/depBase/stubs.go @@ -0,0 +1,9 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build gc + +package depBase + +func ImplementedInAsm() diff --git a/misc/cgo/testshared/testdata/division/division.go b/misc/cgo/testshared/testdata/division/division.go new file mode 100644 index 0000000..bb5fc98 --- /dev/null +++ b/misc/cgo/testshared/testdata/division/division.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +//go:noinline +func div(x, y uint32) uint32 { + return x / y +} + +func main() { + a := div(97, 11) + if a != 8 { + panic("FAIL") + } +} diff --git a/misc/cgo/testshared/testdata/exe/exe.go b/misc/cgo/testshared/testdata/exe/exe.go new file mode 100644 index 0000000..ee95f97 --- /dev/null +++ b/misc/cgo/testshared/testdata/exe/exe.go @@ -0,0 +1,45 @@ +package main + +import ( + "os" + "reflect" + "runtime" + + "testshared/depBase" +) + +// Having a function declared in the main package triggered +// golang.org/issue/18250 +func DeclaredInMain() { +} + +type C struct { +} + +func F() *C { + return nil +} + +var slicePtr interface{} = &[]int{} + +func main() { + defer depBase.ImplementedInAsm() + // This code below causes various go.itab.* symbols to be generated in + // the executable. Similar code in ../depBase/dep.go results in + // exercising https://golang.org/issues/17594 + reflect.TypeOf(os.Stdout).Elem() + runtime.GC() + depBase.V = depBase.F() + 1 + + var c *C + if reflect.TypeOf(F).Out(0) != reflect.TypeOf(c) { + panic("bad reflection results, see golang.org/issue/18252") + } + + sp := reflect.New(reflect.TypeOf(slicePtr).Elem()) + s := sp.Interface() + + if reflect.TypeOf(s) != reflect.TypeOf(slicePtr) { + panic("bad reflection results, see golang.org/issue/18729") + } +} diff --git a/misc/cgo/testshared/testdata/exe2/exe2.go b/misc/cgo/testshared/testdata/exe2/exe2.go new file mode 100644 index 0000000..433f331 --- /dev/null +++ b/misc/cgo/testshared/testdata/exe2/exe2.go @@ -0,0 +1,8 @@ +package main + +import "testshared/dep2" + +func main() { + d := &dep2.Dep2{} + dep2.W = dep2.G() + 1 + d.Method() +} diff --git a/misc/cgo/testshared/testdata/exe3/exe3.go b/misc/cgo/testshared/testdata/exe3/exe3.go new file mode 100644 index 0000000..533e3a9 --- /dev/null +++ b/misc/cgo/testshared/testdata/exe3/exe3.go @@ -0,0 +1,7 @@ +package main + +import "testshared/dep3" + +func main() { + dep3.D3() +} diff --git a/misc/cgo/testshared/testdata/execgo/exe.go b/misc/cgo/testshared/testdata/execgo/exe.go new file mode 100644 index 0000000..0427be8 --- /dev/null +++ b/misc/cgo/testshared/testdata/execgo/exe.go @@ -0,0 +1,8 @@ +package main + +/* + */ +import "C" + +func main() { +} diff --git a/misc/cgo/testshared/testdata/explicit/explicit.go b/misc/cgo/testshared/testdata/explicit/explicit.go new file mode 100644 index 0000000..af969fc --- /dev/null +++ b/misc/cgo/testshared/testdata/explicit/explicit.go @@ -0,0 +1,9 @@ +package explicit + +import ( + "testshared/implicit" +) + +func E() int { + return implicit.I() +} diff --git a/misc/cgo/testshared/testdata/gcdata/main/main.go b/misc/cgo/testshared/testdata/gcdata/main/main.go new file mode 100644 index 0000000..394862f --- /dev/null +++ b/misc/cgo/testshared/testdata/gcdata/main/main.go @@ -0,0 +1,37 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that GC data is generated correctly for global +// variables with types defined in a shared library. +// See issue 39927. + +// This test run under GODEBUG=clobberfree=1. The check +// *x[i] == 12345 depends on this debug mode to clobber +// the value if the object is freed prematurely. + +package main + +import ( + "fmt" + "runtime" + "testshared/gcdata/p" +) + +var x p.T + +func main() { + for i := range x { + x[i] = new(int) + *x[i] = 12345 + } + runtime.GC() + runtime.GC() + runtime.GC() + for i := range x { + if *x[i] != 12345 { + fmt.Printf("x[%d] == %d, want 12345\n", i, *x[i]) + panic("FAIL") + } + } +} diff --git a/misc/cgo/testshared/testdata/gcdata/p/p.go b/misc/cgo/testshared/testdata/gcdata/p/p.go new file mode 100644 index 0000000..1fee754 --- /dev/null +++ b/misc/cgo/testshared/testdata/gcdata/p/p.go @@ -0,0 +1,7 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package p + +type T [10]*int diff --git a/misc/cgo/testshared/testdata/global/main.go b/misc/cgo/testshared/testdata/global/main.go new file mode 100644 index 0000000..f43e7c3 --- /dev/null +++ b/misc/cgo/testshared/testdata/global/main.go @@ -0,0 +1,71 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "testshared/globallib" +) + +//go:noinline +func testLoop() { + for i, s := range globallib.Data { + if s != int64(i) { + panic("testLoop: mismatch") + } + } +} + +//go:noinline +func ptrData() *[1<<20 + 10]int64 { + return &globallib.Data +} + +//go:noinline +func testMediumOffset() { + for i, s := range globallib.Data[1<<16-2:] { + if s != int64(i)+1<<16-2 { + panic("testMediumOffset: index mismatch") + } + } + + x := globallib.Data[1<<16-1] + if x != 1<<16-1 { + panic("testMediumOffset: direct mismatch") + } + + y := &globallib.Data[1<<16-3] + if y != &ptrData()[1<<16-3] { + panic("testMediumOffset: address mismatch") + } +} + +//go:noinline +func testLargeOffset() { + for i, s := range globallib.Data[1<<20:] { + if s != int64(i)+1<<20 { + panic("testLargeOffset: index mismatch") + } + } + + x := globallib.Data[1<<20+1] + if x != 1<<20+1 { + panic("testLargeOffset: direct mismatch") + } + + y := &globallib.Data[1<<20+2] + if y != &ptrData()[1<<20+2] { + panic("testLargeOffset: address mismatch") + } +} + +func main() { + testLoop() + + // SSA rules commonly merge offsets into addresses. These + // tests access global data in different ways to try + // and exercise different SSA rules. + testMediumOffset() + testLargeOffset() +} diff --git a/misc/cgo/testshared/testdata/globallib/global.go b/misc/cgo/testshared/testdata/globallib/global.go new file mode 100644 index 0000000..b4372a2 --- /dev/null +++ b/misc/cgo/testshared/testdata/globallib/global.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package globallib + +// Data is large enough to that offsets into it do not fit into +// 16-bit or 20-bit immediates. Ideally we'd also try and overrun +// 32-bit immediates, but that requires the test machine to have +// too much memory. +var Data [1<<20 + 10]int64 + +func init() { + for i := range Data { + Data[i] = int64(i) + } +} diff --git a/misc/cgo/testshared/testdata/iface/main.go b/misc/cgo/testshared/testdata/iface/main.go new file mode 100644 index 0000000..d26ebbc --- /dev/null +++ b/misc/cgo/testshared/testdata/iface/main.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "testshared/iface_a" +import "testshared/iface_b" + +func main() { + if iface_a.F() != iface_b.F() { + panic("empty interfaces not equal") + } + if iface_a.G() != iface_b.G() { + panic("non-empty interfaces not equal") + } +} diff --git a/misc/cgo/testshared/testdata/iface_a/a.go b/misc/cgo/testshared/testdata/iface_a/a.go new file mode 100644 index 0000000..e2cef1e --- /dev/null +++ b/misc/cgo/testshared/testdata/iface_a/a.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package iface_a + +import "testshared/iface_i" + +//go:noinline +func F() interface{} { + return (*iface_i.T)(nil) +} + +//go:noinline +func G() iface_i.I { + return (*iface_i.T)(nil) +} diff --git a/misc/cgo/testshared/testdata/iface_b/b.go b/misc/cgo/testshared/testdata/iface_b/b.go new file mode 100644 index 0000000..dd3e027 --- /dev/null +++ b/misc/cgo/testshared/testdata/iface_b/b.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package iface_b + +import "testshared/iface_i" + +//go:noinline +func F() interface{} { + return (*iface_i.T)(nil) +} + +//go:noinline +func G() iface_i.I { + return (*iface_i.T)(nil) +} diff --git a/misc/cgo/testshared/testdata/iface_i/i.go b/misc/cgo/testshared/testdata/iface_i/i.go new file mode 100644 index 0000000..31c8038 --- /dev/null +++ b/misc/cgo/testshared/testdata/iface_i/i.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package iface_i + +type I interface { + M() +} + +type T struct { +} + +func (t *T) M() { +} + +// *T implements I diff --git a/misc/cgo/testshared/testdata/implicit/implicit.go b/misc/cgo/testshared/testdata/implicit/implicit.go new file mode 100644 index 0000000..5360188 --- /dev/null +++ b/misc/cgo/testshared/testdata/implicit/implicit.go @@ -0,0 +1,5 @@ +package implicit + +func I() int { + return 42 +} diff --git a/misc/cgo/testshared/testdata/implicitcmd/implicitcmd.go b/misc/cgo/testshared/testdata/implicitcmd/implicitcmd.go new file mode 100644 index 0000000..4d42967 --- /dev/null +++ b/misc/cgo/testshared/testdata/implicitcmd/implicitcmd.go @@ -0,0 +1,10 @@ +package main + +import ( + "testshared/explicit" + "testshared/implicit" +) + +func main() { + println(implicit.I() + explicit.E()) +} diff --git a/misc/cgo/testshared/testdata/issue25065/a.go b/misc/cgo/testshared/testdata/issue25065/a.go new file mode 100644 index 0000000..979350f --- /dev/null +++ b/misc/cgo/testshared/testdata/issue25065/a.go @@ -0,0 +1,20 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package issue25065 has a type with a method that is +// 1) referenced in a method expression +// 2) not called +// 3) not converted to an interface +// 4) is a value method but the reference is to the pointer method +// These cases avoid the call to makefuncsym from typecheckfunc, but we +// still need to call makefuncsym somehow or the symbol will not be defined. +package issue25065 + +type T int + +func (t T) M() {} + +func F() func(*T) { + return (*T).M +} diff --git a/misc/cgo/testshared/testdata/issue30768/issue30768lib/lib.go b/misc/cgo/testshared/testdata/issue30768/issue30768lib/lib.go new file mode 100644 index 0000000..9e45ebe --- /dev/null +++ b/misc/cgo/testshared/testdata/issue30768/issue30768lib/lib.go @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue30768lib + +// S is a struct that requires a generated hash function. +type S struct { + A string + B int +} diff --git a/misc/cgo/testshared/testdata/issue30768/x_test.go b/misc/cgo/testshared/testdata/issue30768/x_test.go new file mode 100644 index 0000000..1bbd139 --- /dev/null +++ b/misc/cgo/testshared/testdata/issue30768/x_test.go @@ -0,0 +1,22 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package issue30768_test + +import ( + "testing" + + "testshared/issue30768/issue30768lib" +) + +type s struct { + s issue30768lib.S +} + +func Test30768(t *testing.T) { + // Calling t.Log will convert S to an empty interface, + // which will force a reference to the generated hash function, + // defined in the shared library. + t.Log(s{}) +} diff --git a/misc/cgo/testshared/testdata/issue39777/a/a.go b/misc/cgo/testshared/testdata/issue39777/a/a.go new file mode 100644 index 0000000..c7bf835 --- /dev/null +++ b/misc/cgo/testshared/testdata/issue39777/a/a.go @@ -0,0 +1,9 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +import "testshared/issue39777/b" + +func F() { b.F() } diff --git a/misc/cgo/testshared/testdata/issue39777/b/b.go b/misc/cgo/testshared/testdata/issue39777/b/b.go new file mode 100644 index 0000000..4e68196 --- /dev/null +++ b/misc/cgo/testshared/testdata/issue39777/b/b.go @@ -0,0 +1,7 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +func F() {} diff --git a/misc/cgo/testshared/testdata/issue44031/a/a.go b/misc/cgo/testshared/testdata/issue44031/a/a.go new file mode 100644 index 0000000..48827e6 --- /dev/null +++ b/misc/cgo/testshared/testdata/issue44031/a/a.go @@ -0,0 +1,9 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type ATypeWithALoooooongName interface { // a long name, so the type descriptor symbol name is mangled + M() +} diff --git a/misc/cgo/testshared/testdata/issue44031/b/b.go b/misc/cgo/testshared/testdata/issue44031/b/b.go new file mode 100644 index 0000000..ad3ebec --- /dev/null +++ b/misc/cgo/testshared/testdata/issue44031/b/b.go @@ -0,0 +1,17 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package b + +import "testshared/issue44031/a" + +type T int + +func (T) M() {} + +var i = a.ATypeWithALoooooongName(T(0)) + +func F() { + i.M() +} diff --git a/misc/cgo/testshared/testdata/issue44031/main/main.go b/misc/cgo/testshared/testdata/issue44031/main/main.go new file mode 100644 index 0000000..47f2e3a --- /dev/null +++ b/misc/cgo/testshared/testdata/issue44031/main/main.go @@ -0,0 +1,20 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "testshared/issue44031/b" + +type t int + +func (t) m() {} + +type i interface{ m() } // test that unexported method is correctly marked + +var v interface{} = t(0) + +func main() { + b.F() + v.(i).m() +} diff --git a/misc/cgo/testshared/testdata/issue47837/a/a.go b/misc/cgo/testshared/testdata/issue47837/a/a.go new file mode 100644 index 0000000..68588ed --- /dev/null +++ b/misc/cgo/testshared/testdata/issue47837/a/a.go @@ -0,0 +1,19 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package a + +type A interface { + M() +} + +//go:noinline +func TheFuncWithArgA(a A) { + a.M() +} + +type ImplA struct{} + +//go:noinline +func (A *ImplA) M() {} diff --git a/misc/cgo/testshared/testdata/issue47837/main/main.go b/misc/cgo/testshared/testdata/issue47837/main/main.go new file mode 100644 index 0000000..77c6f34 --- /dev/null +++ b/misc/cgo/testshared/testdata/issue47837/main/main.go @@ -0,0 +1,14 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "testshared/issue47837/a" +) + +func main() { + var vara a.ImplA + a.TheFuncWithArgA(&vara) +} diff --git a/misc/cgo/testshared/testdata/issue58966/main.go b/misc/cgo/testshared/testdata/issue58966/main.go new file mode 100644 index 0000000..2d923c3 --- /dev/null +++ b/misc/cgo/testshared/testdata/issue58966/main.go @@ -0,0 +1,15 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "crypto/elliptic" + +var curve elliptic.Curve + +func main() { + switch curve { + case elliptic.P224(): + } +} diff --git a/misc/cgo/testshared/testdata/trivial/trivial.go b/misc/cgo/testshared/testdata/trivial/trivial.go new file mode 100644 index 0000000..6ade47c --- /dev/null +++ b/misc/cgo/testshared/testdata/trivial/trivial.go @@ -0,0 +1,9 @@ +package main + +func main() { + // This is enough to make sure that the executable references + // a type descriptor, which was the cause of + // https://golang.org/issue/25970. + c := make(chan int) + _ = c +} diff --git a/misc/cgo/testso/noso_test.go b/misc/cgo/testso/noso_test.go new file mode 100644 index 0000000..1014534 --- /dev/null +++ b/misc/cgo/testso/noso_test.go @@ -0,0 +1,10 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !cgo +// +build !cgo + +package so_test + +// Nothing to test. diff --git a/misc/cgo/testso/overlaydir_test.go b/misc/cgo/testso/overlaydir_test.go new file mode 100644 index 0000000..09a1d51 --- /dev/null +++ b/misc/cgo/testso/overlaydir_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package so_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/testso/so_test.go b/misc/cgo/testso/so_test.go new file mode 100644 index 0000000..6d14e32 --- /dev/null +++ b/misc/cgo/testso/so_test.go @@ -0,0 +1,142 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cgo +// +build cgo + +package so_test + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func requireTestSOSupported(t *testing.T) { + t.Helper() + switch runtime.GOARCH { + case "arm64": + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + t.Skip("No exec facility on iOS.") + } + case "ppc64": + if runtime.GOOS == "linux" { + t.Skip("External linking not implemented on linux/ppc64 (issue #8912).") + } + } + if runtime.GOOS == "android" { + t.Skip("No exec facility on Android.") + } +} + +func TestSO(t *testing.T) { + requireTestSOSupported(t) + + GOPATH, err := os.MkdirTemp("", "cgosotest") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(GOPATH) + + modRoot := filepath.Join(GOPATH, "src", "cgosotest") + if err := overlayDir(modRoot, "testdata"); err != nil { + log.Panic(err) + } + if err := os.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module cgosotest\n"), 0666); err != nil { + log.Panic(err) + } + + cmd := exec.Command("go", "env", "CC", "GOGCCFLAGS") + cmd.Dir = modRoot + cmd.Stderr = new(strings.Builder) + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + out, err := cmd.Output() + if err != nil { + t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) + } + lines := strings.Split(string(out), "\n") + if len(lines) != 3 || lines[2] != "" { + t.Fatalf("Unexpected output from %s:\n%s", strings.Join(cmd.Args, " "), lines) + } + + cc := lines[0] + if cc == "" { + t.Fatal("CC environment variable (go env CC) cannot be empty") + } + gogccflags := strings.Split(lines[1], " ") + + // build shared object + ext := "so" + args := append(gogccflags, "-shared") + switch runtime.GOOS { + case "darwin", "ios": + ext = "dylib" + args = append(args, "-undefined", "suppress", "-flat_namespace") + case "windows": + ext = "dll" + args = append(args, "-DEXPORT_DLL") + // At least in mingw-clang it is not permitted to just name a .dll + // on the command line. You must name the corresponding import + // library instead, even though the dll is used when the executable is run. + args = append(args, "-Wl,-out-implib,libcgosotest.a") + case "aix": + ext = "so.1" + } + sofname := "libcgosotest." + ext + args = append(args, "-o", sofname, "cgoso_c.c") + + cmd = exec.Command(cc, args...) + cmd.Dir = modRoot + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) + + if runtime.GOOS == "aix" { + // Shared object must be wrapped by an archive + cmd = exec.Command("ar", "-X64", "-q", "libcgosotest.a", "libcgosotest.so.1") + cmd.Dir = modRoot + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + } + + cmd = exec.Command("go", "build", "-o", "main.exe", "main.go") + cmd.Dir = modRoot + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) + + cmd = exec.Command("./main.exe") + cmd.Dir = modRoot + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + if runtime.GOOS != "windows" { + s := "LD_LIBRARY_PATH" + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + s = "DYLD_LIBRARY_PATH" + } + cmd.Env = append(os.Environ(), s+"=.") + + // On FreeBSD 64-bit architectures, the 32-bit linker looks for + // different environment variables. + if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" { + cmd.Env = append(cmd.Env, "LD_32_LIBRARY_PATH=.") + } + } + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) +} diff --git a/misc/cgo/testso/testdata/cgoso.c b/misc/cgo/testso/testdata/cgoso.c new file mode 100644 index 0000000..612e5d3 --- /dev/null +++ b/misc/cgo/testso/testdata/cgoso.c @@ -0,0 +1,14 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "_cgo_export.h" + +#if defined(WIN32) || defined(_AIX) +extern void setCallback(void *); +void init() { + setCallback(goCallback); +} +#else +void init() {} +#endif diff --git a/misc/cgo/testso/testdata/cgoso.go b/misc/cgo/testso/testdata/cgoso.go new file mode 100644 index 0000000..b59b2a8 --- /dev/null +++ b/misc/cgo/testso/testdata/cgoso.go @@ -0,0 +1,32 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgosotest + +/* +// intentionally write the same LDFLAGS differently +// to test correct handling of LDFLAGS. +#cgo linux LDFLAGS: -L. -lcgosotest +#cgo dragonfly LDFLAGS: -L. -l cgosotest +#cgo freebsd LDFLAGS: -L. -l cgosotest +#cgo openbsd LDFLAGS: -L. -l cgosotest +#cgo solaris LDFLAGS: -L. -lcgosotest +#cgo netbsd LDFLAGS: -L. libcgosotest.so +#cgo darwin LDFLAGS: -L. libcgosotest.dylib +#cgo windows LDFLAGS: -L. libcgosotest.a +#cgo aix LDFLAGS: -L. -l cgosotest + +void init(void); +void sofunc(void); +*/ +import "C" + +func Test() { + C.init() + C.sofunc() +} + +//export goCallback +func goCallback() { +} diff --git a/misc/cgo/testso/testdata/cgoso_c.c b/misc/cgo/testso/testdata/cgoso_c.c new file mode 100644 index 0000000..e5015ed --- /dev/null +++ b/misc/cgo/testso/testdata/cgoso_c.c @@ -0,0 +1,39 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +#ifdef WIN32 +// A Windows DLL is unable to call an arbitrary function in +// the main executable. Work around that by making the main +// executable pass the callback function pointer to us. +void (*goCallback)(void); +__declspec(dllexport) void setCallback(void *f) +{ + goCallback = (void (*)())f; +} +__declspec(dllexport) void sofunc(void); +#elif defined(_AIX) +// AIX doesn't allow the creation of a shared object with an +// undefined symbol. It's possible to bypass this problem by +// using -Wl,-G and -Wl,-brtl option which allows run-time linking. +// However, that's not how most of AIX shared object works. +// Therefore, it's better to consider goCallback as a pointer and +// to set up during an init function. +void (*goCallback)(void); +void setCallback(void *f) { goCallback = f; } +#else +extern void goCallback(void); +void setCallback(void *f) { (void)f; } +#endif + +// OpenBSD and older Darwin lack TLS support +#if !defined(__OpenBSD__) && !defined(__APPLE__) +__thread int tlsvar = 12345; +#endif + +void sofunc(void) +{ + goCallback(); +} diff --git a/misc/cgo/testso/testdata/cgoso_unix.go b/misc/cgo/testso/testdata/cgoso_unix.go new file mode 100644 index 0000000..1860694 --- /dev/null +++ b/misc/cgo/testso/testdata/cgoso_unix.go @@ -0,0 +1,20 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build aix dragonfly freebsd linux netbsd solaris + +package cgosotest + +/* +extern int __thread tlsvar; +int *getTLS() { return &tlsvar; } +*/ +import "C" + +func init() { + if v := *C.getTLS(); v != 12345 { + println("got", v) + panic("BAD TLS value") + } +} diff --git a/misc/cgo/testso/testdata/main.go b/misc/cgo/testso/testdata/main.go new file mode 100644 index 0000000..963d451 --- /dev/null +++ b/misc/cgo/testso/testdata/main.go @@ -0,0 +1,13 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import "cgosotest" + +func main() { + cgosotest.Test() +} diff --git a/misc/cgo/testsovar/noso_test.go b/misc/cgo/testsovar/noso_test.go new file mode 100644 index 0000000..1014534 --- /dev/null +++ b/misc/cgo/testsovar/noso_test.go @@ -0,0 +1,10 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !cgo +// +build !cgo + +package so_test + +// Nothing to test. diff --git a/misc/cgo/testsovar/overlaydir_test.go b/misc/cgo/testsovar/overlaydir_test.go new file mode 100644 index 0000000..09a1d51 --- /dev/null +++ b/misc/cgo/testsovar/overlaydir_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package so_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/testsovar/so_test.go b/misc/cgo/testsovar/so_test.go new file mode 100644 index 0000000..6d14e32 --- /dev/null +++ b/misc/cgo/testsovar/so_test.go @@ -0,0 +1,142 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cgo +// +build cgo + +package so_test + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func requireTestSOSupported(t *testing.T) { + t.Helper() + switch runtime.GOARCH { + case "arm64": + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + t.Skip("No exec facility on iOS.") + } + case "ppc64": + if runtime.GOOS == "linux" { + t.Skip("External linking not implemented on linux/ppc64 (issue #8912).") + } + } + if runtime.GOOS == "android" { + t.Skip("No exec facility on Android.") + } +} + +func TestSO(t *testing.T) { + requireTestSOSupported(t) + + GOPATH, err := os.MkdirTemp("", "cgosotest") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(GOPATH) + + modRoot := filepath.Join(GOPATH, "src", "cgosotest") + if err := overlayDir(modRoot, "testdata"); err != nil { + log.Panic(err) + } + if err := os.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module cgosotest\n"), 0666); err != nil { + log.Panic(err) + } + + cmd := exec.Command("go", "env", "CC", "GOGCCFLAGS") + cmd.Dir = modRoot + cmd.Stderr = new(strings.Builder) + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + out, err := cmd.Output() + if err != nil { + t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) + } + lines := strings.Split(string(out), "\n") + if len(lines) != 3 || lines[2] != "" { + t.Fatalf("Unexpected output from %s:\n%s", strings.Join(cmd.Args, " "), lines) + } + + cc := lines[0] + if cc == "" { + t.Fatal("CC environment variable (go env CC) cannot be empty") + } + gogccflags := strings.Split(lines[1], " ") + + // build shared object + ext := "so" + args := append(gogccflags, "-shared") + switch runtime.GOOS { + case "darwin", "ios": + ext = "dylib" + args = append(args, "-undefined", "suppress", "-flat_namespace") + case "windows": + ext = "dll" + args = append(args, "-DEXPORT_DLL") + // At least in mingw-clang it is not permitted to just name a .dll + // on the command line. You must name the corresponding import + // library instead, even though the dll is used when the executable is run. + args = append(args, "-Wl,-out-implib,libcgosotest.a") + case "aix": + ext = "so.1" + } + sofname := "libcgosotest." + ext + args = append(args, "-o", sofname, "cgoso_c.c") + + cmd = exec.Command(cc, args...) + cmd.Dir = modRoot + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) + + if runtime.GOOS == "aix" { + // Shared object must be wrapped by an archive + cmd = exec.Command("ar", "-X64", "-q", "libcgosotest.a", "libcgosotest.so.1") + cmd.Dir = modRoot + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + } + + cmd = exec.Command("go", "build", "-o", "main.exe", "main.go") + cmd.Dir = modRoot + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) + + cmd = exec.Command("./main.exe") + cmd.Dir = modRoot + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + if runtime.GOOS != "windows" { + s := "LD_LIBRARY_PATH" + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + s = "DYLD_LIBRARY_PATH" + } + cmd.Env = append(os.Environ(), s+"=.") + + // On FreeBSD 64-bit architectures, the 32-bit linker looks for + // different environment variables. + if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" { + cmd.Env = append(cmd.Env, "LD_32_LIBRARY_PATH=.") + } + } + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) +} diff --git a/misc/cgo/testsovar/testdata/cgoso.go b/misc/cgo/testsovar/testdata/cgoso.go new file mode 100644 index 0000000..d9deb55 --- /dev/null +++ b/misc/cgo/testsovar/testdata/cgoso.go @@ -0,0 +1,44 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgosotest + +// This test verifies that Go can access C variables +// in shared object file via cgo. + +/* +// intentionally write the same LDFLAGS differently +// to test correct handling of LDFLAGS. +#cgo windows CFLAGS: -DIMPORT_DLL +#cgo linux LDFLAGS: -L. -lcgosotest +#cgo dragonfly LDFLAGS: -L. -l cgosotest +#cgo freebsd LDFLAGS: -L. -l cgosotest +#cgo openbsd LDFLAGS: -L. -l cgosotest +#cgo solaris LDFLAGS: -L. -lcgosotest +#cgo netbsd LDFLAGS: -L. libcgosotest.so +#cgo darwin LDFLAGS: -L. libcgosotest.dylib +#cgo windows LDFLAGS: -L. libcgosotest.a +#cgo aix LDFLAGS: -L. -l cgosotest + +#include "cgoso_c.h" + +const char* getVar() { + return exported_var; +} +*/ +import "C" + +import "fmt" + +func Test() { + const want = "Hello world" + got := C.GoString(C.getVar()) + if got != want { + panic(fmt.Sprintf("testExportedVar: got %q, but want %q", got, want)) + } + got = C.GoString(C.exported_var) + if got != want { + panic(fmt.Sprintf("testExportedVar: got %q, but want %q", got, want)) + } +} diff --git a/misc/cgo/testsovar/testdata/cgoso_c.c b/misc/cgo/testsovar/testdata/cgoso_c.c new file mode 100644 index 0000000..a448c01 --- /dev/null +++ b/misc/cgo/testsovar/testdata/cgoso_c.c @@ -0,0 +1,7 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +const char *exported_var = "Hello world"; diff --git a/misc/cgo/testsovar/testdata/cgoso_c.h b/misc/cgo/testsovar/testdata/cgoso_c.h new file mode 100644 index 0000000..640db7b --- /dev/null +++ b/misc/cgo/testsovar/testdata/cgoso_c.h @@ -0,0 +1,17 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +#ifdef WIN32 +#if defined(EXPORT_DLL) +# define VAR __declspec(dllexport) +#elif defined(IMPORT_DLL) +# define VAR __declspec(dllimport) +#endif +#else +# define VAR extern +#endif + +VAR const char *exported_var; diff --git a/misc/cgo/testsovar/testdata/main.go b/misc/cgo/testsovar/testdata/main.go new file mode 100644 index 0000000..87b52ce --- /dev/null +++ b/misc/cgo/testsovar/testdata/main.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import "cgosotest" + +func main() { + cgosotest.Test() +} diff --git a/misc/cgo/testtls/tls.go b/misc/cgo/testtls/tls.go new file mode 100644 index 0000000..e634220 --- /dev/null +++ b/misc/cgo/testtls/tls.go @@ -0,0 +1,30 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotlstest + +// #include <pthread.h> +// extern void setTLS(int); +// extern int getTLS(); +import "C" + +import ( + "runtime" + "testing" +) + +func testTLS(t *testing.T) { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + if val := C.getTLS(); val != 0 { + t.Fatalf("at start, C.getTLS() = %#x, want 0", val) + } + + const keyVal = 0x1234 + C.setTLS(keyVal) + if val := C.getTLS(); val != keyVal { + t.Fatalf("at end, C.getTLS() = %#x, want %#x", val, keyVal) + } +} diff --git a/misc/cgo/testtls/tls_test.go b/misc/cgo/testtls/tls_test.go new file mode 100644 index 0000000..a3b67c0 --- /dev/null +++ b/misc/cgo/testtls/tls_test.go @@ -0,0 +1,14 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !windows +// +build !windows + +package cgotlstest + +import "testing" + +func TestTLS(t *testing.T) { + testTLS(t) +} diff --git a/misc/cgo/testtls/tls_unix.c b/misc/cgo/testtls/tls_unix.c new file mode 100644 index 0000000..957afce --- /dev/null +++ b/misc/cgo/testtls/tls_unix.c @@ -0,0 +1,19 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <pthread.h> + +static __thread int tls; + +void +setTLS(int v) +{ + tls = v; +} + +int +getTLS() +{ + return tls; +} diff --git a/misc/chrome/gophertool/README.txt b/misc/chrome/gophertool/README.txt new file mode 100644 index 0000000..a7c0b4b --- /dev/null +++ b/misc/chrome/gophertool/README.txt @@ -0,0 +1,8 @@ +To install: + +1) chrome://extensions/ +2) click "[+] Developer Mode" in top right +3) "Load unpacked extension..." +4) pick $GOROOT/misc/chrome/gophertool + +Done. It'll now auto-reload from source. diff --git a/misc/chrome/gophertool/background.html b/misc/chrome/gophertool/background.html new file mode 100644 index 0000000..06daa98 --- /dev/null +++ b/misc/chrome/gophertool/background.html @@ -0,0 +1,12 @@ +<html> +<!-- + Copyright 2011 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. +--> +<head> +<script src="gopher.js"></script> +<script src="background.js"></script> +</head> +</html> + diff --git a/misc/chrome/gophertool/background.js b/misc/chrome/gophertool/background.js new file mode 100644 index 0000000..79ae05d --- /dev/null +++ b/misc/chrome/gophertool/background.js @@ -0,0 +1,9 @@ +chrome.omnibox.onInputEntered.addListener(function(t) { + var url = urlForInput(t); + if (url) { + chrome.tabs.query({ "active": true, "currentWindow": true }, function(tab) { + if (!tab) return; + chrome.tabs.update(tab.id, { "url": url, "selected": true }); + }); + } +}); diff --git a/misc/chrome/gophertool/gopher.js b/misc/chrome/gophertool/gopher.js new file mode 100644 index 0000000..09edb29 --- /dev/null +++ b/misc/chrome/gophertool/gopher.js @@ -0,0 +1,41 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +var numericRE = /^\d+$/; +var commitRE = /^(?:\d+:)?([0-9a-f]{6,40})$/; // e.g "8486:ab29d2698a47" or "ab29d2698a47" +var gerritChangeIdRE = /^I[0-9a-f]{4,40}$/; // e.g. Id69c00d908d18151486007ec03da5495b34b05f5 +var pkgRE = /^[a-z0-9_\/]+$/; + +function urlForInput(t) { + if (!t) { + return null; + } + + if (numericRE.test(t)) { + if (t < 150000) { + // We could use the golang.org/cl/ handler here, but + // avoid some redirect latency and go right there, since + // one is easy. (no server-side mapping) + return "https://github.com/golang/go/issues/" + t; + } + return "https://golang.org/cl/" + t; + } + + if (gerritChangeIdRE.test(t)) { + return "https://golang.org/cl/" + t; + } + + var match = commitRE.exec(t); + if (match) { + return "https://golang.org/change/" + match[1]; + } + + if (pkgRE.test(t)) { + // TODO: make this smarter, using a list of packages + substring matches. + // Get the list from godoc itself in JSON format? + return "https://golang.org/pkg/" + t; + } + + return null; +} diff --git a/misc/chrome/gophertool/gopher.png b/misc/chrome/gophertool/gopher.png Binary files differnew file mode 100644 index 0000000..0d1abb7 --- /dev/null +++ b/misc/chrome/gophertool/gopher.png diff --git a/misc/chrome/gophertool/manifest.json b/misc/chrome/gophertool/manifest.json new file mode 100644 index 0000000..0438659 --- /dev/null +++ b/misc/chrome/gophertool/manifest.json @@ -0,0 +1,20 @@ +{ + "name": "Hacking Gopher", + "version": "1.0", + "manifest_version": 2, + "description": "Go Hacking utility", + "background": { + "page": "background.html" + }, + "browser_action": { + "default_icon": "gopher.png", + "default_popup": "popup.html" + }, + "omnibox": { "keyword": "golang" }, + "icons": { + "16": "gopher.png" + }, + "permissions": [ + "tabs" + ] +} diff --git a/misc/chrome/gophertool/popup.html b/misc/chrome/gophertool/popup.html new file mode 100644 index 0000000..ad42a38 --- /dev/null +++ b/misc/chrome/gophertool/popup.html @@ -0,0 +1,21 @@ +<html> +<!-- + Copyright 2011 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. +--> +<head> +<script src="gopher.js"></script> +<script src="popup.js"></script> +</head> +<body style='margin: 0.5em; font-family: sans;'> +<small><a href="#" url="https://golang.org/issue">issue</a>, +<a href="#" url="https://golang.org/cl">codereview</a>, +<a href="#" url="https://golang.org/change">commit</a>, or +<a href="#" url="https://golang.org/pkg/">pkg</a> id/name:</small> +<form style='margin: 0' id='navform'><nobr><input id="inputbox" size=10 tabindex=1 /><input type="submit" value="go" /></nobr></form> +<small>Also: <a href="#" url="https://build.golang.org">buildbots</a> +<a href="#" url="https://github.com/golang/go">GitHub</a> +</small> +</body> +</html> diff --git a/misc/chrome/gophertool/popup.js b/misc/chrome/gophertool/popup.js new file mode 100644 index 0000000..410d651 --- /dev/null +++ b/misc/chrome/gophertool/popup.js @@ -0,0 +1,46 @@ +function openURL(url) { + chrome.tabs.create({ "url": url }) +} + +function addLinks() { + var links = document.getElementsByTagName("a"); + for (var i = 0; i < links.length; i++) { + var url = links[i].getAttribute("url"); + if (url) + links[i].addEventListener("click", function () { + openURL(this.getAttribute("url")); + }); + } +} + +window.addEventListener("load", function () { + addLinks(); + console.log("hacking gopher pop-up loaded."); + document.getElementById("inputbox").focus(); +}); + +window.addEventListener("submit", function () { + console.log("submitting form"); + var box = document.getElementById("inputbox"); + box.focus(); + + var t = box.value; + if (t == "") { + return false; + } + + var success = function(url) { + console.log("matched " + t + " to: " + url) + box.value = ""; + openURL(url); + return false; // cancel form submission + }; + + var url = urlForInput(t); + if (url) { + return success(url); + } + + console.log("no match for text: " + t) + return false; +}); diff --git a/misc/editors b/misc/editors new file mode 100644 index 0000000..3a0f73f --- /dev/null +++ b/misc/editors @@ -0,0 +1,5 @@ +For information about plugins and other support for Go in editors and shells, +see this page on the Go Wiki: + +https://golang.org/wiki/IDEsAndTextEditorPlugins + diff --git a/misc/go.mod b/misc/go.mod new file mode 100644 index 0000000..712a051 --- /dev/null +++ b/misc/go.mod @@ -0,0 +1,11 @@ +// Module misc contains tests and binaries that pertain to specific build modes +// (cgo) and platforms (Android and iOS). +// +// The 'run' scripts in ../src execute these tests and binaries, which need to +// be in a module in order to build and run successfully in module mode. +// (Otherwise, they lack well-defined import paths, and module mode — unlike +// GOPATH mode — does not synthesize import paths from the absolute working +// directory.) +module misc + +go 1.18 diff --git a/misc/ios/README b/misc/ios/README new file mode 100644 index 0000000..0f5e9e3 --- /dev/null +++ b/misc/ios/README @@ -0,0 +1,57 @@ +Go on iOS +========= + +To run the standard library tests, run all.bash as usual, but with the compiler +set to the clang wrapper that invokes clang for iOS. For example, this command runs + all.bash on the iOS emulator: + + GOOS=ios GOARCH=amd64 CGO_ENABLED=1 CC_FOR_TARGET=$(pwd)/../misc/ios/clangwrap.sh ./all.bash + +If CC_FOR_TARGET is not set when the toolchain is built (make.bash or all.bash), CC +can be set on the command line. For example, + + GOOS=ios GOARCH=amd64 CGO_ENABLED=1 CC=$(go env GOROOT)/misc/ios/clangwrap.sh go build + +Setting CC is not necessary if the toolchain is built with CC_FOR_TARGET set. + +To use the go tool to run individual programs and tests, put $GOROOT/bin into PATH to ensure +the go_ios_$GOARCH_exec wrapper is found. For example, to run the archive/tar tests: + + export PATH=$GOROOT/bin:$PATH + GOOS=ios GOARCH=amd64 CGO_ENABLED=1 go test archive/tar + +The go_ios_exec wrapper uses GOARCH to select the emulator (amd64) or the device (arm64). +However, further setup is required to run tests or programs directly on a device. + +First make sure you have a valid developer certificate and have setup your device properly +to run apps signed by your developer certificate. Then install the libimobiledevice and +ideviceinstaller tools from https://www.libimobiledevice.org/. Use the HEAD versions from +source; the stable versions have bugs that prevents the Go exec wrapper to install and run +apps. + +Second, the Go exec wrapper must be told the developer account signing identity, the team +id and a provisioned bundle id to use. They're specified with the environment variables +GOIOS_DEV_ID, GOIOS_TEAM_ID and GOIOS_APP_ID. The detect.go program in this directory will +attempt to auto-detect suitable values. Run it as + + go run detect.go + +which will output something similar to + + export GOIOS_DEV_ID="iPhone Developer: xxx@yyy.zzz (XXXXXXXX)" + export GOIOS_APP_ID=YYYYYYYY.some.bundle.id + export GOIOS_TEAM_ID=ZZZZZZZZ + +If you have multiple devices connected, specify the device UDID with the GOIOS_DEVICE_ID +variable. Use `idevice_id -l` to list all available UDIDs. Then, setting GOARCH to arm64 +will select the device: + + GOOS=ios GOARCH=arm64 CGO_ENABLED=1 CC_FOR_TARGET=$(pwd)/../misc/ios/clangwrap.sh ./all.bash + +Note that the go_darwin_$GOARCH_exec wrapper uninstalls any existing app identified by +the bundle id before installing a new app. If the uninstalled app is the last app by +the developer identity, the device might also remove the permission to run apps from +that developer, and the exec wrapper will fail to install the new app. To avoid that, +install another app with the same developer identity but with a different bundle id. +That way, the permission to install apps is held on to while the primary app is +uninstalled. diff --git a/misc/ios/clangwrap.sh b/misc/ios/clangwrap.sh new file mode 100755 index 0000000..8f7b439 --- /dev/null +++ b/misc/ios/clangwrap.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# This uses the latest available iOS SDK, which is recommended. +# To select a specific SDK, run 'xcodebuild -showsdks' +# to see the available SDKs and replace iphoneos with one of them. +if [ "$GOARCH" == "arm64" ]; then + SDK=iphoneos + PLATFORM=ios + CLANGARCH="arm64" +else + SDK=iphonesimulator + PLATFORM=ios-simulator + CLANGARCH="x86_64" +fi + +SDK_PATH=`xcrun --sdk $SDK --show-sdk-path` +export IPHONEOS_DEPLOYMENT_TARGET=5.1 +# cmd/cgo doesn't support llvm-gcc-4.2, so we have to use clang. +CLANG=`xcrun --sdk $SDK --find clang` + +exec "$CLANG" -arch $CLANGARCH -isysroot "$SDK_PATH" -m${PLATFORM}-version-min=12.0 "$@" diff --git a/misc/ios/detect.go b/misc/ios/detect.go new file mode 100644 index 0000000..1cb8ae5 --- /dev/null +++ b/misc/ios/detect.go @@ -0,0 +1,134 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build ignore +// +build ignore + +// detect attempts to autodetect the correct +// values of the environment variables +// used by go_ios_exec. +// detect shells out to ideviceinfo, a third party program that can +// be obtained by following the instructions at +// https://github.com/libimobiledevice/libimobiledevice. +package main + +import ( + "bytes" + "crypto/x509" + "fmt" + "os" + "os/exec" + "strings" +) + +func main() { + udids := getLines(exec.Command("idevice_id", "-l")) + if len(udids) == 0 { + fail("no udid found; is a device connected?") + } + + mps := detectMobileProvisionFiles(udids) + if len(mps) == 0 { + fail("did not find mobile provision matching device udids %q", udids) + } + + fmt.Println("# Available provisioning profiles below.") + fmt.Println("# NOTE: Any existing app on the device with the app id specified by GOIOS_APP_ID") + fmt.Println("# will be overwritten when running Go programs.") + for _, mp := range mps { + fmt.Println() + f, err := os.CreateTemp("", "go_ios_detect_") + check(err) + fname := f.Name() + defer os.Remove(fname) + + out := output(parseMobileProvision(mp)) + _, err = f.Write(out) + check(err) + check(f.Close()) + + cert, err := plistExtract(fname, "DeveloperCertificates:0") + check(err) + pcert, err := x509.ParseCertificate(cert) + check(err) + fmt.Printf("export GOIOS_DEV_ID=\"%s\"\n", pcert.Subject.CommonName) + + appID, err := plistExtract(fname, "Entitlements:application-identifier") + check(err) + fmt.Printf("export GOIOS_APP_ID=%s\n", appID) + + teamID, err := plistExtract(fname, "Entitlements:com.apple.developer.team-identifier") + check(err) + fmt.Printf("export GOIOS_TEAM_ID=%s\n", teamID) + } +} + +func detectMobileProvisionFiles(udids [][]byte) []string { + cmd := exec.Command("mdfind", "-name", ".mobileprovision") + lines := getLines(cmd) + + var files []string + for _, line := range lines { + if len(line) == 0 { + continue + } + xmlLines := getLines(parseMobileProvision(string(line))) + matches := 0 + for _, udid := range udids { + for _, xmlLine := range xmlLines { + if bytes.Contains(xmlLine, udid) { + matches++ + } + } + } + if matches == len(udids) { + files = append(files, string(line)) + } + } + return files +} + +func parseMobileProvision(fname string) *exec.Cmd { + return exec.Command("security", "cms", "-D", "-i", string(fname)) +} + +func plistExtract(fname string, path string) ([]byte, error) { + out, err := exec.Command("/usr/libexec/PlistBuddy", "-c", "Print "+path, fname).CombinedOutput() + if err != nil { + return nil, err + } + return bytes.TrimSpace(out), nil +} + +func getLines(cmd *exec.Cmd) [][]byte { + out := output(cmd) + lines := bytes.Split(out, []byte("\n")) + // Skip the empty line at the end. + if len(lines[len(lines)-1]) == 0 { + lines = lines[:len(lines)-1] + } + return lines +} + +func output(cmd *exec.Cmd) []byte { + out, err := cmd.Output() + if err != nil { + fmt.Println(strings.Join(cmd.Args, "\n")) + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + return out +} + +func check(err error) { + if err != nil { + fail(err.Error()) + } +} + +func fail(msg string, v ...interface{}) { + fmt.Fprintf(os.Stderr, msg, v...) + fmt.Fprintln(os.Stderr) + os.Exit(1) +} diff --git a/misc/ios/go_ios_exec.go b/misc/ios/go_ios_exec.go new file mode 100644 index 0000000..c275dd3 --- /dev/null +++ b/misc/ios/go_ios_exec.go @@ -0,0 +1,911 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This program can be used as go_ios_$GOARCH_exec by the Go tool. +// It executes binaries on an iOS device using the XCode toolchain +// and the ios-deploy program: https://github.com/phonegap/ios-deploy +// +// This script supports an extra flag, -lldb, that pauses execution +// just before the main program begins and allows the user to control +// the remote lldb session. This flag is appended to the end of the +// script's arguments and is not passed through to the underlying +// binary. +// +// This script requires that three environment variables be set: +// +// GOIOS_DEV_ID: The codesigning developer id or certificate identifier +// GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids. +// GOIOS_TEAM_ID: The team id that owns the app id prefix. +// +// $GOROOT/misc/ios contains a script, detect.go, that attempts to autodetect these. +package main + +import ( + "bytes" + "encoding/xml" + "errors" + "fmt" + "go/build" + "io" + "log" + "net" + "os" + "os/exec" + "os/signal" + "path/filepath" + "runtime" + "strconv" + "strings" + "syscall" + "time" +) + +const debug = false + +var tmpdir string + +var ( + devID string + appID string + teamID string + bundleID string + deviceID string +) + +// lock is a file lock to serialize iOS runs. It is global to avoid the +// garbage collector finalizing it, closing the file and releasing the +// lock prematurely. +var lock *os.File + +func main() { + log.SetFlags(0) + log.SetPrefix("go_ios_exec: ") + if debug { + log.Println(strings.Join(os.Args, " ")) + } + if len(os.Args) < 2 { + log.Fatal("usage: go_ios_exec a.out") + } + + // For compatibility with the old builders, use a fallback bundle ID + bundleID = "golang.gotest" + + exitCode, err := runMain() + if err != nil { + log.Fatalf("%v\n", err) + } + os.Exit(exitCode) +} + +func runMain() (int, error) { + var err error + tmpdir, err = os.MkdirTemp("", "go_ios_exec_") + if err != nil { + return 1, err + } + if !debug { + defer os.RemoveAll(tmpdir) + } + + appdir := filepath.Join(tmpdir, "gotest.app") + os.RemoveAll(appdir) + + if err := assembleApp(appdir, os.Args[1]); err != nil { + return 1, err + } + + // This wrapper uses complicated machinery to run iOS binaries. It + // works, but only when running one binary at a time. + // Use a file lock to make sure only one wrapper is running at a time. + // + // The lock file is never deleted, to avoid concurrent locks on distinct + // files with the same path. + lockName := filepath.Join(os.TempDir(), "go_ios_exec-"+deviceID+".lock") + lock, err = os.OpenFile(lockName, os.O_CREATE|os.O_RDONLY, 0666) + if err != nil { + return 1, err + } + if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil { + return 1, err + } + + if goarch := os.Getenv("GOARCH"); goarch == "arm64" { + err = runOnDevice(appdir) + } else { + err = runOnSimulator(appdir) + } + if err != nil { + // If the lldb driver completed with an exit code, use that. + if err, ok := err.(*exec.ExitError); ok { + if ws, ok := err.Sys().(interface{ ExitStatus() int }); ok { + return ws.ExitStatus(), nil + } + } + return 1, err + } + return 0, nil +} + +func runOnSimulator(appdir string) error { + if err := installSimulator(appdir); err != nil { + return err + } + + return runSimulator(appdir, bundleID, os.Args[2:]) +} + +func runOnDevice(appdir string) error { + // e.g. B393DDEB490947F5A463FD074299B6C0AXXXXXXX + devID = getenv("GOIOS_DEV_ID") + + // e.g. Z8B3JBXXXX.org.golang.sample, Z8B3JBXXXX prefix is available at + // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID. + appID = getenv("GOIOS_APP_ID") + + // e.g. Z8B3JBXXXX, available at + // https://developer.apple.com/membercenter/index.action#accountSummary as Team ID. + teamID = getenv("GOIOS_TEAM_ID") + + // Device IDs as listed with ios-deploy -c. + deviceID = os.Getenv("GOIOS_DEVICE_ID") + + if _, id, ok := strings.Cut(appID, "."); ok { + bundleID = id + } + + if err := signApp(appdir); err != nil { + return err + } + + if err := uninstallDevice(bundleID); err != nil { + return err + } + + if err := installDevice(appdir); err != nil { + return err + } + + if err := mountDevImage(); err != nil { + return err + } + + // Kill any hanging debug bridges that might take up port 3222. + exec.Command("killall", "idevicedebugserverproxy").Run() + + closer, err := startDebugBridge() + if err != nil { + return err + } + defer closer() + + return runDevice(appdir, bundleID, os.Args[2:]) +} + +func getenv(envvar string) string { + s := os.Getenv(envvar) + if s == "" { + log.Fatalf("%s not set\nrun $GOROOT/misc/ios/detect.go to attempt to autodetect", envvar) + } + return s +} + +func assembleApp(appdir, bin string) error { + if err := os.MkdirAll(appdir, 0755); err != nil { + return err + } + + if err := cp(filepath.Join(appdir, "gotest"), bin); err != nil { + return err + } + + pkgpath, err := copyLocalData(appdir) + if err != nil { + return err + } + + entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist") + if err := os.WriteFile(entitlementsPath, []byte(entitlementsPlist()), 0744); err != nil { + return err + } + if err := os.WriteFile(filepath.Join(appdir, "Info.plist"), []byte(infoPlist(pkgpath)), 0744); err != nil { + return err + } + if err := os.WriteFile(filepath.Join(appdir, "ResourceRules.plist"), []byte(resourceRules), 0744); err != nil { + return err + } + return nil +} + +func signApp(appdir string) error { + entitlementsPath := filepath.Join(tmpdir, "Entitlements.plist") + cmd := exec.Command( + "codesign", + "-f", + "-s", devID, + "--entitlements", entitlementsPath, + appdir, + ) + if debug { + log.Println(strings.Join(cmd.Args, " ")) + } + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("codesign: %v", err) + } + return nil +} + +// mountDevImage ensures a developer image is mounted on the device. +// The image contains the device lldb server for idevicedebugserverproxy +// to connect to. +func mountDevImage() error { + // Check for existing mount. + cmd := idevCmd(exec.Command("ideviceimagemounter", "-l", "-x")) + out, err := cmd.CombinedOutput() + if err != nil { + os.Stderr.Write(out) + return fmt.Errorf("ideviceimagemounter: %v", err) + } + var info struct { + Dict struct { + Data []byte `xml:",innerxml"` + } `xml:"dict"` + } + if err := xml.Unmarshal(out, &info); err != nil { + return fmt.Errorf("mountDevImage: failed to decode mount information: %v", err) + } + dict, err := parsePlistDict(info.Dict.Data) + if err != nil { + return fmt.Errorf("mountDevImage: failed to parse mount information: %v", err) + } + if dict["ImagePresent"] == "true" && dict["Status"] == "Complete" { + return nil + } + // Some devices only give us an ImageSignature key. + if _, exists := dict["ImageSignature"]; exists { + return nil + } + // No image is mounted. Find a suitable image. + imgPath, err := findDevImage() + if err != nil { + return err + } + sigPath := imgPath + ".signature" + cmd = idevCmd(exec.Command("ideviceimagemounter", imgPath, sigPath)) + if out, err := cmd.CombinedOutput(); err != nil { + os.Stderr.Write(out) + return fmt.Errorf("ideviceimagemounter: %v", err) + } + return nil +} + +// findDevImage use the device iOS version and build to locate a suitable +// developer image. +func findDevImage() (string, error) { + cmd := idevCmd(exec.Command("ideviceinfo")) + out, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("ideviceinfo: %v", err) + } + var iosVer, buildVer string + lines := bytes.Split(out, []byte("\n")) + for _, line := range lines { + key, val, ok := strings.Cut(string(line), ": ") + if !ok { + continue + } + switch key { + case "ProductVersion": + iosVer = val + case "BuildVersion": + buildVer = val + } + } + if iosVer == "" || buildVer == "" { + return "", errors.New("failed to parse ideviceinfo output") + } + verSplit := strings.Split(iosVer, ".") + if len(verSplit) > 2 { + // Developer images are specific to major.minor ios version. + // Cut off the patch version. + iosVer = strings.Join(verSplit[:2], ".") + } + sdkBase := "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport" + patterns := []string{fmt.Sprintf("%s (%s)", iosVer, buildVer), fmt.Sprintf("%s (*)", iosVer), fmt.Sprintf("%s*", iosVer)} + for _, pattern := range patterns { + matches, err := filepath.Glob(filepath.Join(sdkBase, pattern, "DeveloperDiskImage.dmg")) + if err != nil { + return "", fmt.Errorf("findDevImage: %v", err) + } + if len(matches) > 0 { + return matches[0], nil + } + } + return "", fmt.Errorf("failed to find matching developer image for iOS version %s build %s", iosVer, buildVer) +} + +// startDebugBridge ensures that the idevicedebugserverproxy runs on +// port 3222. +func startDebugBridge() (func(), error) { + errChan := make(chan error, 1) + cmd := idevCmd(exec.Command("idevicedebugserverproxy", "3222")) + var stderr bytes.Buffer + cmd.Stderr = &stderr + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("idevicedebugserverproxy: %v", err) + } + go func() { + if err := cmd.Wait(); err != nil { + if _, ok := err.(*exec.ExitError); ok { + errChan <- fmt.Errorf("idevicedebugserverproxy: %s", stderr.Bytes()) + } else { + errChan <- fmt.Errorf("idevicedebugserverproxy: %v", err) + } + } + errChan <- nil + }() + closer := func() { + cmd.Process.Kill() + <-errChan + } + // Dial localhost:3222 to ensure the proxy is ready. + delay := time.Second / 4 + for attempt := 0; attempt < 5; attempt++ { + conn, err := net.DialTimeout("tcp", "localhost:3222", 5*time.Second) + if err == nil { + conn.Close() + return closer, nil + } + select { + case <-time.After(delay): + delay *= 2 + case err := <-errChan: + return nil, err + } + } + closer() + return nil, errors.New("failed to set up idevicedebugserverproxy") +} + +// findDeviceAppPath returns the device path to the app with the +// given bundle ID. It parses the output of ideviceinstaller -l -o xml, +// looking for the bundle ID and the corresponding Path value. +func findDeviceAppPath(bundleID string) (string, error) { + cmd := idevCmd(exec.Command("ideviceinstaller", "-l", "-o", "xml")) + out, err := cmd.CombinedOutput() + if err != nil { + os.Stderr.Write(out) + return "", fmt.Errorf("ideviceinstaller: -l -o xml %v", err) + } + var list struct { + Apps []struct { + Data []byte `xml:",innerxml"` + } `xml:"array>dict"` + } + if err := xml.Unmarshal(out, &list); err != nil { + return "", fmt.Errorf("failed to parse ideviceinstaller output: %v", err) + } + for _, app := range list.Apps { + values, err := parsePlistDict(app.Data) + if err != nil { + return "", fmt.Errorf("findDeviceAppPath: failed to parse app dict: %v", err) + } + if values["CFBundleIdentifier"] == bundleID { + if path, ok := values["Path"]; ok { + return path, nil + } + } + } + return "", fmt.Errorf("failed to find device path for bundle: %s", bundleID) +} + +// Parse an xml encoded plist. Plist values are mapped to string. +func parsePlistDict(dict []byte) (map[string]string, error) { + d := xml.NewDecoder(bytes.NewReader(dict)) + values := make(map[string]string) + var key string + var hasKey bool + for { + tok, err := d.Token() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + if tok, ok := tok.(xml.StartElement); ok { + if tok.Name.Local == "key" { + if err := d.DecodeElement(&key, &tok); err != nil { + return nil, err + } + hasKey = true + } else if hasKey { + var val string + var err error + switch n := tok.Name.Local; n { + case "true", "false": + // Bools are represented as <true/> and <false/>. + val = n + err = d.Skip() + default: + err = d.DecodeElement(&val, &tok) + } + if err != nil { + return nil, err + } + values[key] = val + hasKey = false + } else { + if err := d.Skip(); err != nil { + return nil, err + } + } + } + } + return values, nil +} + +func installSimulator(appdir string) error { + cmd := exec.Command( + "xcrun", "simctl", "install", + "booted", // Install to the booted simulator. + appdir, + ) + if out, err := cmd.CombinedOutput(); err != nil { + os.Stderr.Write(out) + return fmt.Errorf("xcrun simctl install booted %q: %v", appdir, err) + } + return nil +} + +func uninstallDevice(bundleID string) error { + cmd := idevCmd(exec.Command( + "ideviceinstaller", + "-U", bundleID, + )) + if out, err := cmd.CombinedOutput(); err != nil { + os.Stderr.Write(out) + return fmt.Errorf("ideviceinstaller -U %q: %s", bundleID, err) + } + return nil +} + +func installDevice(appdir string) error { + attempt := 0 + for { + cmd := idevCmd(exec.Command( + "ideviceinstaller", + "-i", appdir, + )) + if out, err := cmd.CombinedOutput(); err != nil { + // Sometimes, installing the app fails for some reason. + // Give the device a few seconds and try again. + if attempt < 5 { + time.Sleep(5 * time.Second) + attempt++ + continue + } + os.Stderr.Write(out) + return fmt.Errorf("ideviceinstaller -i %q: %v (%d attempts)", appdir, err, attempt) + } + return nil + } +} + +func idevCmd(cmd *exec.Cmd) *exec.Cmd { + if deviceID != "" { + // Inject -u device_id after the executable, but before the arguments. + args := []string{cmd.Args[0], "-u", deviceID} + cmd.Args = append(args, cmd.Args[1:]...) + } + return cmd +} + +func runSimulator(appdir, bundleID string, args []string) error { + cmd := exec.Command( + "xcrun", "simctl", "launch", + "--wait-for-debugger", + "booted", + bundleID, + ) + out, err := cmd.CombinedOutput() + if err != nil { + os.Stderr.Write(out) + return fmt.Errorf("xcrun simctl launch booted %q: %v", bundleID, err) + } + var processID int + var ignore string + if _, err := fmt.Sscanf(string(out), "%s %d", &ignore, &processID); err != nil { + return fmt.Errorf("runSimulator: couldn't find processID from `simctl launch`: %v (%q)", err, out) + } + _, err = runLLDB("ios-simulator", appdir, strconv.Itoa(processID), args) + return err +} + +func runDevice(appdir, bundleID string, args []string) error { + attempt := 0 + for { + // The device app path reported by the device might be stale, so retry + // the lookup of the device path along with the lldb launching below. + deviceapp, err := findDeviceAppPath(bundleID) + if err != nil { + // The device app path might not yet exist for a newly installed app. + if attempt == 5 { + return err + } + attempt++ + time.Sleep(5 * time.Second) + continue + } + out, err := runLLDB("remote-ios", appdir, deviceapp, args) + // If the program was not started it can be retried without papering over + // real test failures. + started := bytes.HasPrefix(out, []byte("lldb: running program")) + if started || err == nil || attempt == 5 { + return err + } + // Sometimes, the app was not yet ready to launch or the device path was + // stale. Retry. + attempt++ + time.Sleep(5 * time.Second) + } +} + +func runLLDB(target, appdir, deviceapp string, args []string) ([]byte, error) { + var env []string + for _, e := range os.Environ() { + // Don't override TMPDIR, HOME, GOCACHE on the device. + if strings.HasPrefix(e, "TMPDIR=") || strings.HasPrefix(e, "HOME=") || strings.HasPrefix(e, "GOCACHE=") { + continue + } + env = append(env, e) + } + lldb := exec.Command( + "python", + "-", // Read script from stdin. + target, + appdir, + deviceapp, + ) + lldb.Args = append(lldb.Args, args...) + lldb.Env = env + lldb.Stdin = strings.NewReader(lldbDriver) + lldb.Stdout = os.Stdout + var out bytes.Buffer + lldb.Stderr = io.MultiWriter(&out, os.Stderr) + err := lldb.Start() + if err == nil { + // Forward SIGQUIT to the lldb driver which in turn will forward + // to the running program. + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGQUIT) + proc := lldb.Process + go func() { + for sig := range sigs { + proc.Signal(sig) + } + }() + err = lldb.Wait() + signal.Stop(sigs) + close(sigs) + } + return out.Bytes(), err +} + +func copyLocalDir(dst, src string) error { + if err := os.Mkdir(dst, 0755); err != nil { + return err + } + + d, err := os.Open(src) + if err != nil { + return err + } + defer d.Close() + fi, err := d.Readdir(-1) + if err != nil { + return err + } + + for _, f := range fi { + if f.IsDir() { + if f.Name() == "testdata" { + if err := cp(dst, filepath.Join(src, f.Name())); err != nil { + return err + } + } + continue + } + if err := cp(dst, filepath.Join(src, f.Name())); err != nil { + return err + } + } + return nil +} + +func cp(dst, src string) error { + out, err := exec.Command("cp", "-a", src, dst).CombinedOutput() + if err != nil { + os.Stderr.Write(out) + } + return err +} + +func copyLocalData(dstbase string) (pkgpath string, err error) { + cwd, err := os.Getwd() + if err != nil { + return "", err + } + + finalPkgpath, underGoRoot, err := subdir() + if err != nil { + return "", err + } + cwd = strings.TrimSuffix(cwd, finalPkgpath) + + // Copy all immediate files and testdata directories between + // the package being tested and the source root. + pkgpath = "" + for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) { + if debug { + log.Printf("copying %s", pkgpath) + } + pkgpath = filepath.Join(pkgpath, element) + dst := filepath.Join(dstbase, pkgpath) + src := filepath.Join(cwd, pkgpath) + if err := copyLocalDir(dst, src); err != nil { + return "", err + } + } + + if underGoRoot { + // Copy timezone file. + // + // Typical apps have the zoneinfo.zip in the root of their app bundle, + // read by the time package as the working directory at initialization. + // As we move the working directory to the GOROOT pkg directory, we + // install the zoneinfo.zip file in the pkgpath. + err := cp( + filepath.Join(dstbase, pkgpath), + filepath.Join(cwd, "lib", "time", "zoneinfo.zip"), + ) + if err != nil { + return "", err + } + // Copy src/runtime/textflag.h for (at least) Test386EndToEnd in + // cmd/asm/internal/asm. + runtimePath := filepath.Join(dstbase, "src", "runtime") + if err := os.MkdirAll(runtimePath, 0755); err != nil { + return "", err + } + err = cp( + filepath.Join(runtimePath, "textflag.h"), + filepath.Join(cwd, "src", "runtime", "textflag.h"), + ) + if err != nil { + return "", err + } + } + + return finalPkgpath, nil +} + +// subdir determines the package based on the current working directory, +// and returns the path to the package source relative to $GOROOT (or $GOPATH). +func subdir() (pkgpath string, underGoRoot bool, err error) { + cwd, err := os.Getwd() + if err != nil { + return "", false, err + } + cwd, err = filepath.EvalSymlinks(cwd) + if err != nil { + log.Fatal(err) + } + goroot, err := filepath.EvalSymlinks(runtime.GOROOT()) + if err != nil { + return "", false, err + } + if strings.HasPrefix(cwd, goroot) { + subdir, err := filepath.Rel(goroot, cwd) + if err != nil { + return "", false, err + } + return subdir, true, nil + } + + for _, p := range filepath.SplitList(build.Default.GOPATH) { + pabs, err := filepath.EvalSymlinks(p) + if err != nil { + return "", false, err + } + if !strings.HasPrefix(cwd, pabs) { + continue + } + subdir, err := filepath.Rel(pabs, cwd) + if err == nil { + return subdir, false, nil + } + } + return "", false, fmt.Errorf( + "working directory %q is not in either GOROOT(%q) or GOPATH(%q)", + cwd, + runtime.GOROOT(), + build.Default.GOPATH, + ) +} + +func infoPlist(pkgpath string) string { + return `<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> +<key>CFBundleName</key><string>golang.gotest</string> +<key>CFBundleSupportedPlatforms</key><array><string>iPhoneOS</string></array> +<key>CFBundleExecutable</key><string>gotest</string> +<key>CFBundleVersion</key><string>1.0</string> +<key>CFBundleShortVersionString</key><string>1.0</string> +<key>CFBundleIdentifier</key><string>` + bundleID + `</string> +<key>CFBundleResourceSpecification</key><string>ResourceRules.plist</string> +<key>LSRequiresIPhoneOS</key><true/> +<key>CFBundleDisplayName</key><string>gotest</string> +<key>GoExecWrapperWorkingDirectory</key><string>` + pkgpath + `</string> +</dict> +</plist> +` +} + +func entitlementsPlist() string { + return `<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>keychain-access-groups</key> + <array><string>` + appID + `</string></array> + <key>get-task-allow</key> + <true/> + <key>application-identifier</key> + <string>` + appID + `</string> + <key>com.apple.developer.team-identifier</key> + <string>` + teamID + `</string> +</dict> +</plist> +` +} + +const resourceRules = `<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>rules</key> + <dict> + <key>.*</key> + <true/> + <key>Info.plist</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <integer>10</integer> + </dict> + <key>ResourceRules.plist</key> + <dict> + <key>omit</key> + <true/> + <key>weight</key> + <integer>100</integer> + </dict> + </dict> +</dict> +</plist> +` + +const lldbDriver = ` +import sys +import os +import signal + +platform, exe, device_exe_or_pid, args = sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4:] + +env = [] +for k, v in os.environ.items(): + env.append(k + "=" + v) + +sys.path.append('/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python') + +import lldb + +debugger = lldb.SBDebugger.Create() +debugger.SetAsync(True) +debugger.SkipLLDBInitFiles(True) + +err = lldb.SBError() +target = debugger.CreateTarget(exe, None, platform, True, err) +if not target.IsValid() or not err.Success(): + sys.stderr.write("lldb: failed to setup up target: %s\n" % (err)) + sys.exit(1) + +listener = debugger.GetListener() + +if platform == 'remote-ios': + target.modules[0].SetPlatformFileSpec(lldb.SBFileSpec(device_exe_or_pid)) + process = target.ConnectRemote(listener, 'connect://localhost:3222', None, err) +else: + process = target.AttachToProcessWithID(listener, int(device_exe_or_pid), err) + +if not err.Success(): + sys.stderr.write("lldb: failed to connect to remote target %s: %s\n" % (device_exe_or_pid, err)) + sys.exit(1) + +# Don't stop on signals. +sigs = process.GetUnixSignals() +for i in range(0, sigs.GetNumSignals()): + sig = sigs.GetSignalAtIndex(i) + sigs.SetShouldStop(sig, False) + sigs.SetShouldNotify(sig, False) + +event = lldb.SBEvent() +running = False +prev_handler = None + +def signal_handler(signal, frame): + process.Signal(signal) + +def run_program(): + # Forward SIGQUIT to the program. + prev_handler = signal.signal(signal.SIGQUIT, signal_handler) + # Tell the Go driver that the program is running and should not be retried. + sys.stderr.write("lldb: running program\n") + running = True + # Process is stopped at attach/launch. Let it run. + process.Continue() + +if platform != 'remote-ios': + # For the local emulator the program is ready to run. + # For remote device runs, we need to wait for eStateConnected, + # below. + run_program() + +while True: + if not listener.WaitForEvent(1, event): + continue + if not lldb.SBProcess.EventIsProcessEvent(event): + continue + if running: + # Pass through stdout and stderr. + while True: + out = process.GetSTDOUT(8192) + if not out: + break + sys.stdout.write(out) + while True: + out = process.GetSTDERR(8192) + if not out: + break + sys.stderr.write(out) + state = process.GetStateFromEvent(event) + if state in [lldb.eStateCrashed, lldb.eStateDetached, lldb.eStateUnloaded, lldb.eStateExited]: + if running: + signal.signal(signal.SIGQUIT, prev_handler) + break + elif state == lldb.eStateConnected: + if platform == 'remote-ios': + process.RemoteLaunch(args, env, None, None, None, None, 0, False, err) + if not err.Success(): + sys.stderr.write("lldb: failed to launch remote process: %s\n" % (err)) + process.Kill() + debugger.Terminate() + sys.exit(1) + run_program() + +exitStatus = process.GetExitStatus() +exitDesc = process.GetExitDescription() +process.Kill() +debugger.Terminate() +if exitStatus == 0 and exitDesc is not None: + # Ensure tests fail when killed by a signal. + exitStatus = 123 + +sys.exit(exitStatus) +` diff --git a/misc/linkcheck/linkcheck.go b/misc/linkcheck/linkcheck.go new file mode 100644 index 0000000..efe4009 --- /dev/null +++ b/misc/linkcheck/linkcheck.go @@ -0,0 +1,191 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The linkcheck command finds missing links in the godoc website. +// It crawls a URL recursively and notes URLs and URL fragments +// that it's seen and prints a report of missing links at the end. +package main + +import ( + "errors" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "regexp" + "strings" + "sync" +) + +var ( + root = flag.String("root", "http://localhost:6060", "Root to crawl") + verbose = flag.Bool("verbose", false, "verbose") +) + +var wg sync.WaitGroup // outstanding fetches +var urlq = make(chan string) // URLs to crawl + +// urlFrag is a URL and its optional #fragment (without the #) +type urlFrag struct { + url, frag string +} + +var ( + mu sync.Mutex + crawled = make(map[string]bool) // URL without fragment -> true + neededFrags = make(map[urlFrag][]string) // URL#frag -> who needs it +) + +var aRx = regexp.MustCompile(`<a href=['"]?(/[^\s'">]+)`) + +// Owned by crawlLoop goroutine: +var ( + linkSources = make(map[string][]string) // url no fragment -> sources + fragExists = make(map[urlFrag]bool) + problems []string +) + +func localLinks(body string) (links []string) { + seen := map[string]bool{} + mv := aRx.FindAllStringSubmatch(body, -1) + for _, m := range mv { + ref := m[1] + if strings.HasPrefix(ref, "/src/") { + continue + } + if !seen[ref] { + seen[ref] = true + links = append(links, m[1]) + } + } + return +} + +var idRx = regexp.MustCompile(`\bid=['"]?([^\s'">]+)`) + +func pageIDs(body string) (ids []string) { + mv := idRx.FindAllStringSubmatch(body, -1) + for _, m := range mv { + ids = append(ids, m[1]) + } + return +} + +// url may contain a #fragment, and the fragment is then noted as needing to exist. +func crawl(url string, sourceURL string) { + if strings.Contains(url, "/devel/release") { + return + } + mu.Lock() + defer mu.Unlock() + if u, frag, ok := strings.Cut(url, "#"); ok { + url = u + if frag != "" { + uf := urlFrag{url, frag} + neededFrags[uf] = append(neededFrags[uf], sourceURL) + } + } + if crawled[url] { + return + } + crawled[url] = true + + wg.Add(1) + go func() { + urlq <- url + }() +} + +func addProblem(url, errmsg string) { + msg := fmt.Sprintf("Error on %s: %s (from %s)", url, errmsg, linkSources[url]) + if *verbose { + log.Print(msg) + } + problems = append(problems, msg) +} + +func crawlLoop() { + for url := range urlq { + if err := doCrawl(url); err != nil { + addProblem(url, err.Error()) + } + } +} + +func doCrawl(url string) error { + defer wg.Done() + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + res, err := http.DefaultTransport.RoundTrip(req) + if err != nil { + return err + } + // Handle redirects. + if res.StatusCode/100 == 3 { + newURL, err := res.Location() + if err != nil { + return fmt.Errorf("resolving redirect: %v", err) + } + if !strings.HasPrefix(newURL.String(), *root) { + // Skip off-site redirects. + return nil + } + crawl(newURL.String(), url) + return nil + } + if res.StatusCode != 200 { + return errors.New(res.Status) + } + slurp, err := io.ReadAll(res.Body) + res.Body.Close() + if err != nil { + log.Fatalf("Error reading %s body: %v", url, err) + } + if *verbose { + log.Printf("Len of %s: %d", url, len(slurp)) + } + body := string(slurp) + for _, ref := range localLinks(body) { + if *verbose { + log.Printf(" links to %s", ref) + } + dest := *root + ref + linkSources[dest] = append(linkSources[dest], url) + crawl(dest, url) + } + for _, id := range pageIDs(body) { + if *verbose { + log.Printf(" url %s has #%s", url, id) + } + fragExists[urlFrag{url, id}] = true + } + return nil +} + +func main() { + flag.Parse() + + go crawlLoop() + crawl(*root, "") + + wg.Wait() + close(urlq) + for uf, needers := range neededFrags { + if !fragExists[uf] { + problems = append(problems, fmt.Sprintf("Missing fragment for %+v from %v", uf, needers)) + } + } + + for _, s := range problems { + fmt.Println(s) + } + if len(problems) > 0 { + os.Exit(1) + } +} diff --git a/misc/reboot/experiment_toolid_test.go b/misc/reboot/experiment_toolid_test.go new file mode 100644 index 0000000..87a828e --- /dev/null +++ b/misc/reboot/experiment_toolid_test.go @@ -0,0 +1,101 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build explicit +// +build explicit + +// Package experiment_toolid_test verifies that GOEXPERIMENT settings built +// into the toolchain influence tool ids in the Go command. +// This test requires bootstrapping the toolchain twice, so it's very expensive. +// It must be run explicitly with -tags=explicit. +// Verifies golang.org/issue/33091. +package reboot_test + +import ( + "bytes" + "os" + "os/exec" + "path/filepath" + "runtime" + "testing" +) + +func TestExperimentToolID(t *testing.T) { + // Set up GOROOT + goroot, err := os.MkdirTemp("", "experiment-goroot") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(goroot) + + gorootSrc := filepath.Join(goroot, "src") + if err := overlayDir(gorootSrc, filepath.Join(runtime.GOROOT(), "src")); err != nil { + t.Fatal(err) + } + + if err := os.WriteFile(filepath.Join(goroot, "VERSION"), []byte("go1.999"), 0666); err != nil { + t.Fatal(err) + } + env := append(os.Environ(), "GOROOT=", "GOROOT_BOOTSTRAP="+runtime.GOROOT()) + + // Use a clean cache. + gocache, err := os.MkdirTemp("", "experiment-gocache") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(gocache) + env = append(env, "GOCACHE="+gocache) + + // Build the toolchain without GOEXPERIMENT. + var makeScript string + switch runtime.GOOS { + case "windows": + makeScript = "make.bat" + case "plan9": + makeScript = "make.rc" + default: + makeScript = "make.bash" + } + makeScriptPath := filepath.Join(runtime.GOROOT(), "src", makeScript) + runCmd(t, gorootSrc, env, makeScriptPath) + + // Verify compiler version string. + goCmdPath := filepath.Join(goroot, "bin", "go") + if runtime.GOOS == "windows" { + goCmdPath += ".exe" + } + gotVersion := bytes.TrimSpace(runCmd(t, gorootSrc, env, goCmdPath, "tool", "compile", "-V=full")) + wantVersion := []byte(`compile version go1.999`) + if !bytes.Equal(gotVersion, wantVersion) { + t.Errorf("compile version without experiment: got %q, want %q", gotVersion, wantVersion) + } + + // Build a package in a mode not handled by the make script. + runCmd(t, gorootSrc, env, goCmdPath, "build", "-race", "archive/tar") + + // Rebuild the toolchain with GOEXPERIMENT. + env = append(env, "GOEXPERIMENT=fieldtrack") + runCmd(t, gorootSrc, env, makeScriptPath) + + // Verify compiler version string. + gotVersion = bytes.TrimSpace(runCmd(t, gorootSrc, env, goCmdPath, "tool", "compile", "-V=full")) + wantVersion = []byte(`compile version go1.999 X:fieldtrack,framepointer`) + if !bytes.Equal(gotVersion, wantVersion) { + t.Errorf("compile version with experiment: got %q, want %q", gotVersion, wantVersion) + } + + // Build the same package. We should not get a cache conflict. + runCmd(t, gorootSrc, env, goCmdPath, "build", "-race", "archive/tar") +} + +func runCmd(t *testing.T, dir string, env []string, path string, args ...string) []byte { + cmd := exec.Command(path, args...) + cmd.Dir = dir + cmd.Env = env + out, err := cmd.Output() + if err != nil { + t.Fatal(err) + } + return out +} diff --git a/misc/reboot/overlaydir_test.go b/misc/reboot/overlaydir_test.go new file mode 100644 index 0000000..71faf09 --- /dev/null +++ b/misc/reboot/overlaydir_test.go @@ -0,0 +1,85 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package reboot_test + +import ( + "io" + "io/fs" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.WalkDir(srcRoot, func(srcPath string, entry fs.DirEntry, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + if filepath.Base(srcPath) == "testdata" { + // We're just building, so no need to copy those. + return fs.SkipDir + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + info, err := entry.Info() + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always make copies of directories. + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If we can use a hard link, do that instead of copying bytes. + // Go builds don't like symlinks in some cases, such as go:embed. + if err := os.Link(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/reboot/reboot_test.go b/misc/reboot/reboot_test.go new file mode 100644 index 0000000..a1b513b --- /dev/null +++ b/misc/reboot/reboot_test.go @@ -0,0 +1,70 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package reboot_test verifies that the current GOROOT can be used to bootstrap +// itself. +package reboot_test + +import ( + "os" + "os/exec" + "path/filepath" + "runtime" + "testing" + "time" +) + +func TestRepeatBootstrap(t *testing.T) { + if testing.Short() { + t.Skipf("skipping test that rebuilds the entire toolchain") + } + + realGoroot, err := filepath.Abs(filepath.Join("..", "..")) + if err != nil { + t.Fatal(err) + } + + // To ensure that bootstrapping doesn't unexpectedly depend + // on the Go repo's git metadata, add a fake (unreadable) git + // directory above the simulated GOROOT. + // This mimics the configuration one much have when + // building from distro-packaged source code + // (see https://go.dev/issue/54852). + parent := t.TempDir() + dotGit := filepath.Join(parent, ".git") + if err := os.Mkdir(dotGit, 000); err != nil { + t.Fatal(err) + } + goroot := filepath.Join(parent, "goroot") + + gorootSrc := filepath.Join(goroot, "src") + overlayStart := time.Now() + if err := overlayDir(gorootSrc, filepath.Join(realGoroot, "src")); err != nil { + t.Fatal(err) + } + t.Logf("GOROOT/src overlay set up in %s", time.Since(overlayStart)) + + if err := os.WriteFile(filepath.Join(goroot, "VERSION"), []byte(runtime.Version()), 0666); err != nil { + t.Fatal(err) + } + + var makeScript string + switch runtime.GOOS { + case "windows": + makeScript = "make.bat" + case "plan9": + makeScript = "make.rc" + default: + makeScript = "make.bash" + } + + cmd := exec.Command(filepath.Join(goroot, "src", makeScript)) + cmd.Dir = gorootSrc + cmd.Env = append(cmd.Environ(), "GOROOT=", "GOROOT_BOOTSTRAP="+realGoroot) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + if err := cmd.Run(); err != nil { + t.Fatal(err) + } +} diff --git a/misc/swig/callback/callback.cc b/misc/swig/callback/callback.cc new file mode 100644 index 0000000..88bd49c --- /dev/null +++ b/misc/swig/callback/callback.cc @@ -0,0 +1,15 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This .cc file will be automatically compiled by the go tool and +// included in the package. + +#include <string> +#include "callback.h" + +std::string Caller::call() { + if (callback_ != 0) + return callback_->run(); + return ""; +} diff --git a/misc/swig/callback/callback.go b/misc/swig/callback/callback.go new file mode 100644 index 0000000..0d6e97f --- /dev/null +++ b/misc/swig/callback/callback.go @@ -0,0 +1,11 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package callback + +type GoCallback struct{} + +func (p *GoCallback) Run() string { + return "GoCallback.Run" +} diff --git a/misc/swig/callback/callback.h b/misc/swig/callback/callback.h new file mode 100644 index 0000000..4b66106 --- /dev/null +++ b/misc/swig/callback/callback.h @@ -0,0 +1,20 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +class Callback { +public: + virtual ~Callback() { } + virtual std::string run() { return "Callback::run"; } +}; + +class Caller { +private: + Callback *callback_; +public: + Caller(): callback_(0) { } + ~Caller() { delCallback(); } + void delCallback() { delete callback_; callback_ = 0; } + void setCallback(Callback *cb) { delCallback(); callback_ = cb; } + std::string call(); +}; diff --git a/misc/swig/callback/callback.swigcxx b/misc/swig/callback/callback.swigcxx new file mode 100644 index 0000000..6181fe9 --- /dev/null +++ b/misc/swig/callback/callback.swigcxx @@ -0,0 +1,18 @@ +/* Copyright 2011 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. */ + +/* An example of writing a C++ virtual function in Go. */ + +%module(directors="1") callback + +%{ +#include <string> +#include "callback.h" +%} + +%include "std_string.i" + +%feature("director"); + +%include "callback.h" diff --git a/misc/swig/callback/callback_test.go b/misc/swig/callback/callback_test.go new file mode 100644 index 0000000..0c8a300 --- /dev/null +++ b/misc/swig/callback/callback_test.go @@ -0,0 +1,33 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package callback + +import ( + "testing" +) + +func TestCall(t *testing.T) { + c := NewCaller() + cb := NewCallback() + + c.SetCallback(cb) + s := c.Call() + if s != "Callback::run" { + t.Errorf("unexpected string from Call: %q", s) + } + c.DelCallback() +} + +func TestCallback(t *testing.T) { + c := NewCaller() + cb := NewDirectorCallback(&GoCallback{}) + c.SetCallback(cb) + s := c.Call() + if s != "GoCallback.Run" { + t.Errorf("unexpected string from Call with callback: %q", s) + } + c.DelCallback() + DeleteDirectorCallback(cb) +} diff --git a/misc/swig/stdio/file.go b/misc/swig/stdio/file.go new file mode 100644 index 0000000..a582f77 --- /dev/null +++ b/misc/swig/stdio/file.go @@ -0,0 +1,15 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file is here just to cause problems. +// file.swig turns into a file also named file.go. +// Make sure cmd/go keeps them separate +// when both are passed to cgo. + +package file + +//int F(void) { return 1; } +import "C" + +func F() int { return int(C.F()) } diff --git a/misc/swig/stdio/file.swig b/misc/swig/stdio/file.swig new file mode 100644 index 0000000..b28ae0a --- /dev/null +++ b/misc/swig/stdio/file.swig @@ -0,0 +1,24 @@ +/* Copyright 2011 The Go Authors. All rights reserved. + Use of this source code is governed by a BSD-style + license that can be found in the LICENSE file. */ + +/* A trivial example of wrapping a C library using SWIG. */ + +%{ +#include <stdio.h> +#include <stdlib.h> +%} + +%typemap(gotype) const char * "string" +%typemap(in) const char * %{ + $1 = malloc($input.n + 1); + memcpy($1, $input.p, $input.n); + $1[$input.n] = '\0'; +%} +%typemap(freearg) const char * %{ + free($1); +%} + +FILE *fopen(const char *name, const char *mode); +int fclose(FILE *); +int fgetc(FILE *); diff --git a/misc/swig/stdio/file_test.go b/misc/swig/stdio/file_test.go new file mode 100644 index 0000000..aea92aa --- /dev/null +++ b/misc/swig/stdio/file_test.go @@ -0,0 +1,28 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package file + +import "testing" + +// Open this file itself and verify that the first few characters are +// as expected. +func TestRead(t *testing.T) { + f := Fopen("file_test.go", "r") + if f.Swigcptr() == 0 { + t.Fatal("fopen failed") + } + if Fgetc(f) != '/' || Fgetc(f) != '/' || Fgetc(f) != ' ' || Fgetc(f) != 'C' { + t.Error("read unexpected characters") + } + if Fclose(f) != 0 { + t.Error("fclose failed") + } +} + +func TestF(t *testing.T) { + if x := F(); x != 1 { + t.Fatalf("x = %d, want 1", x) + } +} diff --git a/misc/wasm/go_js_wasm_exec b/misc/wasm/go_js_wasm_exec new file mode 100755 index 0000000..fcbd0e4 --- /dev/null +++ b/misc/wasm/go_js_wasm_exec @@ -0,0 +1,14 @@ +#!/bin/bash +# Copyright 2018 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +SOURCE="${BASH_SOURCE[0]}" +while [ -h "$SOURCE" ]; do + DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + SOURCE="$(readlink "$SOURCE")" + [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" +done +DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + +exec node "$DIR/wasm_exec_node.js" "$@" diff --git a/misc/wasm/wasm_exec.html b/misc/wasm/wasm_exec.html new file mode 100644 index 0000000..72e6447 --- /dev/null +++ b/misc/wasm/wasm_exec.html @@ -0,0 +1,49 @@ +<!doctype html> +<!-- +Copyright 2018 The Go Authors. All rights reserved. +Use of this source code is governed by a BSD-style +license that can be found in the LICENSE file. +--> +<html> + +<head> + <meta charset="utf-8"> + <title>Go wasm</title> +</head> + +<body> + <!-- + Add the following polyfill for Microsoft Edge 17/18 support: + <script src="https://cdn.jsdelivr.net/npm/text-encoding@0.7.0/lib/encoding.min.js"></script> + (see https://caniuse.com/#feat=textencoder) + --> + <script src="wasm_exec.js"></script> + <script> + if (!WebAssembly.instantiateStreaming) { // polyfill + WebAssembly.instantiateStreaming = async (resp, importObject) => { + const source = await (await resp).arrayBuffer(); + return await WebAssembly.instantiate(source, importObject); + }; + } + + const go = new Go(); + let mod, inst; + WebAssembly.instantiateStreaming(fetch("test.wasm"), go.importObject).then((result) => { + mod = result.module; + inst = result.instance; + document.getElementById("runButton").disabled = false; + }).catch((err) => { + console.error(err); + }); + + async function run() { + console.clear(); + await go.run(inst); + inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance + } + </script> + + <button onClick="run();" id="runButton" disabled>Run</button> +</body> + +</html>
\ No newline at end of file diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js new file mode 100644 index 0000000..e6c8921 --- /dev/null +++ b/misc/wasm/wasm_exec.js @@ -0,0 +1,554 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +"use strict"; + +(() => { + const enosys = () => { + const err = new Error("not implemented"); + err.code = "ENOSYS"; + return err; + }; + + if (!globalThis.fs) { + let outputBuf = ""; + globalThis.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + writeSync(fd, buf) { + outputBuf += decoder.decode(buf); + const nl = outputBuf.lastIndexOf("\n"); + if (nl != -1) { + console.log(outputBuf.substring(0, nl)); + outputBuf = outputBuf.substring(nl + 1); + } + return buf.length; + }, + write(fd, buf, offset, length, position, callback) { + if (offset !== 0 || length !== buf.length || position !== null) { + callback(enosys()); + return; + } + const n = this.writeSync(fd, buf); + callback(null, n); + }, + chmod(path, mode, callback) { callback(enosys()); }, + chown(path, uid, gid, callback) { callback(enosys()); }, + close(fd, callback) { callback(enosys()); }, + fchmod(fd, mode, callback) { callback(enosys()); }, + fchown(fd, uid, gid, callback) { callback(enosys()); }, + fstat(fd, callback) { callback(enosys()); }, + fsync(fd, callback) { callback(null); }, + ftruncate(fd, length, callback) { callback(enosys()); }, + lchown(path, uid, gid, callback) { callback(enosys()); }, + link(path, link, callback) { callback(enosys()); }, + lstat(path, callback) { callback(enosys()); }, + mkdir(path, perm, callback) { callback(enosys()); }, + open(path, flags, mode, callback) { callback(enosys()); }, + read(fd, buffer, offset, length, position, callback) { callback(enosys()); }, + readdir(path, callback) { callback(enosys()); }, + readlink(path, callback) { callback(enosys()); }, + rename(from, to, callback) { callback(enosys()); }, + rmdir(path, callback) { callback(enosys()); }, + stat(path, callback) { callback(enosys()); }, + symlink(path, link, callback) { callback(enosys()); }, + truncate(path, length, callback) { callback(enosys()); }, + unlink(path, callback) { callback(enosys()); }, + utimes(path, atime, mtime, callback) { callback(enosys()); }, + }; + } + + if (!globalThis.process) { + globalThis.process = { + getuid() { return -1; }, + getgid() { return -1; }, + geteuid() { return -1; }, + getegid() { return -1; }, + getgroups() { throw enosys(); }, + pid: -1, + ppid: -1, + umask() { throw enosys(); }, + cwd() { throw enosys(); }, + chdir() { throw enosys(); }, + } + } + + if (!globalThis.crypto) { + throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)"); + } + + if (!globalThis.performance) { + throw new Error("globalThis.performance is not available, polyfill required (performance.now only)"); + } + + if (!globalThis.TextEncoder) { + throw new Error("globalThis.TextEncoder is not available, polyfill required"); + } + + if (!globalThis.TextDecoder) { + throw new Error("globalThis.TextDecoder is not available, polyfill required"); + } + + const encoder = new TextEncoder("utf-8"); + const decoder = new TextDecoder("utf-8"); + + globalThis.Go = class { + constructor() { + this.argv = ["js"]; + this.env = {}; + this.exit = (code) => { + if (code !== 0) { + console.warn("exit code:", code); + } + }; + this._exitPromise = new Promise((resolve) => { + this._resolveExitPromise = resolve; + }); + this._pendingEvent = null; + this._scheduledTimeouts = new Map(); + this._nextCallbackTimeoutID = 1; + + const setInt64 = (addr, v) => { + this.mem.setUint32(addr + 0, v, true); + this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true); + } + + const getInt64 = (addr) => { + const low = this.mem.getUint32(addr + 0, true); + const high = this.mem.getInt32(addr + 4, true); + return low + high * 4294967296; + } + + const loadValue = (addr) => { + const f = this.mem.getFloat64(addr, true); + if (f === 0) { + return undefined; + } + if (!isNaN(f)) { + return f; + } + + const id = this.mem.getUint32(addr, true); + return this._values[id]; + } + + const storeValue = (addr, v) => { + const nanHead = 0x7FF80000; + + if (typeof v === "number" && v !== 0) { + if (isNaN(v)) { + this.mem.setUint32(addr + 4, nanHead, true); + this.mem.setUint32(addr, 0, true); + return; + } + this.mem.setFloat64(addr, v, true); + return; + } + + if (v === undefined) { + this.mem.setFloat64(addr, 0, true); + return; + } + + let id = this._ids.get(v); + if (id === undefined) { + id = this._idPool.pop(); + if (id === undefined) { + id = this._values.length; + } + this._values[id] = v; + this._goRefCounts[id] = 0; + this._ids.set(v, id); + } + this._goRefCounts[id]++; + let typeFlag = 0; + switch (typeof v) { + case "object": + if (v !== null) { + typeFlag = 1; + } + break; + case "string": + typeFlag = 2; + break; + case "symbol": + typeFlag = 3; + break; + case "function": + typeFlag = 4; + break; + } + this.mem.setUint32(addr + 4, nanHead | typeFlag, true); + this.mem.setUint32(addr, id, true); + } + + const loadSlice = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + return new Uint8Array(this._inst.exports.mem.buffer, array, len); + } + + const loadSliceOfValues = (addr) => { + const array = getInt64(addr + 0); + const len = getInt64(addr + 8); + const a = new Array(len); + for (let i = 0; i < len; i++) { + a[i] = loadValue(array + i * 8); + } + return a; + } + + const loadString = (addr) => { + const saddr = getInt64(addr + 0); + const len = getInt64(addr + 8); + return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); + } + + const timeOrigin = Date.now() - performance.now(); + this.importObject = { + go: { + // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) + // may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported + // function. A goroutine can switch to a new stack if the current stack is too small (see morestack function). + // This changes the SP, thus we have to update the SP used by the imported function. + + // func wasmExit(code int32) + "runtime.wasmExit": (sp) => { + sp >>>= 0; + const code = this.mem.getInt32(sp + 8, true); + this.exited = true; + delete this._inst; + delete this._values; + delete this._goRefCounts; + delete this._ids; + delete this._idPool; + this.exit(code); + }, + + // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) + "runtime.wasmWrite": (sp) => { + sp >>>= 0; + const fd = getInt64(sp + 8); + const p = getInt64(sp + 16); + const n = this.mem.getInt32(sp + 24, true); + fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n)); + }, + + // func resetMemoryDataView() + "runtime.resetMemoryDataView": (sp) => { + sp >>>= 0; + this.mem = new DataView(this._inst.exports.mem.buffer); + }, + + // func nanotime1() int64 + "runtime.nanotime1": (sp) => { + sp >>>= 0; + setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000); + }, + + // func walltime() (sec int64, nsec int32) + "runtime.walltime": (sp) => { + sp >>>= 0; + const msec = (new Date).getTime(); + setInt64(sp + 8, msec / 1000); + this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true); + }, + + // func scheduleTimeoutEvent(delay int64) int32 + "runtime.scheduleTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this._nextCallbackTimeoutID; + this._nextCallbackTimeoutID++; + this._scheduledTimeouts.set(id, setTimeout( + () => { + this._resume(); + while (this._scheduledTimeouts.has(id)) { + // for some reason Go failed to register the timeout event, log and try again + // (temporary workaround for https://github.com/golang/go/issues/28975) + console.warn("scheduleTimeoutEvent: missed timeout event"); + this._resume(); + } + }, + getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early + )); + this.mem.setInt32(sp + 16, id, true); + }, + + // func clearTimeoutEvent(id int32) + "runtime.clearTimeoutEvent": (sp) => { + sp >>>= 0; + const id = this.mem.getInt32(sp + 8, true); + clearTimeout(this._scheduledTimeouts.get(id)); + this._scheduledTimeouts.delete(id); + }, + + // func getRandomData(r []byte) + "runtime.getRandomData": (sp) => { + sp >>>= 0; + crypto.getRandomValues(loadSlice(sp + 8)); + }, + + // func finalizeRef(v ref) + "syscall/js.finalizeRef": (sp) => { + sp >>>= 0; + const id = this.mem.getUint32(sp + 8, true); + this._goRefCounts[id]--; + if (this._goRefCounts[id] === 0) { + const v = this._values[id]; + this._values[id] = null; + this._ids.delete(v); + this._idPool.push(id); + } + }, + + // func stringVal(value string) ref + "syscall/js.stringVal": (sp) => { + sp >>>= 0; + storeValue(sp + 24, loadString(sp + 8)); + }, + + // func valueGet(v ref, p string) ref + "syscall/js.valueGet": (sp) => { + sp >>>= 0; + const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16)); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 32, result); + }, + + // func valueSet(v ref, p string, x ref) + "syscall/js.valueSet": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32)); + }, + + // func valueDelete(v ref, p string) + "syscall/js.valueDelete": (sp) => { + sp >>>= 0; + Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16)); + }, + + // func valueIndex(v ref, i int) ref + "syscall/js.valueIndex": (sp) => { + sp >>>= 0; + storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16))); + }, + + // valueSetIndex(v ref, i int, x ref) + "syscall/js.valueSetIndex": (sp) => { + sp >>>= 0; + Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24)); + }, + + // func valueCall(v ref, m string, args []ref) (ref, bool) + "syscall/js.valueCall": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const m = Reflect.get(v, loadString(sp + 16)); + const args = loadSliceOfValues(sp + 32); + const result = Reflect.apply(m, v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, result); + this.mem.setUint8(sp + 64, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 56, err); + this.mem.setUint8(sp + 64, 0); + } + }, + + // func valueInvoke(v ref, args []ref) (ref, bool) + "syscall/js.valueInvoke": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.apply(v, undefined, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueNew(v ref, args []ref) (ref, bool) + "syscall/js.valueNew": (sp) => { + sp >>>= 0; + try { + const v = loadValue(sp + 8); + const args = loadSliceOfValues(sp + 16); + const result = Reflect.construct(v, args); + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, result); + this.mem.setUint8(sp + 48, 1); + } catch (err) { + sp = this._inst.exports.getsp() >>> 0; // see comment above + storeValue(sp + 40, err); + this.mem.setUint8(sp + 48, 0); + } + }, + + // func valueLength(v ref) int + "syscall/js.valueLength": (sp) => { + sp >>>= 0; + setInt64(sp + 16, parseInt(loadValue(sp + 8).length)); + }, + + // valuePrepareString(v ref) (ref, int) + "syscall/js.valuePrepareString": (sp) => { + sp >>>= 0; + const str = encoder.encode(String(loadValue(sp + 8))); + storeValue(sp + 16, str); + setInt64(sp + 24, str.length); + }, + + // valueLoadString(v ref, b []byte) + "syscall/js.valueLoadString": (sp) => { + sp >>>= 0; + const str = loadValue(sp + 8); + loadSlice(sp + 16).set(str); + }, + + // func valueInstanceOf(v ref, t ref) bool + "syscall/js.valueInstanceOf": (sp) => { + sp >>>= 0; + this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0); + }, + + // func copyBytesToGo(dst []byte, src ref) (int, bool) + "syscall/js.copyBytesToGo": (sp) => { + sp >>>= 0; + const dst = loadSlice(sp + 8); + const src = loadValue(sp + 32); + if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + // func copyBytesToJS(dst ref, src []byte) (int, bool) + "syscall/js.copyBytesToJS": (sp) => { + sp >>>= 0; + const dst = loadValue(sp + 8); + const src = loadSlice(sp + 16); + if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) { + this.mem.setUint8(sp + 48, 0); + return; + } + const toCopy = src.subarray(0, dst.length); + dst.set(toCopy); + setInt64(sp + 40, toCopy.length); + this.mem.setUint8(sp + 48, 1); + }, + + "debug": (value) => { + console.log(value); + }, + } + }; + } + + async run(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new Error("Go.run: WebAssembly.Instance expected"); + } + this._inst = instance; + this.mem = new DataView(this._inst.exports.mem.buffer); + this._values = [ // JS values that Go currently has references to, indexed by reference id + NaN, + 0, + null, + true, + false, + globalThis, + this, + ]; + this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id + this._ids = new Map([ // mapping from JS values to reference ids + [0, 1], + [null, 2], + [true, 3], + [false, 4], + [globalThis, 5], + [this, 6], + ]); + this._idPool = []; // unused ids that have been garbage collected + this.exited = false; // whether the Go program has exited + + // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory. + let offset = 4096; + + const strPtr = (str) => { + const ptr = offset; + const bytes = encoder.encode(str + "\0"); + new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes); + offset += bytes.length; + if (offset % 8 !== 0) { + offset += 8 - (offset % 8); + } + return ptr; + }; + + const argc = this.argv.length; + + const argvPtrs = []; + this.argv.forEach((arg) => { + argvPtrs.push(strPtr(arg)); + }); + argvPtrs.push(0); + + const keys = Object.keys(this.env).sort(); + keys.forEach((key) => { + argvPtrs.push(strPtr(`${key}=${this.env[key]}`)); + }); + argvPtrs.push(0); + + const argv = offset; + argvPtrs.forEach((ptr) => { + this.mem.setUint32(offset, ptr, true); + this.mem.setUint32(offset + 4, 0, true); + offset += 8; + }); + + // The linker guarantees global data starts from at least wasmMinDataAddr. + // Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr. + const wasmMinDataAddr = 4096 + 8192; + if (offset >= wasmMinDataAddr) { + throw new Error("total length of command line and environment variables exceeds limit"); + } + + this._inst.exports.run(argc, argv); + if (this.exited) { + this._resolveExitPromise(); + } + await this._exitPromise; + } + + _resume() { + if (this.exited) { + throw new Error("Go program has already exited"); + } + this._inst.exports.resume(); + if (this.exited) { + this._resolveExitPromise(); + } + } + + _makeFuncWrapper(id) { + const go = this; + return function () { + const event = { id: id, this: this, args: arguments }; + go._pendingEvent = event; + go._resume(); + return event.result; + }; + } + } +})(); diff --git a/misc/wasm/wasm_exec_node.js b/misc/wasm/wasm_exec_node.js new file mode 100644 index 0000000..f9200ca --- /dev/null +++ b/misc/wasm/wasm_exec_node.js @@ -0,0 +1,49 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +"use strict"; + +if (process.argv.length < 3) { + console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); + process.exit(1); +} + +globalThis.require = require; +globalThis.fs = require("fs"); +globalThis.TextEncoder = require("util").TextEncoder; +globalThis.TextDecoder = require("util").TextDecoder; + +globalThis.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, +}; + +const crypto = require("crypto"); +globalThis.crypto = { + getRandomValues(b) { + crypto.randomFillSync(b); + }, +}; + +require("./wasm_exec"); + +const go = new Go(); +go.argv = process.argv.slice(2); +go.env = Object.assign({ TMPDIR: require("os").tmpdir() }, process.env); +go.exit = process.exit; +WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => { + process.on("exit", (code) => { // Node.js exits if no event handler is pending + if (code === 0 && !go.exited) { + // deadlock, make Go print error and stack traces + go._pendingEvent = { id: 0 }; + go._resume(); + } + }); + return go.run(result.instance); +}).catch((err) => { + console.error(err); + process.exit(1); +}); |