1
2
3
4
5 package plugin_test
6
7 import (
8 "bytes"
9 "cmd/cgo/internal/cgotest"
10 "context"
11 "flag"
12 "fmt"
13 "internal/platform"
14 "internal/testenv"
15 "log"
16 "os"
17 "os/exec"
18 "path/filepath"
19 "runtime"
20 "strings"
21 "testing"
22 "time"
23 )
24
25 var globalSkip = func(t *testing.T) {}
26
27 var gcflags string = os.Getenv("GO_GCFLAGS")
28 var goroot string
29
30 func TestMain(m *testing.M) {
31 flag.Parse()
32 log.SetFlags(log.Lshortfile)
33 os.Exit(testMain(m))
34 }
35
36
37 var tmpDir string
38
39
40 func prettyPrintf(format string, args ...interface{}) {
41 s := fmt.Sprintf(format, args...)
42 if tmpDir != "" {
43 s = strings.ReplaceAll(s, tmpDir, "$TMPDIR")
44 }
45 fmt.Print(s)
46 }
47
48 func testMain(m *testing.M) int {
49 if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
50 globalSkip = func(t *testing.T) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
51 return m.Run()
52 }
53 if !platform.BuildModeSupported(runtime.Compiler, "plugin", runtime.GOOS, runtime.GOARCH) {
54 globalSkip = func(t *testing.T) { t.Skip("plugin build mode not supported") }
55 return m.Run()
56 }
57 if !testenv.HasCGO() {
58 globalSkip = func(t *testing.T) { t.Skip("cgo not supported") }
59 return m.Run()
60 }
61
62 cwd, err := os.Getwd()
63 if err != nil {
64 log.Fatal(err)
65 }
66 goroot = filepath.Join(cwd, "../../../../..")
67
68
69
70
71 GOPATH, err := os.MkdirTemp("", "plugin_test")
72 if err != nil {
73 log.Panic(err)
74 }
75 defer os.RemoveAll(GOPATH)
76 tmpDir = GOPATH
77 fmt.Printf("TMPDIR=%s\n", tmpDir)
78
79 modRoot := filepath.Join(GOPATH, "src", "testplugin")
80 altRoot := filepath.Join(GOPATH, "alt", "src", "testplugin")
81 for srcRoot, dstRoot := range map[string]string{
82 "testdata": modRoot,
83 filepath.Join("altpath", "testdata"): altRoot,
84 } {
85 if err := cgotest.OverlayDir(dstRoot, srcRoot); err != nil {
86 log.Panic(err)
87 }
88 prettyPrintf("mkdir -p %s\n", dstRoot)
89 prettyPrintf("rsync -a %s/ %s\n", srcRoot, dstRoot)
90
91 if err := os.WriteFile(filepath.Join(dstRoot, "go.mod"), []byte("module testplugin\n"), 0666); err != nil {
92 log.Panic(err)
93 }
94 prettyPrintf("echo 'module testplugin' > %s/go.mod\n", dstRoot)
95 }
96
97 os.Setenv("GOPATH", filepath.Join(GOPATH, "alt"))
98 if err := os.Chdir(altRoot); err != nil {
99 log.Panic(err)
100 } else {
101 prettyPrintf("cd %s\n", altRoot)
102 }
103 os.Setenv("PWD", altRoot)
104 goCmd(nil, "build", "-buildmode=plugin", "-o", filepath.Join(modRoot, "plugin-mismatch.so"), "./plugin-mismatch")
105
106 os.Setenv("GOPATH", GOPATH)
107 if err := os.Chdir(modRoot); err != nil {
108 log.Panic(err)
109 } else {
110 prettyPrintf("cd %s\n", modRoot)
111 }
112 os.Setenv("PWD", modRoot)
113
114 os.Setenv("LD_LIBRARY_PATH", modRoot)
115
116 goCmd(nil, "build", "-buildmode=plugin", "./plugin1")
117 goCmd(nil, "build", "-buildmode=plugin", "./plugin2")
118 so, err := os.ReadFile("plugin2.so")
119 if err != nil {
120 log.Panic(err)
121 }
122 if err := os.WriteFile("plugin2-dup.so", so, 0444); err != nil {
123 log.Panic(err)
124 }
125 prettyPrintf("cp plugin2.so plugin2-dup.so\n")
126
127 goCmd(nil, "build", "-buildmode=plugin", "-o=sub/plugin1.so", "./sub/plugin1")
128 goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed1.so", "./unnamed1/main.go")
129 goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed2.so", "./unnamed2/main.go")
130 goCmd(nil, "build", "-o", "host.exe", "./host")
131
132 return m.Run()
133 }
134
135 func goCmd(t *testing.T, op string, args ...string) string {
136 if t != nil {
137 t.Helper()
138 }
139 var flags []string
140 if op != "tool" {
141 flags = []string{"-gcflags", gcflags}
142 }
143 return run(t, filepath.Join(goroot, "bin", "go"), append(append([]string{op}, flags...), args...)...)
144 }
145
146
147 func escape(s string) string {
148 s = strings.Replace(s, "\\", "\\\\", -1)
149 s = strings.Replace(s, "'", "\\'", -1)
150
151 if s == "" || strings.ContainsAny(s, "\\ ;#*&$~?!|[]()<>{}`") {
152 s = "'" + s + "'"
153 }
154 return s
155 }
156
157
158 func asCommandLine(cwd string, cmd *exec.Cmd) string {
159 s := "("
160 if cmd.Dir != "" && cmd.Dir != cwd {
161 s += "cd" + escape(cmd.Dir) + ";"
162 }
163 for _, e := range cmd.Env {
164 if !strings.HasPrefix(e, "PATH=") &&
165 !strings.HasPrefix(e, "HOME=") &&
166 !strings.HasPrefix(e, "USER=") &&
167 !strings.HasPrefix(e, "SHELL=") {
168 s += " "
169 s += escape(e)
170 }
171 }
172
173 for _, e := range os.Environ() {
174 if strings.HasPrefix(e, "PWD=") ||
175 strings.HasPrefix(e, "GOPATH=") ||
176 strings.HasPrefix(e, "LD_LIBRARY_PATH=") {
177 s += " "
178 s += escape(e)
179 }
180 }
181 for _, a := range cmd.Args {
182 s += " "
183 s += escape(a)
184 }
185 s += " )"
186 return s
187 }
188
189 func run(t *testing.T, bin string, args ...string) string {
190 cmd := exec.Command(bin, args...)
191 cmdLine := asCommandLine(".", cmd)
192 prettyPrintf("%s\n", cmdLine)
193 cmd.Stderr = new(strings.Builder)
194 out, err := cmd.Output()
195 if err != nil {
196 if t == nil {
197 log.Panicf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
198 } else {
199 t.Helper()
200 t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
201 }
202 }
203
204 return string(bytes.TrimSpace(out))
205 }
206
207 func TestDWARFSections(t *testing.T) {
208
209 globalSkip(t)
210 goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse")
211 goCmd(t, "run", "./checkdwarf/main.go", "./host.exe", "main.main")
212 }
213
214 func TestBuildID(t *testing.T) {
215
216 globalSkip(t)
217 b := goCmd(t, "tool", "buildid", "plugin1.so")
218 if len(b) == 0 {
219 t.Errorf("build id not found")
220 }
221 }
222
223 func TestRunHost(t *testing.T) {
224 globalSkip(t)
225 run(t, "./host.exe")
226 }
227
228 func TestUniqueTypesAndItabs(t *testing.T) {
229 globalSkip(t)
230 goCmd(t, "build", "-buildmode=plugin", "./iface_a")
231 goCmd(t, "build", "-buildmode=plugin", "./iface_b")
232 goCmd(t, "build", "-o", "iface.exe", "./iface")
233 run(t, "./iface.exe")
234 }
235
236 func TestIssue18676(t *testing.T) {
237
238
239 globalSkip(t)
240 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18676/plugin.go")
241 goCmd(t, "build", "-o", "issue18676.exe", "./issue18676/main.go")
242
243 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
244 defer cancel()
245 cmd := exec.CommandContext(ctx, "./issue18676.exe")
246 out, err := cmd.CombinedOutput()
247 if err != nil {
248 t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
249 }
250 }
251
252 func TestIssue19534(t *testing.T) {
253
254 globalSkip(t)
255 goCmd(t, "build", "-buildmode=plugin", "-gcflags=-p=issue.19534", "-ldflags=-pluginpath=issue.19534", "-o", "plugin.so", "./issue19534/plugin.go")
256 goCmd(t, "build", "-o", "issue19534.exe", "./issue19534/main.go")
257 run(t, "./issue19534.exe")
258 }
259
260 func TestIssue18584(t *testing.T) {
261 globalSkip(t)
262 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18584/plugin.go")
263 goCmd(t, "build", "-o", "issue18584.exe", "./issue18584/main.go")
264 run(t, "./issue18584.exe")
265 }
266
267 func TestIssue19418(t *testing.T) {
268 globalSkip(t)
269 goCmd(t, "build", "-buildmode=plugin", "-ldflags=-X main.Val=linkstr", "-o", "plugin.so", "./issue19418/plugin.go")
270 goCmd(t, "build", "-o", "issue19418.exe", "./issue19418/main.go")
271 run(t, "./issue19418.exe")
272 }
273
274 func TestIssue19529(t *testing.T) {
275 globalSkip(t)
276 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue19529/plugin.go")
277 }
278
279 func TestIssue22175(t *testing.T) {
280 globalSkip(t)
281 goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin1.so", "./issue22175/plugin1.go")
282 goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin2.so", "./issue22175/plugin2.go")
283 goCmd(t, "build", "-o", "issue22175.exe", "./issue22175/main.go")
284 run(t, "./issue22175.exe")
285 }
286
287 func TestIssue22295(t *testing.T) {
288 globalSkip(t)
289 goCmd(t, "build", "-buildmode=plugin", "-o", "issue.22295.so", "./issue22295.pkg")
290 goCmd(t, "build", "-o", "issue22295.exe", "./issue22295.pkg/main.go")
291 run(t, "./issue22295.exe")
292 }
293
294 func TestIssue24351(t *testing.T) {
295 globalSkip(t)
296 goCmd(t, "build", "-buildmode=plugin", "-o", "issue24351.so", "./issue24351/plugin.go")
297 goCmd(t, "build", "-o", "issue24351.exe", "./issue24351/main.go")
298 run(t, "./issue24351.exe")
299 }
300
301 func TestIssue25756(t *testing.T) {
302 globalSkip(t)
303 goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
304 goCmd(t, "build", "-o", "issue25756.exe", "./issue25756/main.go")
305
306 for n := 20; n > 0; n-- {
307 t.Run(fmt.Sprint(n), func(t *testing.T) {
308 t.Parallel()
309 run(t, "./issue25756.exe")
310 })
311 }
312 }
313
314
315 func TestIssue25756pie(t *testing.T) {
316 globalSkip(t)
317 goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
318 goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
319 run(t, "./issue25756pie.exe")
320 }
321
322 func TestMethod(t *testing.T) {
323
324 globalSkip(t)
325 goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go")
326 goCmd(t, "build", "-o", "method.exe", "./method/main.go")
327 run(t, "./method.exe")
328 }
329
330 func TestMethod2(t *testing.T) {
331 globalSkip(t)
332 goCmd(t, "build", "-buildmode=plugin", "-o", "method2.so", "./method2/plugin.go")
333 goCmd(t, "build", "-o", "method2.exe", "./method2/main.go")
334 run(t, "./method2.exe")
335 }
336
337 func TestMethod3(t *testing.T) {
338 globalSkip(t)
339 goCmd(t, "build", "-buildmode=plugin", "-o", "method3.so", "./method3/plugin.go")
340 goCmd(t, "build", "-o", "method3.exe", "./method3/main.go")
341 run(t, "./method3.exe")
342 }
343
344 func TestIssue44956(t *testing.T) {
345 globalSkip(t)
346 goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p1.so", "./issue44956/plugin1.go")
347 goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p2.so", "./issue44956/plugin2.go")
348 goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
349 run(t, "./issue44956.exe")
350 }
351
352 func TestIssue52937(t *testing.T) {
353 globalSkip(t)
354 goCmd(t, "build", "-buildmode=plugin", "-o", "issue52937.so", "./issue52937/main.go")
355 }
356
357 func TestIssue53989(t *testing.T) {
358 globalSkip(t)
359 goCmd(t, "build", "-buildmode=plugin", "-o", "issue53989.so", "./issue53989/plugin.go")
360 goCmd(t, "build", "-o", "issue53989.exe", "./issue53989/main.go")
361 run(t, "./issue53989.exe")
362 }
363
364 func TestForkExec(t *testing.T) {
365
366 globalSkip(t)
367
368 t.Parallel()
369 goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go")
370
371 for i := 0; i < 100; i++ {
372 cmd := testenv.Command(t, "./forkexec.exe", "1")
373 err := cmd.Run()
374 if err != nil {
375 if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
376 t.Logf("stderr:\n%s", ee.Stderr)
377 }
378 t.Errorf("running command failed: %v", err)
379 break
380 }
381 }
382 }
383
384 func TestSymbolNameMangle(t *testing.T) {
385
386
387
388
389 globalSkip(t)
390 goCmd(t, "build", "-buildmode=plugin", "-o", "mangle.so", "./mangle/plugin.go")
391 }
392
393 func TestIssue62430(t *testing.T) {
394 globalSkip(t)
395 goCmd(t, "build", "-buildmode=plugin", "-o", "issue62430.so", "./issue62430/plugin.go")
396 goCmd(t, "build", "-o", "issue62430.exe", "./issue62430/main.go")
397 run(t, "./issue62430.exe")
398 }
399
400 func TestTextSectionSplit(t *testing.T) {
401 globalSkip(t)
402 if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
403 t.Skipf("text section splitting is not done in %s/%s", runtime.GOOS, runtime.GOARCH)
404 }
405
406
407
408 goCmd(nil, "build", "-ldflags=-debugtextsize=262144", "-o", "host-split.exe", "./host")
409 run(t, "./host-split.exe")
410
411
412 syms := goCmd(nil, "tool", "nm", "host-split.exe")
413 if !strings.Contains(syms, "runtime.text.1") {
414 t.Errorf("runtime.text.1 not found, text section not split?")
415 }
416 }
417
418 func TestIssue67976(t *testing.T) {
419
420
421
422 globalSkip(t)
423 goCmd(t, "build", "-buildmode=plugin", "-o", "issue67976.so", "./issue67976/plugin.go")
424 }
425
View as plain text