summaryrefslogtreecommitdiffstats
path: root/misc/cgo/testplugin
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
commit43a123c1ae6613b3efeed291fa552ecd909d3acf (patch)
treefd92518b7024bc74031f78a1cf9e454b65e73665 /misc/cgo/testplugin
parentInitial commit. (diff)
downloadgolang-1.20-upstream.tar.xz
golang-1.20-upstream.zip
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'misc/cgo/testplugin')
-rw-r--r--misc/cgo/testplugin/altpath/testdata/common/common.go11
-rw-r--r--misc/cgo/testplugin/altpath/testdata/plugin-mismatch/main.go17
-rw-r--r--misc/cgo/testplugin/overlaydir_test.go78
-rw-r--r--misc/cgo/testplugin/plugin_test.go342
-rw-r--r--misc/cgo/testplugin/testdata/checkdwarf/main.go106
-rw-r--r--misc/cgo/testplugin/testdata/common/common.go11
-rw-r--r--misc/cgo/testplugin/testdata/forkexec/main.go30
-rw-r--r--misc/cgo/testplugin/testdata/host/host.go176
-rw-r--r--misc/cgo/testplugin/testdata/iface/main.go47
-rw-r--r--misc/cgo/testplugin/testdata/iface_a/a.go17
-rw-r--r--misc/cgo/testplugin/testdata/iface_b/b.go17
-rw-r--r--misc/cgo/testplugin/testdata/iface_i/i.go17
-rw-r--r--misc/cgo/testplugin/testdata/issue18584/main.go23
-rw-r--r--misc/cgo/testplugin/testdata/issue18584/plugin.go19
-rw-r--r--misc/cgo/testplugin/testdata/issue18676/dynamodbstreamsevt/definition.go13
-rw-r--r--misc/cgo/testplugin/testdata/issue18676/main.go31
-rw-r--r--misc/cgo/testplugin/testdata/issue18676/plugin.go11
-rw-r--r--misc/cgo/testplugin/testdata/issue19418/main.go29
-rw-r--r--misc/cgo/testplugin/testdata/issue19418/plugin.go7
-rw-r--r--misc/cgo/testplugin/testdata/issue19529/plugin.go15
-rw-r--r--misc/cgo/testplugin/testdata/issue19534/main.go23
-rw-r--r--misc/cgo/testplugin/testdata/issue19534/plugin.go9
-rw-r--r--misc/cgo/testplugin/testdata/issue22175/main.go28
-rw-r--r--misc/cgo/testplugin/testdata/issue22175/plugin1.go21
-rw-r--r--misc/cgo/testplugin/testdata/issue22175/plugin2.go9
-rw-r--r--misc/cgo/testplugin/testdata/issue22295.pkg/main.go28
-rw-r--r--misc/cgo/testplugin/testdata/issue22295.pkg/plugin.go16
-rw-r--r--misc/cgo/testplugin/testdata/issue24351/main.go21
-rw-r--r--misc/cgo/testplugin/testdata/issue24351/plugin.go14
-rw-r--r--misc/cgo/testplugin/testdata/issue25756/main.go52
-rw-r--r--misc/cgo/testplugin/testdata/issue25756/plugin/c-life.c56
-rw-r--r--misc/cgo/testplugin/testdata/issue25756/plugin/life.go39
-rw-r--r--misc/cgo/testplugin/testdata/issue25756/plugin/life.h7
-rw-r--r--misc/cgo/testplugin/testdata/issue44956/base/base.go7
-rw-r--r--misc/cgo/testplugin/testdata/issue44956/main.go47
-rw-r--r--misc/cgo/testplugin/testdata/issue44956/plugin1.go9
-rw-r--r--misc/cgo/testplugin/testdata/issue44956/plugin2.go11
-rw-r--r--misc/cgo/testplugin/testdata/issue52937/main.go9
-rw-r--r--misc/cgo/testplugin/testdata/issue53989/main.go32
-rw-r--r--misc/cgo/testplugin/testdata/issue53989/p/p.go52
-rw-r--r--misc/cgo/testplugin/testdata/issue53989/plugin.go13
-rw-r--r--misc/cgo/testplugin/testdata/method/main.go26
-rw-r--r--misc/cgo/testplugin/testdata/method/plugin.go13
-rw-r--r--misc/cgo/testplugin/testdata/method2/main.go32
-rw-r--r--misc/cgo/testplugin/testdata/method2/p/p.go9
-rw-r--r--misc/cgo/testplugin/testdata/method2/plugin.go11
-rw-r--r--misc/cgo/testplugin/testdata/method3/main.go32
-rw-r--r--misc/cgo/testplugin/testdata/method3/p/p.go17
-rw-r--r--misc/cgo/testplugin/testdata/method3/plugin.go11
-rw-r--r--misc/cgo/testplugin/testdata/plugin1/plugin1.go57
-rw-r--r--misc/cgo/testplugin/testdata/plugin2/plugin2.go44
-rw-r--r--misc/cgo/testplugin/testdata/sub/plugin1/plugin1.go23
-rw-r--r--misc/cgo/testplugin/testdata/unnamed1/main.go25
-rw-r--r--misc/cgo/testplugin/testdata/unnamed2/main.go23
54 files changed, 1843 insertions, 0 deletions
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() {}