| | |
| | |
| | |
| |
|
| | package plugin_test |
| |
|
| | import ( |
| | "bytes" |
| | "cmd/cgo/internal/cgotest" |
| | "context" |
| | "flag" |
| | "fmt" |
| | "internal/platform" |
| | "internal/testenv" |
| | "log" |
| | "os" |
| | "os/exec" |
| | "path/filepath" |
| | "runtime" |
| | "strings" |
| | "testing" |
| | "time" |
| | ) |
| |
|
| | var globalSkip = func(t *testing.T) {} |
| |
|
| | var gcflags string = os.Getenv("GO_GCFLAGS") |
| | var goroot string |
| |
|
| | func TestMain(m *testing.M) { |
| | flag.Parse() |
| | log.SetFlags(log.Lshortfile) |
| | os.Exit(testMain(m)) |
| | } |
| |
|
| | |
| | var tmpDir string |
| |
|
| | |
| | func prettyPrintf(format string, args ...any) { |
| | s := fmt.Sprintf(format, args...) |
| | if tmpDir != "" { |
| | s = strings.ReplaceAll(s, tmpDir, "$TMPDIR") |
| | } |
| | fmt.Print(s) |
| | } |
| |
|
| | func testMain(m *testing.M) int { |
| | if testing.Short() && testenv.Builder() == "" { |
| | globalSkip = func(t *testing.T) { t.Skip("short mode and $GO_BUILDER_NAME not set") } |
| | return m.Run() |
| | } |
| | if !platform.BuildModeSupported(runtime.Compiler, "plugin", runtime.GOOS, runtime.GOARCH) { |
| | globalSkip = func(t *testing.T) { t.Skip("plugin build mode not supported") } |
| | return m.Run() |
| | } |
| | if !testenv.HasCGO() { |
| | globalSkip = func(t *testing.T) { t.Skip("cgo not supported") } |
| | return m.Run() |
| | } |
| |
|
| | cwd, err := os.Getwd() |
| | if err != nil { |
| | log.Fatal(err) |
| | } |
| | goroot = filepath.Join(cwd, "../../../../..") |
| |
|
| | |
| | |
| |
|
| | GOPATH, err := os.MkdirTemp("", "plugin_test") |
| | if err != nil { |
| | log.Panic(err) |
| | } |
| | defer os.RemoveAll(GOPATH) |
| | tmpDir = GOPATH |
| | fmt.Printf("TMPDIR=%s\n", tmpDir) |
| |
|
| | 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 := cgotest.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) string { |
| | if t != nil { |
| | t.Helper() |
| | } |
| | var flags []string |
| | if op != "tool" { |
| | flags = []string{"-gcflags", gcflags} |
| | } |
| | return run(t, filepath.Join(goroot, "bin", "go"), append(append([]string{op}, flags...), args...)...) |
| | } |
| |
|
| | |
| | func escape(s string) string { |
| | s = strings.ReplaceAll(s, "\\", "\\\\") |
| | s = strings.ReplaceAll(s, "'", "\\'") |
| | |
| | if s == "" || strings.ContainsAny(s, "\\ ;#*&$~?!|[]()<>{}`") { |
| | s = "'" + s + "'" |
| | } |
| | return s |
| | } |
| |
|
| | |
| | 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) |
| | } |
| | } |
| | |
| | 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("%#q: %v\n%s", cmd, err, cmd.Stderr) |
| | } else { |
| | t.Helper() |
| | t.Fatalf("%#q: %v\n%s", cmd, err, cmd.Stderr) |
| | } |
| | } |
| |
|
| | return string(bytes.TrimSpace(out)) |
| | } |
| |
|
| | func TestDWARFSections(t *testing.T) { |
| | |
| | globalSkip(t) |
| | goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse") |
| | goCmd(t, "run", "./checkdwarf/main.go", "./host.exe", "main.main") |
| | } |
| |
|
| | func TestBuildID(t *testing.T) { |
| | |
| | globalSkip(t) |
| | b := goCmd(t, "tool", "buildid", "plugin1.so") |
| | if len(b) == 0 { |
| | t.Errorf("build id not found") |
| | } |
| | } |
| |
|
| | func TestRunHost(t *testing.T) { |
| | globalSkip(t) |
| | run(t, "./host.exe") |
| | } |
| |
|
| | func TestUniqueTypesAndItabs(t *testing.T) { |
| | globalSkip(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) { |
| | |
| | |
| | globalSkip(t) |
| | 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("%#q: %v\n%s", cmd, err, out) |
| | } |
| | } |
| |
|
| | func TestIssue19534(t *testing.T) { |
| | |
| | globalSkip(t) |
| | 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) { |
| | globalSkip(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) { |
| | globalSkip(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) { |
| | globalSkip(t) |
| | goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue19529/plugin.go") |
| | } |
| |
|
| | func TestIssue22175(t *testing.T) { |
| | globalSkip(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) { |
| | globalSkip(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) { |
| | globalSkip(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) { |
| | globalSkip(t) |
| | goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin") |
| | goCmd(t, "build", "-o", "issue25756.exe", "./issue25756/main.go") |
| | |
| | for n := 20; n > 0; n-- { |
| | t.Run(fmt.Sprint(n), func(t *testing.T) { |
| | t.Parallel() |
| | run(t, "./issue25756.exe") |
| | }) |
| | } |
| | } |
| |
|
| | |
| | func TestIssue25756pie(t *testing.T) { |
| | globalSkip(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) { |
| | |
| | globalSkip(t) |
| | 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) { |
| | globalSkip(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) { |
| | globalSkip(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) { |
| | globalSkip(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) { |
| | globalSkip(t) |
| | goCmd(t, "build", "-buildmode=plugin", "-o", "issue52937.so", "./issue52937/main.go") |
| | } |
| |
|
| | func TestIssue53989(t *testing.T) { |
| | globalSkip(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) { |
| | |
| | globalSkip(t) |
| |
|
| | t.Parallel() |
| | goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go") |
| |
|
| | for i := 0; i < 100; i++ { |
| | cmd := testenv.Command(t, "./forkexec.exe", "1") |
| | err := cmd.Run() |
| | if err != nil { |
| | if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { |
| | t.Logf("stderr:\n%s", ee.Stderr) |
| | } |
| | t.Errorf("running command failed: %v", err) |
| | break |
| | } |
| | } |
| | } |
| |
|
| | func TestSymbolNameMangle(t *testing.T) { |
| | |
| | |
| | |
| | |
| | globalSkip(t) |
| | goCmd(t, "build", "-buildmode=plugin", "-o", "mangle.so", "./mangle/plugin.go") |
| | } |
| |
|
| | func TestIssue62430(t *testing.T) { |
| | globalSkip(t) |
| | goCmd(t, "build", "-buildmode=plugin", "-o", "issue62430.so", "./issue62430/plugin.go") |
| | goCmd(t, "build", "-o", "issue62430.exe", "./issue62430/main.go") |
| | run(t, "./issue62430.exe") |
| | } |
| |
|
| | func TestTextSectionSplit(t *testing.T) { |
| | globalSkip(t) |
| | if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" { |
| | t.Skipf("text section splitting is not done in %s/%s", runtime.GOOS, runtime.GOARCH) |
| | } |
| |
|
| | |
| | |
| | goCmd(nil, "build", "-ldflags=-debugtextsize=262144", "-o", "host-split.exe", "./host") |
| | run(t, "./host-split.exe") |
| |
|
| | |
| | syms := goCmd(nil, "tool", "nm", "host-split.exe") |
| | if !strings.Contains(syms, "runtime.text.1") { |
| | t.Errorf("runtime.text.1 not found, text section not split?") |
| | } |
| | } |
| |
|
| | func TestIssue67976(t *testing.T) { |
| | |
| | |
| | |
| | globalSkip(t) |
| | goCmd(t, "build", "-buildmode=plugin", "-o", "issue67976.so", "./issue67976/plugin.go") |
| | } |
| |
|
| | func TestIssue75102(t *testing.T) { |
| | globalSkip(t) |
| | |
| | goCmd(t, "build", "-gcflags=all=-N -l", "-buildmode=plugin", "-o", "issue75102.so", "./issue75102/plugin.go") |
| | goCmd(t, "build", "-o", "issue75102.exe", "./issue75102/main.go") |
| | run(t, "./issue75102.exe") |
| | } |
| |
|