1
2
3
4
5 package shared_test
6
7 import (
8 "bufio"
9 "bytes"
10 "cmd/cgo/internal/cgotest"
11 "debug/elf"
12 "encoding/binary"
13 "flag"
14 "fmt"
15 "go/build"
16 "internal/platform"
17 "internal/testenv"
18 "io"
19 "log"
20 "os"
21 "os/exec"
22 "path/filepath"
23 "regexp"
24 "runtime"
25 "sort"
26 "strconv"
27 "strings"
28 "testing"
29 "time"
30 )
31
32 var globalSkip = func(t testing.TB) {}
33
34 var gopathInstallDir, gorootInstallDir string
35 var oldGOROOT string
36
37
38
39 var minpkgs = []string{"runtime", "sync/atomic"}
40 var soname = "libruntime,sync-atomic.so"
41
42 var testX = flag.Bool("testx", false, "if true, pass -x to 'go' subcommands invoked by the test")
43 var testWork = flag.Bool("testwork", false, "if true, log and do not delete the temporary working directory")
44
45
46 func run(t *testing.T, msg string, args ...string) {
47 runWithEnv(t, msg, nil, args...)
48 }
49
50
51 func runWithEnv(t *testing.T, msg string, env []string, args ...string) {
52 c := exec.Command(args[0], args[1:]...)
53 if len(env) != 0 {
54 c.Env = append(os.Environ(), env...)
55 }
56 if output, err := c.CombinedOutput(); err != nil {
57 t.Errorf("executing %s (%s) failed %s:\n%s", strings.Join(args, " "), msg, err, output)
58 }
59 }
60
61
62
63 func goCmd(t *testing.T, args ...string) string {
64 newargs := []string{args[0]}
65 if *testX && args[0] != "env" {
66 newargs = append(newargs, "-x", "-ldflags=-v")
67 }
68 newargs = append(newargs, args[1:]...)
69 c := exec.Command(filepath.Join(oldGOROOT, "bin", "go"), newargs...)
70 stderr := new(strings.Builder)
71 c.Stderr = stderr
72
73 if testing.Verbose() && t == nil {
74 fmt.Fprintf(os.Stderr, "+ go %s\n", strings.Join(args, " "))
75 c.Stderr = os.Stderr
76 }
77 output, err := c.Output()
78
79 if err != nil {
80 if t != nil {
81 t.Helper()
82 t.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr)
83 } else {
84
85 log.Panicf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr)
86 }
87 }
88 if testing.Verbose() && t != nil {
89 t.Logf("go %s", strings.Join(args, " "))
90 if stderr.Len() > 0 {
91 t.Logf("%s", stderr)
92 }
93 }
94 return string(bytes.TrimSpace(output))
95 }
96
97
98 func testMain(m *testing.M) (int, error) {
99 if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
100 globalSkip = func(t testing.TB) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
101 return m.Run(), nil
102 }
103 if !platform.BuildModeSupported(runtime.Compiler, "shared", runtime.GOOS, runtime.GOARCH) {
104 globalSkip = func(t testing.TB) { t.Skip("shared build mode not supported") }
105 return m.Run(), nil
106 }
107 if !testenv.HasCGO() {
108 globalSkip = testenv.MustHaveCGO
109 return m.Run(), nil
110 }
111
112 cwd, err := os.Getwd()
113 if err != nil {
114 log.Fatal(err)
115 }
116 oldGOROOT = filepath.Join(cwd, "../../../../..")
117
118 workDir, err := os.MkdirTemp("", "shared_test")
119 if err != nil {
120 return 0, err
121 }
122 if *testWork || testing.Verbose() {
123 fmt.Printf("+ mkdir -p %s\n", workDir)
124 }
125 if !*testWork {
126 defer os.RemoveAll(workDir)
127 }
128
129
130
131
132
133
134
135
136 os.Setenv("GO111MODULE", "off")
137
138
139
140 gopath := filepath.Join(workDir, "gopath")
141 modRoot, err := cloneTestdataModule(gopath)
142 if err != nil {
143 return 0, err
144 }
145 if testing.Verbose() {
146 fmt.Printf("+ export GOPATH=%s\n", gopath)
147 fmt.Printf("+ cd %s\n", modRoot)
148 }
149 os.Setenv("GOPATH", gopath)
150
151 os.Setenv("GOBIN", filepath.Join(gopath, "bin"))
152 os.Chdir(modRoot)
153 os.Setenv("PWD", modRoot)
154
155
156
157
158
159
160 goroot := filepath.Join(workDir, "goroot")
161 if err := cloneGOROOTDeps(goroot); err != nil {
162 return 0, err
163 }
164 if testing.Verbose() {
165 fmt.Fprintf(os.Stderr, "+ export GOROOT=%s\n", goroot)
166 }
167 os.Setenv("GOROOT", goroot)
168
169 myContext := build.Default
170 myContext.GOROOT = goroot
171 myContext.GOPATH = gopath
172
173
174
175
176 goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs...)...)
177
178 shlib := goCmd(nil, "list", "-linkshared", "-f={{.Shlib}}", "runtime")
179 if shlib != "" {
180 gorootInstallDir = filepath.Dir(shlib)
181 }
182
183 myContext.InstallSuffix = "_dynlink"
184 depP, err := myContext.Import("./depBase", ".", build.ImportComment)
185 if err != nil {
186 return 0, fmt.Errorf("import failed: %v", err)
187 }
188 if depP.PkgTargetRoot == "" {
189 gopathInstallDir = filepath.Dir(goCmd(nil, "list", "-buildmode=shared", "-f", "{{.Target}}", "./depBase"))
190 } else {
191 gopathInstallDir = filepath.Join(depP.PkgTargetRoot, "testshared")
192 }
193 return m.Run(), nil
194 }
195
196 func TestMain(m *testing.M) {
197 log.SetFlags(log.Lshortfile)
198 flag.Parse()
199
200 exitCode, err := testMain(m)
201 if err != nil {
202 log.Fatal(err)
203 }
204 os.Exit(exitCode)
205 }
206
207
208
209 func cloneTestdataModule(gopath string) (string, error) {
210 modRoot := filepath.Join(gopath, "src", "testshared")
211 if err := cgotest.OverlayDir(modRoot, "testdata"); err != nil {
212 return "", err
213 }
214 if err := os.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module testshared\n"), 0644); err != nil {
215 return "", err
216 }
217 return modRoot, nil
218 }
219
220
221
222
223 func cloneGOROOTDeps(goroot string) error {
224
225 listArgs := []string{
226 "list",
227 "-deps",
228 "-f", "{{if and .Standard (not .ForTest)}}{{.ImportPath}}{{end}}",
229 }
230 stdDeps := goCmd(nil, append(listArgs, minpkgs...)...)
231 testdataDeps := goCmd(nil, append(listArgs, "-test", "./...")...)
232
233 pkgs := append(strings.Split(strings.TrimSpace(stdDeps), "\n"),
234 strings.Split(strings.TrimSpace(testdataDeps), "\n")...)
235 sort.Strings(pkgs)
236 var pkgRoots []string
237 for _, pkg := range pkgs {
238 parentFound := false
239 for _, prev := range pkgRoots {
240 if pkg == prev || strings.HasPrefix(pkg, prev+"/") {
241
242 parentFound = true
243 break
244 }
245 }
246 if !parentFound {
247 pkgRoots = append(pkgRoots, pkg)
248 }
249 }
250
251 gorootDirs := []string{
252 "pkg/tool",
253 "pkg/include",
254 }
255 for _, pkg := range pkgRoots {
256 gorootDirs = append(gorootDirs, filepath.Join("src", pkg))
257 }
258
259 for _, dir := range gorootDirs {
260 if testing.Verbose() {
261 fmt.Fprintf(os.Stderr, "+ cp -r %s %s\n", filepath.Join(oldGOROOT, dir), filepath.Join(goroot, dir))
262 }
263 if err := cgotest.OverlayDir(filepath.Join(goroot, dir), filepath.Join(oldGOROOT, dir)); err != nil {
264 return err
265 }
266 }
267
268 return nil
269 }
270
271
272 func TestSOBuilt(t *testing.T) {
273 globalSkip(t)
274 _, err := os.Stat(filepath.Join(gorootInstallDir, soname))
275 if err != nil {
276 t.Error(err)
277 }
278 }
279
280 func hasDynTag(f *elf.File, tag elf.DynTag) bool {
281 ds := f.SectionByType(elf.SHT_DYNAMIC)
282 if ds == nil {
283 return false
284 }
285 d, err := ds.Data()
286 if err != nil {
287 return false
288 }
289 for len(d) > 0 {
290 var t elf.DynTag
291 switch f.Class {
292 case elf.ELFCLASS32:
293 t = elf.DynTag(f.ByteOrder.Uint32(d[0:4]))
294 d = d[8:]
295 case elf.ELFCLASS64:
296 t = elf.DynTag(f.ByteOrder.Uint64(d[0:8]))
297 d = d[16:]
298 }
299 if t == tag {
300 return true
301 }
302 }
303 return false
304 }
305
306
307 func TestNoTextrel(t *testing.T) {
308 globalSkip(t)
309 sopath := filepath.Join(gorootInstallDir, soname)
310 f, err := elf.Open(sopath)
311 if err != nil {
312 t.Fatal("elf.Open failed: ", err)
313 }
314 defer f.Close()
315 if hasDynTag(f, elf.DT_TEXTREL) {
316 t.Errorf("%s has DT_TEXTREL set", soname)
317 }
318 }
319
320
321
322 func TestNoDupSymbols(t *testing.T) {
323 globalSkip(t)
324 sopath := filepath.Join(gorootInstallDir, soname)
325 f, err := elf.Open(sopath)
326 if err != nil {
327 t.Fatal("elf.Open failed: ", err)
328 }
329 defer f.Close()
330 syms, err := f.Symbols()
331 if err != nil {
332 t.Errorf("error reading symbols %v", err)
333 return
334 }
335 for _, s := range syms {
336 if s.Name == ".dup" {
337 t.Fatalf("%s contains symbol called .dup", sopath)
338 }
339 }
340 }
341
342
343
344
345 func TestShlibnameFiles(t *testing.T) {
346 globalSkip(t)
347 pkgs := append([]string{}, minpkgs...)
348 pkgs = append(pkgs, "runtime/cgo")
349 if runtime.GOARCH == "arm" {
350 pkgs = append(pkgs, "math")
351 }
352 for _, pkg := range pkgs {
353 shlibnamefile := filepath.Join(gorootInstallDir, pkg+".shlibname")
354 contentsb, err := os.ReadFile(shlibnamefile)
355 if err != nil {
356 t.Errorf("error reading shlibnamefile for %s: %v", pkg, err)
357 continue
358 }
359 contents := strings.TrimSpace(string(contentsb))
360 if contents != soname {
361 t.Errorf("shlibnamefile for %s has wrong contents: %q", pkg, contents)
362 }
363 }
364 }
365
366
367 func isOffsetLoaded(f *elf.File, offset uint64) bool {
368 for _, prog := range f.Progs {
369 if prog.Type == elf.PT_LOAD {
370 if prog.Off <= offset && offset < prog.Off+prog.Filesz {
371 return true
372 }
373 }
374 }
375 return false
376 }
377
378 func rnd(v int32, r int32) int32 {
379 if r <= 0 {
380 return v
381 }
382 v += r - 1
383 c := v % r
384 if c < 0 {
385 c += r
386 }
387 v -= c
388 return v
389 }
390
391 func readwithpad(r io.Reader, sz int32) ([]byte, error) {
392 data := make([]byte, rnd(sz, 4))
393 _, err := io.ReadFull(r, data)
394 if err != nil {
395 return nil, err
396 }
397 data = data[:sz]
398 return data, nil
399 }
400
401 type note struct {
402 name string
403 tag int32
404 desc string
405 section *elf.Section
406 }
407
408
409
410
411 func readNotes(f *elf.File) ([]*note, error) {
412 var notes []*note
413 for _, sect := range f.Sections {
414 if sect.Type != elf.SHT_NOTE {
415 continue
416 }
417 r := sect.Open()
418 for {
419 var namesize, descsize, tag int32
420 err := binary.Read(r, f.ByteOrder, &namesize)
421 if err != nil {
422 if err == io.EOF {
423 break
424 }
425 return nil, fmt.Errorf("read namesize failed: %v", err)
426 }
427 err = binary.Read(r, f.ByteOrder, &descsize)
428 if err != nil {
429 return nil, fmt.Errorf("read descsize failed: %v", err)
430 }
431 err = binary.Read(r, f.ByteOrder, &tag)
432 if err != nil {
433 return nil, fmt.Errorf("read type failed: %v", err)
434 }
435 name, err := readwithpad(r, namesize)
436 if err != nil {
437 return nil, fmt.Errorf("read name failed: %v", err)
438 }
439 desc, err := readwithpad(r, descsize)
440 if err != nil {
441 return nil, fmt.Errorf("read desc failed: %v", err)
442 }
443 notes = append(notes, ¬e{name: string(name), tag: tag, desc: string(desc), section: sect})
444 }
445 }
446 return notes, nil
447 }
448
449 func dynStrings(t *testing.T, path string, flag elf.DynTag) []string {
450 t.Helper()
451 f, err := elf.Open(path)
452 if err != nil {
453 t.Fatalf("elf.Open(%q) failed: %v", path, err)
454 }
455 defer f.Close()
456 dynstrings, err := f.DynString(flag)
457 if err != nil {
458 t.Fatalf("DynString(%s) failed on %s: %v", flag, path, err)
459 }
460 return dynstrings
461 }
462
463 func AssertIsLinkedToRegexp(t *testing.T, path string, re *regexp.Regexp) {
464 t.Helper()
465 for _, dynstring := range dynStrings(t, path, elf.DT_NEEDED) {
466 if re.MatchString(dynstring) {
467 return
468 }
469 }
470 t.Errorf("%s is not linked to anything matching %v", path, re)
471 }
472
473 func AssertIsLinkedTo(t *testing.T, path, lib string) {
474 t.Helper()
475 AssertIsLinkedToRegexp(t, path, regexp.MustCompile(regexp.QuoteMeta(lib)))
476 }
477
478 func AssertHasRPath(t *testing.T, path, dir string) {
479 t.Helper()
480 for _, tag := range []elf.DynTag{elf.DT_RPATH, elf.DT_RUNPATH} {
481 for _, dynstring := range dynStrings(t, path, tag) {
482 for _, rpath := range strings.Split(dynstring, ":") {
483 if filepath.Clean(rpath) == filepath.Clean(dir) {
484 return
485 }
486 }
487 }
488 }
489 t.Errorf("%s does not have rpath %s", path, dir)
490 }
491
492
493 func TestTrivialExecutable(t *testing.T) {
494 globalSkip(t)
495 goCmd(t, "install", "-linkshared", "./trivial")
496 run(t, "trivial executable", "../../bin/trivial")
497 AssertIsLinkedTo(t, "../../bin/trivial", soname)
498 AssertHasRPath(t, "../../bin/trivial", gorootInstallDir)
499
500
501 checkSize(t, "../../bin/trivial", 256000)
502 }
503
504
505 func TestTrivialExecutablePIE(t *testing.T) {
506 globalSkip(t)
507 goCmd(t, "build", "-buildmode=pie", "-o", "trivial.pie", "-linkshared", "./trivial")
508 run(t, "trivial executable", "./trivial.pie")
509 AssertIsLinkedTo(t, "./trivial.pie", soname)
510 AssertHasRPath(t, "./trivial.pie", gorootInstallDir)
511
512
513 checkSize(t, "./trivial.pie", 256000)
514 }
515
516
517 func checkSize(t *testing.T, f string, limit int64) {
518 fi, err := os.Stat(f)
519 if err != nil {
520 t.Fatalf("stat failed: %v", err)
521 }
522 if sz := fi.Size(); sz > limit {
523 t.Errorf("file too large: got %d, want <= %d", sz, limit)
524 }
525 }
526
527
528 func TestDivisionExecutable(t *testing.T) {
529 globalSkip(t)
530 goCmd(t, "install", "-linkshared", "./division")
531 run(t, "division executable", "../../bin/division")
532 }
533
534
535
536 func TestCgoExecutable(t *testing.T) {
537 globalSkip(t)
538 goCmd(t, "install", "-linkshared", "./execgo")
539 run(t, "cgo executable", "../../bin/execgo")
540 }
541
542 func checkPIE(t *testing.T, name string) {
543 f, err := elf.Open(name)
544 if err != nil {
545 t.Fatal("elf.Open failed: ", err)
546 }
547 defer f.Close()
548 if f.Type != elf.ET_DYN {
549 t.Errorf("%s has type %v, want ET_DYN", name, f.Type)
550 }
551 if hasDynTag(f, elf.DT_TEXTREL) {
552 t.Errorf("%s has DT_TEXTREL set", name)
553 }
554 }
555
556 func TestTrivialPIE(t *testing.T) {
557 if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-alpine") {
558 t.Skip("skipping on alpine until issue #54354 resolved")
559 }
560 globalSkip(t)
561 testenv.MustHaveBuildMode(t, "pie")
562 name := "trivial_pie"
563 goCmd(t, "build", "-buildmode=pie", "-o="+name, "./trivial")
564 defer os.Remove(name)
565 run(t, name, "./"+name)
566 checkPIE(t, name)
567 }
568
569 func TestCgoPIE(t *testing.T) {
570 globalSkip(t)
571 testenv.MustHaveCGO(t)
572 testenv.MustHaveBuildMode(t, "pie")
573 name := "cgo_pie"
574 goCmd(t, "build", "-buildmode=pie", "-o="+name, "./execgo")
575 defer os.Remove(name)
576 run(t, name, "./"+name)
577 checkPIE(t, name)
578 }
579
580
581
582 func TestGopathShlib(t *testing.T) {
583 globalSkip(t)
584 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
585 shlib := goCmd(t, "list", "-f", "{{.Shlib}}", "-buildmode=shared", "-linkshared", "./depBase")
586 AssertIsLinkedTo(t, shlib, soname)
587 goCmd(t, "install", "-linkshared", "./exe")
588 AssertIsLinkedTo(t, "../../bin/exe", soname)
589 AssertIsLinkedTo(t, "../../bin/exe", filepath.Base(shlib))
590 AssertHasRPath(t, "../../bin/exe", gorootInstallDir)
591 AssertHasRPath(t, "../../bin/exe", filepath.Dir(gopathInstallDir))
592
593 run(t, "executable linked to GOPATH library", "../../bin/exe")
594 }
595
596
597
598 func testPkgListNote(t *testing.T, f *elf.File, note *note) {
599 if note.section.Flags != 0 {
600 t.Errorf("package list section has flags %v, want 0", note.section.Flags)
601 }
602 if isOffsetLoaded(f, note.section.Offset) {
603 t.Errorf("package list section contained in PT_LOAD segment")
604 }
605 if note.desc != "testshared/depBase\n" {
606 t.Errorf("incorrect package list %q, want %q", note.desc, "testshared/depBase\n")
607 }
608 }
609
610
611
612
613 func testABIHashNote(t *testing.T, f *elf.File, note *note) {
614 if note.section.Flags != elf.SHF_ALLOC {
615 t.Errorf("abi hash section has flags %v, want SHF_ALLOC", note.section.Flags)
616 }
617 if !isOffsetLoaded(f, note.section.Offset) {
618 t.Errorf("abihash section not contained in PT_LOAD segment")
619 }
620 var hashbytes elf.Symbol
621 symbols, err := f.Symbols()
622 if err != nil {
623 t.Errorf("error reading symbols %v", err)
624 return
625 }
626 for _, sym := range symbols {
627 if sym.Name == "go:link.abihashbytes" {
628 hashbytes = sym
629 }
630 }
631 if hashbytes.Name == "" {
632 t.Errorf("no symbol called go:link.abihashbytes")
633 return
634 }
635 if elf.ST_BIND(hashbytes.Info) != elf.STB_LOCAL {
636 t.Errorf("%s has incorrect binding %v, want STB_LOCAL", hashbytes.Name, elf.ST_BIND(hashbytes.Info))
637 }
638 if f.Sections[hashbytes.Section] != note.section {
639 t.Errorf("%s has incorrect section %v, want %s", hashbytes.Name, f.Sections[hashbytes.Section].Name, note.section.Name)
640 }
641 if hashbytes.Value-note.section.Addr != 16 {
642 t.Errorf("%s has incorrect offset into section %d, want 16", hashbytes.Name, hashbytes.Value-note.section.Addr)
643 }
644 }
645
646
647
648 func testDepsNote(t *testing.T, f *elf.File, note *note) {
649 if note.section.Flags != 0 {
650 t.Errorf("package list section has flags %v, want 0", note.section.Flags)
651 }
652 if isOffsetLoaded(f, note.section.Offset) {
653 t.Errorf("package list section contained in PT_LOAD segment")
654 }
655
656 if note.desc != soname {
657 t.Errorf("incorrect dependency list %q, want %q", note.desc, soname)
658 }
659 }
660
661
662 func TestNotes(t *testing.T) {
663 globalSkip(t)
664 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
665 shlib := goCmd(t, "list", "-f", "{{.Shlib}}", "-buildmode=shared", "-linkshared", "./depBase")
666 f, err := elf.Open(shlib)
667 if err != nil {
668 t.Fatal(err)
669 }
670 defer f.Close()
671 notes, err := readNotes(f)
672 if err != nil {
673 t.Fatal(err)
674 }
675 pkgListNoteFound := false
676 abiHashNoteFound := false
677 depsNoteFound := false
678 for _, note := range notes {
679 if note.name != "Go\x00\x00" {
680 continue
681 }
682 switch note.tag {
683 case 1:
684 if pkgListNoteFound {
685 t.Error("multiple package list notes")
686 }
687 testPkgListNote(t, f, note)
688 pkgListNoteFound = true
689 case 2:
690 if abiHashNoteFound {
691 t.Error("multiple abi hash notes")
692 }
693 testABIHashNote(t, f, note)
694 abiHashNoteFound = true
695 case 3:
696 if depsNoteFound {
697 t.Error("multiple dependency list notes")
698 }
699 testDepsNote(t, f, note)
700 depsNoteFound = true
701 }
702 }
703 if !pkgListNoteFound {
704 t.Error("package list note not found")
705 }
706 if !abiHashNoteFound {
707 t.Error("abi hash note not found")
708 }
709 if !depsNoteFound {
710 t.Error("deps note not found")
711 }
712 }
713
714
715
716
717 func TestTwoGopathShlibs(t *testing.T) {
718 globalSkip(t)
719 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
720 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep2")
721 goCmd(t, "install", "-linkshared", "./exe2")
722 run(t, "executable linked to GOPATH library", "../../bin/exe2")
723 }
724
725 func TestThreeGopathShlibs(t *testing.T) {
726 globalSkip(t)
727 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
728 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep2")
729 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep3")
730 goCmd(t, "install", "-linkshared", "./exe3")
731 run(t, "executable linked to GOPATH library", "../../bin/exe3")
732 }
733
734
735 func requireGccgo(t *testing.T) {
736 t.Helper()
737
738 if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
739 t.Skip("gccgo test skipped on PPC64 until issue #60798 is resolved")
740 }
741
742 gccgoName := os.Getenv("GCCGO")
743 if gccgoName == "" {
744 gccgoName = "gccgo"
745 }
746 gccgoPath, err := exec.LookPath(gccgoName)
747 if err != nil {
748 t.Skip("gccgo not found")
749 }
750 cmd := exec.Command(gccgoPath, "-dumpversion")
751 output, err := cmd.CombinedOutput()
752 if err != nil {
753 t.Fatalf("%s -dumpversion failed: %v\n%s", gccgoPath, err, output)
754 }
755 dot := bytes.Index(output, []byte{'.'})
756 if dot > 0 {
757 output = output[:dot]
758 }
759 major, err := strconv.Atoi(strings.TrimSpace(string(output)))
760 if err != nil {
761 t.Skipf("can't parse gccgo version number %s", output)
762 }
763 if major < 5 {
764 t.Skipf("gccgo too old (%s)", strings.TrimSpace(string(output)))
765 }
766
767 gomod, err := exec.Command("go", "env", "GOMOD").Output()
768 if err != nil {
769 t.Fatalf("go env GOMOD: %v", err)
770 }
771 if len(bytes.TrimSpace(gomod)) > 0 {
772 t.Skipf("gccgo not supported in module mode; see golang.org/issue/30344")
773 }
774 }
775
776
777
778 func TestGoPathShlibGccgo(t *testing.T) {
779 globalSkip(t)
780 requireGccgo(t)
781
782 libgoRE := regexp.MustCompile("libgo.so.[0-9]+")
783
784 goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./depBase")
785
786
787
788 shlib := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./depBase")
789
790 AssertIsLinkedToRegexp(t, shlib, libgoRE)
791 goCmd(t, "install", "-compiler=gccgo", "-linkshared", "./exe")
792 AssertIsLinkedToRegexp(t, "../../bin/exe", libgoRE)
793 AssertIsLinkedTo(t, "../../bin/exe", filepath.Base(shlib))
794 AssertHasRPath(t, "../../bin/exe", filepath.Dir(shlib))
795
796 run(t, "gccgo-built", "../../bin/exe")
797 }
798
799
800
801
802 func TestTwoGopathShlibsGccgo(t *testing.T) {
803 globalSkip(t)
804 requireGccgo(t)
805
806 libgoRE := regexp.MustCompile("libgo.so.[0-9]+")
807
808 goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./depBase")
809 goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./dep2")
810 goCmd(t, "install", "-compiler=gccgo", "-linkshared", "./exe2")
811
812
813
814 dep2 := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./dep2")
815 depBase := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./depBase")
816
817 AssertIsLinkedToRegexp(t, depBase, libgoRE)
818 AssertIsLinkedToRegexp(t, dep2, libgoRE)
819 AssertIsLinkedTo(t, dep2, filepath.Base(depBase))
820 AssertIsLinkedToRegexp(t, "../../bin/exe2", libgoRE)
821 AssertIsLinkedTo(t, "../../bin/exe2", filepath.Base(dep2))
822 AssertIsLinkedTo(t, "../../bin/exe2", filepath.Base(depBase))
823
824
825 run(t, "gccgo-built", "../../bin/exe2")
826 }
827
828
829
830
831
832
833
834
835 var oldTime = time.Now().Add(-9 * time.Second)
836 var nearlyNew = time.Now().Add(-6 * time.Second)
837 var stampTime = time.Now().Add(-3 * time.Second)
838
839
840
841 func resetFileStamps() {
842 chtime := func(path string, info os.FileInfo, err error) error {
843 return os.Chtimes(path, oldTime, oldTime)
844 }
845 reset := func(path string) {
846 if err := filepath.Walk(path, chtime); err != nil {
847 log.Panicf("resetFileStamps failed: %v", err)
848 }
849
850 }
851 reset("../../bin")
852 reset("../../pkg")
853 reset("../../src")
854 reset(gorootInstallDir)
855 }
856
857
858
859 func touch(t *testing.T, path string) (cleanup func()) {
860 t.Helper()
861 data, err := os.ReadFile(path)
862 if err != nil {
863 t.Fatal(err)
864 }
865 old := make([]byte, len(data))
866 copy(old, data)
867 if bytes.HasPrefix(data, []byte("!<arch>\n")) {
868
869
870 const marker = `build id "`
871 i := bytes.Index(data, []byte(marker))
872 if i < 0 {
873 t.Fatal("cannot find build id in archive")
874 }
875 j := bytes.IndexByte(data[i+len(marker):], '"')
876 if j < 0 {
877 t.Fatal("cannot find build id in archive")
878 }
879 i += len(marker) + j - 1
880 if data[i] == 'a' {
881 data[i] = 'b'
882 } else {
883 data[i] = 'a'
884 }
885 } else {
886
887 data = append(data, '\n')
888 }
889
890
891
892 fi, err := os.Lstat(path)
893 if err == nil && !fi.Mode().IsRegular() {
894 fi, err = os.Stat(path)
895 if err := os.Remove(path); err != nil {
896 t.Fatal(err)
897 }
898 }
899 if err != nil {
900 t.Fatal(err)
901 }
902
903
904
905 perm := fi.Mode().Perm() | 0200
906
907 if err := os.WriteFile(path, data, perm); err != nil {
908 t.Fatal(err)
909 }
910 if err := os.Chtimes(path, nearlyNew, nearlyNew); err != nil {
911 t.Fatal(err)
912 }
913 return func() {
914 if err := os.WriteFile(path, old, perm); err != nil {
915 t.Fatal(err)
916 }
917 }
918 }
919
920
921 func isNew(t *testing.T, path string) bool {
922 t.Helper()
923 fi, err := os.Stat(path)
924 if err != nil {
925 t.Fatal(err)
926 }
927 return fi.ModTime().After(stampTime)
928 }
929
930
931
932 func AssertRebuilt(t *testing.T, msg, path string) {
933 t.Helper()
934 if !isNew(t, path) {
935 t.Errorf("%s was not rebuilt (%s)", msg, path)
936 }
937 }
938
939
940 func AssertNotRebuilt(t *testing.T, msg, path string) {
941 t.Helper()
942 if isNew(t, path) {
943 t.Errorf("%s was rebuilt (%s)", msg, path)
944 }
945 }
946
947 func TestRebuilding(t *testing.T) {
948 globalSkip(t)
949 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
950 goCmd(t, "install", "-linkshared", "./exe")
951 info := strings.Fields(goCmd(t, "list", "-buildmode=shared", "-linkshared", "-f", "{{.Target}} {{.Shlib}}", "./depBase"))
952 if len(info) != 2 {
953 t.Fatalf("go list failed to report Target and/or Shlib")
954 }
955 target := info[0]
956 shlib := info[1]
957
958
959 t.Run("newsource", func(t *testing.T) {
960 resetFileStamps()
961 cleanup := touch(t, "./depBase/dep.go")
962 defer func() {
963 cleanup()
964 goCmd(t, "install", "-linkshared", "./exe")
965 }()
966 goCmd(t, "install", "-linkshared", "./exe")
967 AssertRebuilt(t, "new source", target)
968 AssertRebuilt(t, "new source", shlib)
969 })
970
971
972 t.Run("newarchive", func(t *testing.T) {
973 resetFileStamps()
974 AssertNotRebuilt(t, "new .a file before build", target)
975 goCmd(t, "list", "-linkshared", "-f={{.ImportPath}} {{.Stale}} {{.StaleReason}} {{.Target}}", "./depBase")
976 AssertNotRebuilt(t, "new .a file before build", target)
977 cleanup := touch(t, target)
978 defer func() {
979 cleanup()
980 goCmd(t, "install", "-v", "-linkshared", "./exe")
981 }()
982 goCmd(t, "install", "-v", "-linkshared", "./exe")
983 AssertNotRebuilt(t, "new .a file", target)
984 AssertRebuilt(t, "new .a file", shlib)
985 })
986 }
987
988 func appendFile(t *testing.T, path, content string) {
989 t.Helper()
990 f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0660)
991 if err != nil {
992 t.Fatalf("os.OpenFile failed: %v", err)
993 }
994 defer func() {
995 err := f.Close()
996 if err != nil {
997 t.Fatalf("f.Close failed: %v", err)
998 }
999 }()
1000 _, err = f.WriteString(content)
1001 if err != nil {
1002 t.Fatalf("f.WriteString failed: %v", err)
1003 }
1004 }
1005
1006 func createFile(t *testing.T, path, content string) {
1007 t.Helper()
1008 f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
1009 if err != nil {
1010 t.Fatalf("os.OpenFile failed: %v", err)
1011 }
1012 _, err = f.WriteString(content)
1013 if closeErr := f.Close(); err == nil {
1014 err = closeErr
1015 }
1016 if err != nil {
1017 t.Fatalf("WriteString failed: %v", err)
1018 }
1019 }
1020
1021 func TestABIChecking(t *testing.T) {
1022 globalSkip(t)
1023 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
1024 goCmd(t, "install", "-linkshared", "./exe")
1025
1026
1027
1028
1029
1030
1031 resetFileStamps()
1032
1033 createFile(t, "./depBase/break.go", "package depBase\nfunc ABIBreak() {}\n")
1034 defer os.Remove("./depBase/break.go")
1035
1036 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
1037 c := exec.Command("../../bin/exe")
1038 output, err := c.CombinedOutput()
1039 if err == nil {
1040 t.Fatal("executing exe did not fail after ABI break")
1041 }
1042 scanner := bufio.NewScanner(bytes.NewReader(output))
1043 foundMsg := false
1044 const wantPrefix = "abi mismatch detected between the executable and lib"
1045 for scanner.Scan() {
1046 if strings.HasPrefix(scanner.Text(), wantPrefix) {
1047 foundMsg = true
1048 break
1049 }
1050 }
1051 if err = scanner.Err(); err != nil {
1052 t.Errorf("scanner encountered error: %v", err)
1053 }
1054 if !foundMsg {
1055 t.Fatalf("exe failed, but without line %q; got output:\n%s", wantPrefix, output)
1056 }
1057
1058
1059 goCmd(t, "install", "-linkshared", "./exe")
1060 run(t, "rebuilt exe", "../../bin/exe")
1061
1062
1063
1064
1065 resetFileStamps()
1066 createFile(t, "./depBase/dep2.go", "package depBase\nfunc noABIBreak() {}\n")
1067 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
1068 run(t, "after non-ABI breaking change", "../../bin/exe")
1069 }
1070
1071
1072
1073
1074
1075
1076
1077 func TestImplicitInclusion(t *testing.T) {
1078 globalSkip(t)
1079 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./explicit")
1080 goCmd(t, "install", "-linkshared", "./implicitcmd")
1081 run(t, "running executable linked against library that contains same package as it", "../../bin/implicitcmd")
1082 }
1083
1084
1085
1086
1087 func TestInterface(t *testing.T) {
1088 globalSkip(t)
1089 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./iface_a")
1090
1091 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./iface_b")
1092 goCmd(t, "install", "-linkshared", "./iface")
1093 run(t, "running type/itab uniqueness tester", "../../bin/iface")
1094 }
1095
1096
1097 func TestGlobal(t *testing.T) {
1098 globalSkip(t)
1099 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./globallib")
1100 goCmd(t, "install", "-linkshared", "./global")
1101 run(t, "global executable", "../../bin/global")
1102 AssertIsLinkedTo(t, "../../bin/global", soname)
1103 AssertHasRPath(t, "../../bin/global", gorootInstallDir)
1104 }
1105
1106
1107
1108 func TestTestInstalledShared(t *testing.T) {
1109 globalSkip(t)
1110 goCmd(t, "test", "-linkshared", "-test.short", "sync/atomic")
1111 }
1112
1113
1114
1115 func TestGeneratedMethod(t *testing.T) {
1116 globalSkip(t)
1117 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue25065")
1118 }
1119
1120
1121
1122 func TestGeneratedHash(t *testing.T) {
1123 globalSkip(t)
1124 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib")
1125 goCmd(t, "test", "-linkshared", "./issue30768")
1126 }
1127
1128
1129
1130 func TestPackageOrder(t *testing.T) {
1131 globalSkip(t)
1132 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue39777/a", "./issue39777/b")
1133 }
1134
1135
1136
1137 func TestGCData(t *testing.T) {
1138 globalSkip(t)
1139 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./gcdata/p")
1140 goCmd(t, "build", "-linkshared", "./gcdata/main")
1141 runWithEnv(t, "running gcdata/main", []string{"GODEBUG=clobberfree=1"}, "./main")
1142 }
1143
1144
1145
1146 func TestIssue44031(t *testing.T) {
1147 globalSkip(t)
1148 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/a")
1149 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/b")
1150 goCmd(t, "run", "-linkshared", "./issue44031/main")
1151 }
1152
1153
1154
1155
1156 func TestIssue47873(t *testing.T) {
1157 globalSkip(t)
1158 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue47837/a")
1159 goCmd(t, "run", "-linkshared", "./issue47837/main")
1160 }
1161
1162 func TestIssue62277(t *testing.T) {
1163 globalSkip(t)
1164 goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue62277/p")
1165 goCmd(t, "test", "-linkshared", "./issue62277")
1166 }
1167
1168
1169 func TestStd(t *testing.T) {
1170 if testing.Short() {
1171 t.Skip("skip in short mode")
1172 }
1173 globalSkip(t)
1174 t.Parallel()
1175 tmpDir := t.TempDir()
1176
1177
1178 runWithEnv(t, "building std", []string{"GOROOT=" + oldGOROOT},
1179 filepath.Join(oldGOROOT, "bin", "go"), "install", "-buildmode=shared", "-pkgdir="+tmpDir, "std")
1180
1181
1182 runWithEnv(t, "testing issue #58966", []string{"GOROOT=" + oldGOROOT},
1183 filepath.Join(oldGOROOT, "bin", "go"), "run", "-linkshared", "-pkgdir="+tmpDir, "./issue58966/main.go")
1184 }
1185
View as plain text